2020-12-05 13:08:24 +01:00
|
|
|
#pragma once
|
2018-12-20 23:35:49 +01:00
|
|
|
|
2020-12-12 13:01:29 +01:00
|
|
|
#include "util/types.hpp"
|
2024-03-26 19:39:37 +01:00
|
|
|
#include "util/logs.hpp"
|
2025-04-08 18:46:57 +02:00
|
|
|
#include "util/mutex.h"
|
|
|
|
|
#include "util/StrFmt.h"
|
2024-03-26 19:39:37 +01:00
|
|
|
#include "Emu/system_config_types.h"
|
2022-05-05 15:47:44 +02:00
|
|
|
#include <numbers>
|
2018-12-20 23:35:49 +01:00
|
|
|
|
|
|
|
|
enum : u32
|
|
|
|
|
{
|
|
|
|
|
DEFAULT_AUDIO_SAMPLING_RATE = 48000,
|
|
|
|
|
MAX_AUDIO_BUFFERS = 64,
|
2021-11-24 19:41:05 +01:00
|
|
|
AUDIO_BUFFER_SAMPLES = 256,
|
|
|
|
|
AUDIO_MAX_CHANNELS = 8,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum class AudioFreq : u32
|
|
|
|
|
{
|
2025-04-05 21:50:45 +02:00
|
|
|
FREQ_32K = 32000,
|
|
|
|
|
FREQ_44K = 44100,
|
|
|
|
|
FREQ_48K = 48000,
|
|
|
|
|
FREQ_88K = 88200,
|
|
|
|
|
FREQ_96K = 96000,
|
2021-11-24 19:41:05 +01:00
|
|
|
FREQ_176K = 176400,
|
|
|
|
|
FREQ_192K = 192000,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum class AudioSampleSize : u32
|
|
|
|
|
{
|
|
|
|
|
FLOAT = sizeof(float),
|
|
|
|
|
S16 = sizeof(s16),
|
|
|
|
|
};
|
|
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
// This enum is only used for emulation
|
2021-11-24 19:41:05 +01:00
|
|
|
enum class AudioChannelCnt : u32
|
|
|
|
|
{
|
2025-04-05 21:50:45 +02:00
|
|
|
STEREO = 2,
|
2021-11-24 19:41:05 +01:00
|
|
|
SURROUND_5_1 = 6,
|
|
|
|
|
SURROUND_7_1 = 8,
|
2018-12-20 23:35:49 +01:00
|
|
|
};
|
2015-01-11 00:46:10 +01:00
|
|
|
|
2022-07-08 17:13:38 +02:00
|
|
|
enum class AudioStateEvent : u32
|
|
|
|
|
{
|
|
|
|
|
UNSPECIFIED_ERROR,
|
2022-10-13 14:39:16 +02:00
|
|
|
DEFAULT_DEVICE_MAYBE_CHANGED,
|
2022-07-08 17:13:38 +02:00
|
|
|
};
|
|
|
|
|
|
2018-12-16 18:40:50 +01:00
|
|
|
class AudioBackend
|
2015-01-11 00:46:10 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2022-05-05 15:47:44 +02:00
|
|
|
struct VolumeParam
|
|
|
|
|
{
|
|
|
|
|
f32 initial_volume = 1.0f;
|
|
|
|
|
f32 current_volume = 1.0f;
|
|
|
|
|
f32 target_volume = 1.0f;
|
|
|
|
|
u32 freq = 48000;
|
|
|
|
|
u32 ch_cnt = 2;
|
|
|
|
|
};
|
|
|
|
|
|
2020-06-20 02:44:32 +02:00
|
|
|
AudioBackend();
|
|
|
|
|
|
2018-12-16 18:40:50 +01:00
|
|
|
virtual ~AudioBackend() = default;
|
2015-01-11 00:46:10 +01:00
|
|
|
|
2018-12-21 03:13:22 +01:00
|
|
|
/*
|
2022-05-05 15:47:44 +02:00
|
|
|
* Virtual methods
|
2018-12-21 03:13:22 +01:00
|
|
|
*/
|
2021-11-29 12:52:02 +01:00
|
|
|
virtual std::string_view GetName() const = 0;
|
2018-12-16 18:40:50 +01:00
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
// (Re)create output stream with new parameters. Blocks until data callback returns.
|
2022-07-08 17:13:38 +02:00
|
|
|
// If dev_id is empty, then default device will be selected.
|
|
|
|
|
// May override channel count if device has smaller number of channels.
|
2022-05-05 15:47:44 +02:00
|
|
|
// Should return 'true' on success.
|
2024-03-26 19:39:37 +01:00
|
|
|
virtual bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) = 0;
|
2022-05-05 15:47:44 +02:00
|
|
|
|
|
|
|
|
// Reset backend state. Blocks until data callback returns.
|
2015-01-11 00:46:10 +01:00
|
|
|
virtual void Close() = 0;
|
2018-12-20 23:35:49 +01:00
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
// Sets write callback. It's called when backend requests new data to be sent.
|
|
|
|
|
// Callback should return number of submitted bytes. Calling other backend functions from callback is unsafe.
|
2022-07-08 17:13:38 +02:00
|
|
|
virtual void SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void* /* buffer */)> cb);
|
2022-05-05 15:47:44 +02:00
|
|
|
|
2022-07-08 17:13:38 +02:00
|
|
|
// Sets error callback. It's called when backend detects event in audio chain that needs immediate attention.
|
2022-05-05 15:47:44 +02:00
|
|
|
// Calling other backend functions from callback is unsafe.
|
2022-07-08 17:13:38 +02:00
|
|
|
virtual void SetStateCallback(std::function<void(AudioStateEvent)> cb);
|
2022-05-05 15:47:44 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* All functions below require that Open() was called prior.
|
|
|
|
|
*/
|
2018-12-16 18:40:50 +01:00
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
// Returns length of one write callback frame in seconds. Open() must be called prior.
|
2021-11-24 19:41:05 +01:00
|
|
|
virtual f64 GetCallbackFrameLen() = 0;
|
|
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
// Returns true if audio is currently being played, false otherwise. Reflects end result of Play() and Pause() calls.
|
2025-04-05 21:50:45 +02:00
|
|
|
virtual bool IsPlaying()
|
|
|
|
|
{
|
|
|
|
|
return m_playing;
|
|
|
|
|
}
|
2021-11-24 19:41:05 +01:00
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
// Start playing enqueued data.
|
2021-11-24 19:41:05 +01:00
|
|
|
virtual void Play() = 0;
|
|
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
// Pause playing enqueued data. No additional callbacks will be issued. Blocks until data callback returns.
|
2021-11-24 19:41:05 +01:00
|
|
|
virtual void Pause() = 0;
|
2018-12-21 03:13:22 +01:00
|
|
|
|
2020-02-18 20:42:55 +01:00
|
|
|
/*
|
|
|
|
|
* This virtual method should be reimplemented if backend can fail to be initialized under non-error conditions
|
|
|
|
|
* eg. when there is no audio devices attached
|
|
|
|
|
*/
|
2025-04-05 21:50:45 +02:00
|
|
|
virtual bool Initialized()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-02-18 20:42:55 +01:00
|
|
|
|
2018-12-21 03:13:22 +01:00
|
|
|
/*
|
2021-11-24 19:41:05 +01:00
|
|
|
* This virtual method should be reimplemented if backend can fail during normal operation
|
2018-12-21 03:13:22 +01:00
|
|
|
*/
|
2025-04-05 21:50:45 +02:00
|
|
|
virtual bool Operational()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-12-21 03:13:22 +01:00
|
|
|
|
2022-07-08 17:13:38 +02:00
|
|
|
/*
|
|
|
|
|
* This virtual method should be reimplemented if backend can report device changes
|
|
|
|
|
*/
|
2025-04-05 21:50:45 +02:00
|
|
|
virtual bool DefaultDeviceChanged()
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-07-08 17:13:38 +02:00
|
|
|
|
2018-12-21 03:13:22 +01:00
|
|
|
/*
|
|
|
|
|
* Helper methods
|
|
|
|
|
*/
|
2020-06-20 02:44:32 +02:00
|
|
|
u32 get_sampling_rate() const;
|
2018-12-20 23:35:49 +01:00
|
|
|
|
2020-06-20 02:44:32 +02:00
|
|
|
u32 get_sample_size() const;
|
2018-12-20 23:35:49 +01:00
|
|
|
|
2020-06-20 02:44:32 +02:00
|
|
|
u32 get_channels() const;
|
2024-03-26 19:39:37 +01:00
|
|
|
audio_channel_layout get_channel_layout() const;
|
2020-12-12 13:01:29 +01:00
|
|
|
|
2021-11-24 19:41:05 +01:00
|
|
|
bool get_convert_to_s16() const;
|
2018-12-16 22:12:58 +01:00
|
|
|
|
2022-01-05 09:26:12 +01:00
|
|
|
/*
|
|
|
|
|
* Convert float buffer to s16 one. src and dst could be the same. cnt is number of buffer elements.
|
|
|
|
|
*/
|
|
|
|
|
static void convert_to_s16(u32 cnt, const f32* src, void* dst);
|
2020-06-20 02:44:32 +02:00
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
/*
|
|
|
|
|
* Apply volume parameters to the buffer. Gradually changes volume. src and dst could be the same.
|
|
|
|
|
* Number of channels must be >1 and multiple of 2.
|
|
|
|
|
* sample_cnt is number of buffer elements. Returns current volume.
|
|
|
|
|
*/
|
|
|
|
|
static f32 apply_volume(const VolumeParam& param, u32 sample_cnt, const f32* src, f32* dst);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Apply volume value to the buffer. src and dst could be the same. sample_cnt is number of buffer elements.
|
|
|
|
|
* Returns current volume.
|
|
|
|
|
*/
|
|
|
|
|
static void apply_volume_static(f32 vol, u32 sample_cnt, const f32* src, f32* dst);
|
|
|
|
|
|
2022-05-28 12:43:15 +02:00
|
|
|
/*
|
|
|
|
|
* Normalize float samples in range from -1.0 to 1.0.
|
|
|
|
|
*/
|
|
|
|
|
static void normalize(u32 sample_cnt, const f32* src, f32* dst);
|
|
|
|
|
|
|
|
|
|
/*
|
2022-06-19 17:08:03 +02:00
|
|
|
* Returns the output channel count and downmix mode.
|
2022-05-28 12:43:15 +02:00
|
|
|
*/
|
2022-06-06 18:18:13 +02:00
|
|
|
static std::pair<AudioChannelCnt, AudioChannelCnt> get_channel_count_and_downmixer(u32 device_index);
|
2022-05-28 12:43:15 +02:00
|
|
|
|
2022-06-07 17:53:13 +02:00
|
|
|
/*
|
|
|
|
|
* Returns the max supported channel count.
|
|
|
|
|
*/
|
|
|
|
|
static AudioChannelCnt get_max_channel_count(u32 device_index);
|
|
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
/*
|
|
|
|
|
* Get default channel count for a layout
|
|
|
|
|
*/
|
|
|
|
|
static u32 default_layout_channel_count(audio_channel_layout layout);
|
|
|
|
|
|
2022-07-08 17:13:38 +02:00
|
|
|
/*
|
|
|
|
|
* Converts raw channel count to value usable by backends
|
|
|
|
|
*/
|
2024-03-26 19:39:37 +01:00
|
|
|
static u32 layout_channel_count(u32 channels, audio_channel_layout layout);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get the default layout for raw channel count
|
|
|
|
|
*/
|
|
|
|
|
static audio_channel_layout default_layout(u32 channels);
|
2022-07-08 17:13:38 +02:00
|
|
|
|
2022-05-05 15:47:44 +02:00
|
|
|
/*
|
|
|
|
|
* Downmix audio stream.
|
|
|
|
|
*/
|
2025-04-05 21:50:45 +02:00
|
|
|
template <AudioChannelCnt src_ch_cnt, audio_channel_layout dst_layout>
|
2022-05-05 15:47:44 +02:00
|
|
|
static void downmix(u32 sample_cnt, const f32* src, f32* dst)
|
|
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
const u32 dst_ch_cnt = default_layout_channel_count(dst_layout);
|
2025-04-05 21:50:45 +02:00
|
|
|
if (static_cast<u32>(src_ch_cnt) <= dst_ch_cnt)
|
|
|
|
|
fmt::throw_exception("src channel count must be bigger than dst channel count");
|
2022-05-05 15:47:44 +02:00
|
|
|
|
|
|
|
|
static constexpr f32 center_coef = std::numbers::sqrt2_v<f32> / 2;
|
|
|
|
|
static constexpr f32 surround_coef = std::numbers::sqrt2_v<f32> / 2;
|
|
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
for (u32 src_sample = 0, dst_sample = 0; src_sample < sample_cnt; src_sample += static_cast<u32>(src_ch_cnt), dst_sample += dst_ch_cnt)
|
2022-05-05 15:47:44 +02:00
|
|
|
{
|
2025-04-05 21:50:45 +02:00
|
|
|
const f32 left = src[src_sample + 0];
|
2024-03-26 19:39:37 +01:00
|
|
|
const f32 right = src[src_sample + 1];
|
2025-04-05 21:50:45 +02:00
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
if constexpr (src_ch_cnt == AudioChannelCnt::STEREO)
|
2022-05-05 15:47:44 +02:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
if constexpr (dst_layout == audio_channel_layout::mono)
|
|
|
|
|
{
|
|
|
|
|
dst[dst_sample + 0] = left + right;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_5_1)
|
|
|
|
|
{
|
2025-04-05 21:50:45 +02:00
|
|
|
const f32 center = src[src_sample + 2];
|
|
|
|
|
const f32 low_freq = src[src_sample + 3];
|
|
|
|
|
const f32 side_left = src[src_sample + 4];
|
2022-05-05 15:47:44 +02:00
|
|
|
const f32 side_right = src[src_sample + 5];
|
|
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe)
|
|
|
|
|
{
|
|
|
|
|
const f32 mid = center * center_coef;
|
|
|
|
|
dst[dst_sample + 0] = left + mid;
|
|
|
|
|
dst[dst_sample + 1] = right + mid;
|
|
|
|
|
dst[dst_sample + 2] = side_left;
|
|
|
|
|
dst[dst_sample + 3] = side_right;
|
|
|
|
|
|
|
|
|
|
if constexpr (dst_layout == audio_channel_layout::quadraphonic_lfe)
|
|
|
|
|
{
|
|
|
|
|
dst[dst_sample + 4] = low_freq;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (dst_layout == audio_channel_layout::stereo || dst_layout == audio_channel_layout::stereo_lfe)
|
|
|
|
|
{
|
|
|
|
|
const f32 mid = center * center_coef;
|
|
|
|
|
dst[dst_sample + 0] = left + mid + side_left * surround_coef;
|
|
|
|
|
dst[dst_sample + 1] = right + mid + side_right * surround_coef;
|
|
|
|
|
|
|
|
|
|
if constexpr (dst_layout == audio_channel_layout::stereo_lfe)
|
|
|
|
|
{
|
|
|
|
|
dst[dst_sample + 2] = low_freq;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (dst_layout == audio_channel_layout::mono)
|
|
|
|
|
{
|
|
|
|
|
dst[dst_sample + 0] = left + right + center + side_left + side_right;
|
|
|
|
|
}
|
2022-05-05 15:47:44 +02:00
|
|
|
}
|
2024-03-26 19:39:37 +01:00
|
|
|
else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_7_1)
|
2022-05-05 15:47:44 +02:00
|
|
|
{
|
2025-04-05 21:50:45 +02:00
|
|
|
const f32 center = src[src_sample + 2];
|
|
|
|
|
const f32 low_freq = src[src_sample + 3];
|
|
|
|
|
const f32 rear_left = src[src_sample + 4];
|
2022-05-05 15:47:44 +02:00
|
|
|
const f32 rear_right = src[src_sample + 5];
|
2025-04-05 21:50:45 +02:00
|
|
|
const f32 side_left = src[src_sample + 6];
|
2022-05-05 15:47:44 +02:00
|
|
|
const f32 side_right = src[src_sample + 7];
|
|
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
if constexpr (dst_layout == audio_channel_layout::surround_5_1)
|
2022-05-05 15:47:44 +02:00
|
|
|
{
|
|
|
|
|
dst[dst_sample + 0] = left;
|
|
|
|
|
dst[dst_sample + 1] = right;
|
|
|
|
|
dst[dst_sample + 2] = center;
|
|
|
|
|
dst[dst_sample + 3] = low_freq;
|
|
|
|
|
dst[dst_sample + 4] = side_left + rear_left;
|
|
|
|
|
dst[dst_sample + 5] = side_right + rear_right;
|
|
|
|
|
}
|
2024-03-26 19:39:37 +01:00
|
|
|
else if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe)
|
|
|
|
|
{
|
|
|
|
|
const f32 mid = center * center_coef;
|
|
|
|
|
dst[dst_sample + 0] = left + mid;
|
|
|
|
|
dst[dst_sample + 1] = right + mid;
|
|
|
|
|
dst[dst_sample + 2] = side_left + rear_left;
|
|
|
|
|
dst[dst_sample + 3] = side_right + rear_right;
|
|
|
|
|
|
|
|
|
|
if constexpr (dst_layout == audio_channel_layout::quadraphonic_lfe)
|
|
|
|
|
{
|
|
|
|
|
dst[dst_sample + 4] = low_freq;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (dst_layout == audio_channel_layout::stereo || dst_layout == audio_channel_layout::stereo_lfe)
|
2022-05-05 15:47:44 +02:00
|
|
|
{
|
|
|
|
|
const f32 mid = center * center_coef;
|
|
|
|
|
dst[dst_sample + 0] = left + mid + (side_left + rear_left) * surround_coef;
|
|
|
|
|
dst[dst_sample + 1] = right + mid + (side_right + rear_right) * surround_coef;
|
2024-03-26 19:39:37 +01:00
|
|
|
|
|
|
|
|
if constexpr (dst_layout == audio_channel_layout::stereo_lfe)
|
|
|
|
|
{
|
|
|
|
|
dst[dst_sample + 2] = low_freq;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if constexpr (dst_layout == audio_channel_layout::mono)
|
|
|
|
|
{
|
|
|
|
|
dst[dst_sample + 0] = left + right + center + side_left + rear_left + side_right + rear_right;
|
2022-05-05 15:47:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
static void downmix(u32 sample_cnt, u32 src_ch_cnt, audio_channel_layout dst_layout, const f32* src, f32* dst)
|
2023-11-15 22:10:19 +01:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
const u32 dst_ch_cnt = default_layout_channel_count(dst_layout);
|
|
|
|
|
|
2023-11-15 22:10:19 +01:00
|
|
|
if (src_ch_cnt <= dst_ch_cnt)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-26 19:39:37 +01:00
|
|
|
switch (src_ch_cnt)
|
2023-11-15 22:10:19 +01:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
case static_cast<u32>(AudioChannelCnt::SURROUND_7_1):
|
|
|
|
|
{
|
|
|
|
|
switch (dst_layout)
|
2023-11-15 22:10:19 +01:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
case audio_channel_layout::mono:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::stereo:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::stereo_lfe:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo_lfe>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::quadraphonic:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::quadraphonic>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::quadraphonic_lfe:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::quadraphonic_lfe>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::surround_5_1:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::surround_5_1>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
2023-11-15 22:10:19 +01:00
|
|
|
}
|
2024-03-26 19:39:37 +01:00
|
|
|
break;
|
2023-11-15 22:10:19 +01:00
|
|
|
}
|
2024-03-26 19:39:37 +01:00
|
|
|
case static_cast<u32>(AudioChannelCnt::SURROUND_5_1):
|
2023-11-15 22:10:19 +01:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
switch (dst_layout)
|
2023-11-15 22:10:19 +01:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
case audio_channel_layout::mono:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::stereo:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::stereo>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::stereo_lfe:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::stereo_lfe>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::quadraphonic:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::quadraphonic>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
case audio_channel_layout::quadraphonic_lfe:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::quadraphonic_lfe>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
2023-11-15 22:10:19 +01:00
|
|
|
}
|
2024-03-26 19:39:37 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case static_cast<u32>(AudioChannelCnt::STEREO):
|
|
|
|
|
{
|
|
|
|
|
switch (dst_layout)
|
2023-11-15 22:10:19 +01:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
case audio_channel_layout::mono:
|
|
|
|
|
AudioBackend::downmix<AudioChannelCnt::STEREO, audio_channel_layout::mono>(sample_cnt, src, dst);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
2023-11-15 22:10:19 +01:00
|
|
|
}
|
2024-03-26 19:39:37 +01:00
|
|
|
break;
|
2023-11-15 22:10:19 +01:00
|
|
|
}
|
2024-03-26 19:39:37 +01:00
|
|
|
default:
|
2023-11-15 22:10:19 +01:00
|
|
|
{
|
2024-03-26 19:39:37 +01:00
|
|
|
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
|
|
|
|
}
|
2023-11-15 22:10:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-20 02:44:32 +02:00
|
|
|
protected:
|
2024-03-26 19:39:37 +01:00
|
|
|
void setup_channel_layout(u32 input_channel_count, u32 output_channel_count, audio_channel_layout layout, logs::channel& log);
|
|
|
|
|
|
2021-11-24 19:41:05 +01:00
|
|
|
AudioSampleSize m_sample_size = AudioSampleSize::FLOAT;
|
2025-04-05 21:50:45 +02:00
|
|
|
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
|
|
|
|
|
u32 m_channels = 2;
|
2024-03-26 19:39:37 +01:00
|
|
|
audio_channel_layout m_layout = audio_channel_layout::automatic;
|
2022-05-05 15:47:44 +02:00
|
|
|
|
2022-10-13 14:39:16 +02:00
|
|
|
std::timed_mutex m_cb_mutex{};
|
2025-04-05 21:50:45 +02:00
|
|
|
std::function<u32(u32, void*)> m_write_callback{};
|
2022-07-08 17:13:38 +02:00
|
|
|
|
2022-10-13 14:39:16 +02:00
|
|
|
shared_mutex m_state_cb_mutex{};
|
2022-07-08 17:13:38 +02:00
|
|
|
std::function<void(AudioStateEvent)> m_state_callback{};
|
2022-05-05 15:47:44 +02:00
|
|
|
|
|
|
|
|
bool m_playing = false;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
static constexpr f32 VOLUME_CHANGE_DURATION = 0.016f; // sec
|
2015-06-19 17:49:38 +02:00
|
|
|
};
|