Downmix surround 5.1 to stereo

This commit is contained in:
loki
2021-05-14 21:44:20 +02:00
parent 3901e404a9
commit 33a330fd6c
5 changed files with 305 additions and 129 deletions

View File

@@ -49,10 +49,154 @@ public:
}
};
struct format_t {
std::string_view name;
int channels;
int channel_mask;
} formats [] {
{
"Stereo"sv,
2,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
},
{
"Mono"sv,
1,
SPEAKER_FRONT_CENTER
},
{
"Surround 5.1"sv,
6,
SPEAKER_FRONT_LEFT |
SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER |
SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT |
SPEAKER_BACK_RIGHT
}
};
void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
wave_format->nChannels = format.channels;
wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8;
wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign;
if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask;
}
}
void surround51_to_stereo(std::vector<std::int16_t> &sample_in, const util::buffer_t<std::int16_t> &sample_out) {
enum surround51_e : int {
front_left,
front_right,
front_center,
low_frequency, // subwoofer
back_left,
back_right,
channels51 // number of channels in surround sound
};
auto sample_in_pos = std::begin(sample_in);
auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51;
for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; sample_out_p += channels51) {
std::uint32_t left {}, right {};
left += sample_out_p[front_left];
left += sample_out_p[front_center] * 90 / 100;
left += sample_out_p[low_frequency] * 30 / 100;
left += sample_out_p[back_left] * 70 / 100;
left += sample_out_p[back_right] * 30 / 100;
right += sample_out_p[front_right];
right += sample_out_p[front_center] * 90 / 100;
right += sample_out_p[low_frequency] * 30 / 100;
right += sample_out_p[back_left] * 30 / 100;
right += sample_out_p[back_right] * 70 / 100;;
*sample_in_pos++ = (std::uint16_t)left;
*sample_in_pos++ = (std::uint16_t)right;
}
}
void mono_to_stereo(std::vector<std::int16_t> &sample_in, const util::buffer_t<std::int16_t> &sample_out) {
auto sample_in_pos = std::begin(sample_in);
auto sample_end = std::begin(sample_out) + sample_in.size() / 2;
for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; ++sample_out_p) {
*sample_in_pos++ = *sample_out_p;
*sample_in_pos++ = *sample_out_p;
}
}
audio_client_t make_audio_client(device_t &device, const format_t &format, int sample_rate) {
audio_client_t audio_client;
auto status = device->Activate(
IID_IAudioClient,
CLSCTX_ALL,
nullptr,
(void **)&audio_client);
if(FAILED(status)) {
BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']';
return nullptr;
}
wave_format_t wave_format;
status = audio_client->GetMixFormat(&wave_format);
if(FAILED(status)) {
BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']';
return nullptr;
}
wave_format->wBitsPerSample = 16;
wave_format->nSamplesPerSec = sample_rate;
switch(wave_format->wFormatTag) {
case WAVE_FORMAT_PCM:
break;
case WAVE_FORMAT_IEEE_FLOAT:
break;
case WAVE_FORMAT_EXTENSIBLE: {
auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get();
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wave_ex->Samples.wValidBitsPerSample = 16;
break;
}
BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']';
}
default:
BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']';
return nullptr;
};
set_wave_format(wave_format, format);
status = audio_client->Initialize(
AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0,
wave_format.get(),
nullptr);
if(status) {
BOOST_LOG(debug) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']';
return nullptr;
}
return audio_client;
}
class mic_wasapi_t : public mic_t {
public:
capture_e sample(std::vector<std::int16_t> &sample_in) override {
while(sample_buf_pos - std::begin(sample_buf) < sample_in.size()) {
auto sample_size = sample_in.size() /2 * format->channels;
while(sample_buf_pos - std::begin(sample_buf) < sample_size) {
//FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples
auto capture_result = _fill_buffer();
@@ -61,17 +205,32 @@ public:
}
}
std::copy_n(std::begin(sample_buf), sample_in.size(), std::begin(sample_in));
switch(format->channels) {
case 1:
mono_to_stereo(sample_in, sample_buf);
break;
case 2:
std::copy_n(std::begin(sample_buf), sample_size, std::begin(sample_in));
break;
case 6:
if(format->name == "Surround 5.1"sv) {
surround51_to_stereo(sample_in, sample_buf);
break;
}
BOOST_LOG(error) << '[' << format->name << "] not yet supported"sv;
return capture_e::error;
}
// The excess samples should be in front of the queue
std::move(&sample_buf[sample_in.size()], sample_buf_pos, std::begin(sample_buf));
sample_buf_pos -= sample_in.size();
std::move(&sample_buf[sample_size], sample_buf_pos, std::begin(sample_buf));
sample_buf_pos -= sample_size;
return capture_e::ok;
}
int init(std::uint32_t sample_rate) {
int init(std::uint32_t sample_rate, std::uint32_t frame_size) {
audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr));
if(!audio_event) {
BOOST_LOG(error) << "Couldn't create Event handle"sv;
@@ -113,70 +272,26 @@ public:
return -1;
}
status = device->Activate(
IID_IAudioClient,
CLSCTX_ALL,
nullptr,
(void **) &audio_client);
for(auto &format : formats) {
BOOST_LOG(debug) << "Trying audio format ["sv << format.name << ']';
audio_client = make_audio_client(device, format, sample_rate);
if (FAILED(status)) {
BOOST_LOG(error) << "Couldn't activate audio Device [0x"sv << util::hex(status).to_string_view() << ']';
return -1;
}
status = audio_client->GetMixFormat(&wave_format);
if(FAILED(status)) {
BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']';
return -1;
}
wave_format->nChannels = 2;
wave_format->wBitsPerSample = 16;
wave_format->nSamplesPerSec = sample_rate;
wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8;
wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign;
switch(wave_format->wFormatTag) {
case WAVE_FORMAT_PCM:
if(audio_client) {
BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']';
this->format = &format;
break;
case WAVE_FORMAT_IEEE_FLOAT:
wave_format->wFormatTag = WAVE_FORMAT_PCM;
break;
case WAVE_FORMAT_EXTENSIBLE: {
auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get();
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wave_ex->Samples.wValidBitsPerSample = 16;
break;
}
BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']';
return -1;
}
default:
BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']';
return -1;
};
}
if(!audio_client) {
BOOST_LOG(error) << "Couldn't find supported format for audio"sv;
return -1;
}
REFERENCE_TIME default_latency;
audio_client->GetDevicePeriod(&default_latency, nullptr);
default_latency_ms = default_latency / 1000;
status = audio_client->Initialize(
AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0,
wave_format.get(),
nullptr);
if (FAILED(status)) {
BOOST_LOG(error) << "Couldn't initialize audio client [0x"sv << util::hex(status).to_string_view() << ']';
return -1;
}
std::uint32_t frames;
status = audio_client->GetBufferSize(&frames);
if (FAILED(status)) {
@@ -185,7 +300,8 @@ public:
return -1;
}
sample_buf = util::buffer_t<std::int16_t> { frames };
// *2 --> needs to fit double
sample_buf = util::buffer_t<std::int16_t> { std::max(frames *2, frame_size * format->channels *2) };
sample_buf_pos = std::begin(sample_buf);
status = audio_client->GetService(IID_IAudioCaptureClient, (void**)&audio_capture);
@@ -267,7 +383,7 @@ private:
}
sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos;
auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * wave_format->nChannels);
auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels);
if (buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) {
std::fill_n(sample_buf_pos, n, 0);
@@ -296,13 +412,14 @@ public:
device_enum_t device_enum;
device_t device;
audio_client_t audio_client;
wave_format_t wave_format;
audio_capture_t audio_capture;
REFERENCE_TIME default_latency_ms;
util::buffer_t<std::int16_t> sample_buf;
std::int16_t *sample_buf_pos;
format_t *format;
};
}
@@ -313,10 +430,10 @@ namespace dxgi {
int init();
}
std::unique_ptr<mic_t> microphone(std::uint32_t sample_rate) {
std::unique_ptr<mic_t> microphone(std::uint32_t sample_rate, std::uint32_t frame_size) {
auto mic = std::make_unique<audio::mic_wasapi_t>();
if(mic->init(sample_rate)) {
if(mic->init(sample_rate, frame_size)) {
return nullptr;
}