From f723fcb759ffea529831b56c95172f8acc5025b1 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 12:41:49 -0500 Subject: [PATCH 001/309] Add read function to audio_driver --- audio/audio_driver.c | 1 + audio/audio_driver.h | 9 +++++++++ audio/audio_thread_wrapper.c | 1 + audio/drivers/alsa.c | 1 + audio/drivers/alsa_qsa.c | 1 + audio/drivers/alsathread.c | 1 + audio/drivers/audioio.c | 1 + audio/drivers/coreaudio.c | 1 + audio/drivers/ctr_csnd_audio.c | 1 + audio/drivers/ctr_dsp_audio.c | 1 + audio/drivers/ctr_dsp_thread_audio.c | 1 + audio/drivers/dsound.c | 1 + audio/drivers/gx_audio.c | 1 + audio/drivers/jack.c | 1 + audio/drivers/openal.c | 1 + audio/drivers/opensl.c | 1 + audio/drivers/oss.c | 1 + audio/drivers/ps2_audio.c | 1 + audio/drivers/ps3_audio.c | 1 + audio/drivers/psp_audio.c | 1 + audio/drivers/pulse.c | 1 + audio/drivers/roar.c | 1 + audio/drivers/rsound.c | 1 + audio/drivers/rwebaudio.c | 1 + audio/drivers/sdl_audio.c | 1 + audio/drivers/switch_audio.c | 1 + audio/drivers/switch_libnx_audren_audio.c | 1 + audio/drivers/switch_libnx_audren_thread_audio.c | 1 + audio/drivers/switch_thread_audio.c | 1 + audio/drivers/tinyalsa.c | 1 + audio/drivers/wasapi.c | 1 + audio/drivers/wiiu_audio.c | 1 + audio/drivers/xaudio.c | 1 + audio/drivers/xenon360_audio.c | 1 + 34 files changed, 42 insertions(+) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index a82270cf213e..497c2955baf0 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -67,6 +67,7 @@ audio_driver_t audio_null = { NULL, /* init */ NULL, /* write */ + NULL, /* read */ NULL, /* stop */ NULL, /* start */ NULL, /* alive */ diff --git a/audio/audio_driver.h b/audio/audio_driver.h index f7e8e429c408..eafb7de57ffa 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -112,6 +112,15 @@ typedef struct audio_driver */ ssize_t (*write)(void *data, const void *buf, size_t size); + /* + * @data : Pointer to audio data handle. + * @buf : Buffer for received audio data. + * @size : Size of audio buffer. + * + * Read samples from the input driver, e.g. for microphones. + */ + ssize_t (*read)(void *data, const void *buf, size_t size); + /* Temporarily pauses the audio driver. */ bool (*stop)(void *data); diff --git a/audio/audio_thread_wrapper.c b/audio/audio_thread_wrapper.c index 964ddd29b99b..2b7de2513208 100644 --- a/audio/audio_thread_wrapper.c +++ b/audio/audio_thread_wrapper.c @@ -253,6 +253,7 @@ static ssize_t audio_thread_write(void *data, const void *buf, size_t size) static const audio_driver_t audio_thread = { NULL, audio_thread_write, + NULL, audio_thread_stop, audio_thread_start, audio_thread_alive, diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 8a604324846b..8bf3a1df30f9 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -417,6 +417,7 @@ static void alsa_device_list_free(void *data, void *array_list_data) audio_driver_t audio_alsa = { alsa_init, alsa_write, + NULL, alsa_stop, alsa_start, alsa_alive, diff --git a/audio/drivers/alsa_qsa.c b/audio/drivers/alsa_qsa.c index f534a7a7c69f..efd9157ea99e 100644 --- a/audio/drivers/alsa_qsa.c +++ b/audio/drivers/alsa_qsa.c @@ -368,6 +368,7 @@ static size_t alsa_qsa_buffer_size(void *data) audio_driver_t audio_alsa = { alsa_qsa_init, alsa_qsa_write, + NULL, alsa_qsa_stop, alsa_qsa_start, alsa_qsa_alive, diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 1e239ef119fa..e67c460f703b 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -405,6 +405,7 @@ static void alsa_thread_device_list_free(void *data, void *array_list_data) audio_driver_t audio_alsathread = { alsa_thread_init, alsa_thread_write, + NULL, alsa_thread_stop, alsa_thread_start, alsa_thread_alive, diff --git a/audio/drivers/audioio.c b/audio/drivers/audioio.c index 8176a0d7b4d3..00079d02561d 100644 --- a/audio/drivers/audioio.c +++ b/audio/drivers/audioio.c @@ -198,6 +198,7 @@ static bool audioio_use_float(void *data) audio_driver_t audio_audioio = { audioio_init, audioio_write, + NULL, audioio_stop, audioio_start, audioio_alive, diff --git a/audio/drivers/coreaudio.c b/audio/drivers/coreaudio.c index 0c7d07c8188f..033349c073e7 100644 --- a/audio/drivers/coreaudio.c +++ b/audio/drivers/coreaudio.c @@ -455,6 +455,7 @@ static void coreaudio_device_list_free(void *data, void *array_list_data) audio_driver_t audio_coreaudio = { coreaudio_init, coreaudio_write, + NULL, coreaudio_stop, coreaudio_start, coreaudio_alive, diff --git a/audio/drivers/ctr_csnd_audio.c b/audio/drivers/ctr_csnd_audio.c index eb7986f90b97..0c0cab9e7fe6 100644 --- a/audio/drivers/ctr_csnd_audio.c +++ b/audio/drivers/ctr_csnd_audio.c @@ -292,6 +292,7 @@ static size_t ctr_csnd_audio_buffer_size(void *data) audio_driver_t audio_ctr_csnd = { ctr_csnd_audio_init, ctr_csnd_audio_write, + NULL, ctr_csnd_audio_stop, ctr_csnd_audio_start, ctr_csnd_audio_alive, diff --git a/audio/drivers/ctr_dsp_audio.c b/audio/drivers/ctr_dsp_audio.c index e25f0bede472..d0c209cbdc03 100644 --- a/audio/drivers/ctr_dsp_audio.c +++ b/audio/drivers/ctr_dsp_audio.c @@ -206,6 +206,7 @@ static size_t ctr_dsp_audio_buffer_size(void *data) audio_driver_t audio_ctr_dsp = { ctr_dsp_audio_init, ctr_dsp_audio_write, + NULL, ctr_dsp_audio_stop, ctr_dsp_audio_start, ctr_dsp_audio_alive, diff --git a/audio/drivers/ctr_dsp_thread_audio.c b/audio/drivers/ctr_dsp_thread_audio.c index 4fbbf5f6fa6b..69e2b7a354f2 100644 --- a/audio/drivers/ctr_dsp_thread_audio.c +++ b/audio/drivers/ctr_dsp_thread_audio.c @@ -325,6 +325,7 @@ static size_t ctr_dsp_thread_audio_buffer_size(void *data) audio_driver_t audio_ctr_dsp_thread = { ctr_dsp_thread_audio_init, ctr_dsp_thread_audio_write, + NULL, ctr_dsp_thread_audio_stop, ctr_dsp_thread_audio_start, ctr_dsp_thread_audio_alive, diff --git a/audio/drivers/dsound.c b/audio/drivers/dsound.c index 000540540f39..a1f68d9ad047 100644 --- a/audio/drivers/dsound.c +++ b/audio/drivers/dsound.c @@ -602,6 +602,7 @@ static void dsound_device_list_free(void *u, void *slp) audio_driver_t audio_dsound = { dsound_init, dsound_write, + NULL, dsound_stop, dsound_start, dsound_alive, diff --git a/audio/drivers/gx_audio.c b/audio/drivers/gx_audio.c index f594863b9815..2ce7e4cf46c8 100644 --- a/audio/drivers/gx_audio.c +++ b/audio/drivers/gx_audio.c @@ -225,6 +225,7 @@ static bool gx_audio_use_float(void *data) audio_driver_t audio_gx = { gx_audio_init, gx_audio_write, + NULL, gx_audio_stop, gx_audio_start, gx_audio_alive, diff --git a/audio/drivers/jack.c b/audio/drivers/jack.c index 6d199067e252..0bc3bc0bc79d 100644 --- a/audio/drivers/jack.c +++ b/audio/drivers/jack.c @@ -371,6 +371,7 @@ static size_t ja_buffer_size(void *data) audio_driver_t audio_jack = { ja_init, ja_write, + NULL, ja_stop, ja_start, ja_alive, diff --git a/audio/drivers/openal.c b/audio/drivers/openal.c index 606274847e18..dabde35abec1 100644 --- a/audio/drivers/openal.c +++ b/audio/drivers/openal.c @@ -263,6 +263,7 @@ static bool al_use_float(void *data) audio_driver_t audio_openal = { al_init, al_write, + NULL, al_stop, al_start, al_alive, diff --git a/audio/drivers/opensl.c b/audio/drivers/opensl.c index 70f584653821..20826181844b 100644 --- a/audio/drivers/opensl.c +++ b/audio/drivers/opensl.c @@ -306,6 +306,7 @@ static bool sl_use_float(void *data) audio_driver_t audio_opensl = { sl_init, sl_write, + NULL, sl_stop, sl_start, sl_alive, diff --git a/audio/drivers/oss.c b/audio/drivers/oss.c index 52b525ac34a6..7dcc4f7a19e8 100644 --- a/audio/drivers/oss.c +++ b/audio/drivers/oss.c @@ -216,6 +216,7 @@ static bool oss_use_float(void *data) audio_driver_t audio_oss = { oss_init, oss_write, + NULL, oss_stop, oss_start, oss_alive, diff --git a/audio/drivers/ps2_audio.c b/audio/drivers/ps2_audio.c index 786b2a49b429..20d33cab7bc4 100644 --- a/audio/drivers/ps2_audio.c +++ b/audio/drivers/ps2_audio.c @@ -148,6 +148,7 @@ static size_t ps2_audio_buffer_size(void *data) audio_driver_t audio_ps2 = { ps2_audio_init, ps2_audio_write, + NULL, ps2_audio_stop, ps2_audio_start, ps2_audio_alive, diff --git a/audio/drivers/ps3_audio.c b/audio/drivers/ps3_audio.c index 1b40ed645f95..99b427907910 100644 --- a/audio/drivers/ps3_audio.c +++ b/audio/drivers/ps3_audio.c @@ -234,6 +234,7 @@ static size_t ps3_audio_write_avail(void *data) audio_driver_t audio_ps3 = { ps3_audio_init, ps3_audio_write, + NULL, ps3_audio_stop, ps3_audio_start, ps3_audio_alive, diff --git a/audio/drivers/psp_audio.c b/audio/drivers/psp_audio.c index 6e9a394672a5..375efd162dfa 100644 --- a/audio/drivers/psp_audio.c +++ b/audio/drivers/psp_audio.c @@ -322,6 +322,7 @@ static size_t psp_buffer_size(void *data) audio_driver_t audio_psp = { psp_audio_init, psp_audio_write, + NULL, psp_audio_stop, psp_audio_start, psp_audio_alive, diff --git a/audio/drivers/pulse.c b/audio/drivers/pulse.c index fd27ca7b8fcd..eed7fef8a192 100644 --- a/audio/drivers/pulse.c +++ b/audio/drivers/pulse.c @@ -355,6 +355,7 @@ static size_t pulse_buffer_size(void *data) audio_driver_t audio_pulse = { pulse_init, pulse_write, + NULL, pulse_stop, pulse_start, pulse_alive, diff --git a/audio/drivers/roar.c b/audio/drivers/roar.c index a36bbb7b2626..64839041700b 100644 --- a/audio/drivers/roar.c +++ b/audio/drivers/roar.c @@ -142,6 +142,7 @@ static size_t ra_write_avail(void *data) audio_driver_t audio_roar = { ra_init, ra_write, + NULL, ra_stop, ra_start, ra_alive, diff --git a/audio/drivers/rsound.c b/audio/drivers/rsound.c index fbc6f749a76a..66c6469853e0 100644 --- a/audio/drivers/rsound.c +++ b/audio/drivers/rsound.c @@ -228,6 +228,7 @@ static bool rs_use_float(void *data) audio_driver_t audio_rsound = { rs_init, rs_write, + NULL, rs_stop, rs_start, rs_alive, diff --git a/audio/drivers/rwebaudio.c b/audio/drivers/rwebaudio.c index 41cf2d1ea6bb..c38622fd45e9 100644 --- a/audio/drivers/rwebaudio.c +++ b/audio/drivers/rwebaudio.c @@ -97,6 +97,7 @@ static bool rwebaudio_use_float(void *data) { return true; } audio_driver_t audio_rwebaudio = { rwebaudio_init, rwebaudio_write, + NULL, rwebaudio_stop, rwebaudio_start, rwebaudio_alive, diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 56554083f575..cb67ef659b34 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -260,6 +260,7 @@ static size_t sdl_audio_write_avail(void *data) audio_driver_t audio_sdl = { sdl_audio_init, sdl_audio_write, + NULL, sdl_audio_stop, sdl_audio_start, sdl_audio_alive, diff --git a/audio/drivers/switch_audio.c b/audio/drivers/switch_audio.c index a2b6291ccbfc..c15a98762c74 100644 --- a/audio/drivers/switch_audio.c +++ b/audio/drivers/switch_audio.c @@ -355,6 +355,7 @@ static void *switch_audio_init(const char *device, audio_driver_t audio_switch = { switch_audio_init, switch_audio_write, + NULL, switch_audio_stop, switch_audio_start, switch_audio_alive, diff --git a/audio/drivers/switch_libnx_audren_audio.c b/audio/drivers/switch_libnx_audren_audio.c index 998a25a6a270..ca2a1908ae47 100644 --- a/audio/drivers/switch_libnx_audren_audio.c +++ b/audio/drivers/switch_libnx_audren_audio.c @@ -354,6 +354,7 @@ static void libnx_audren_audio_set_nonblock_state(void *data, bool state) audio_driver_t audio_switch_libnx_audren = { libnx_audren_audio_init, libnx_audren_audio_write, + NULL, libnx_audren_audio_stop, libnx_audren_audio_start, libnx_audren_audio_alive, diff --git a/audio/drivers/switch_libnx_audren_thread_audio.c b/audio/drivers/switch_libnx_audren_thread_audio.c index 286dbd1dc637..bef63a9fedbd 100644 --- a/audio/drivers/switch_libnx_audren_thread_audio.c +++ b/audio/drivers/switch_libnx_audren_thread_audio.c @@ -427,6 +427,7 @@ static void libnx_audren_thread_audio_set_nonblock_state(void *data, bool state) audio_driver_t audio_switch_libnx_audren_thread = { libnx_audren_thread_audio_init, libnx_audren_thread_audio_write, + NULL, libnx_audren_thread_audio_stop, libnx_audren_thread_audio_start, libnx_audren_thread_audio_alive, diff --git a/audio/drivers/switch_thread_audio.c b/audio/drivers/switch_thread_audio.c index 3a530b24f07d..7919deb7cbf4 100644 --- a/audio/drivers/switch_thread_audio.c +++ b/audio/drivers/switch_thread_audio.c @@ -432,6 +432,7 @@ size_t switch_thread_audio_buffer_size(void *data) audio_driver_t audio_switch_thread = { switch_thread_audio_init, switch_thread_audio_write, + NULL, switch_thread_audio_stop, switch_thread_audio_start, switch_thread_audio_alive, diff --git a/audio/drivers/tinyalsa.c b/audio/drivers/tinyalsa.c index fee417477709..eb40c06b3962 100644 --- a/audio/drivers/tinyalsa.c +++ b/audio/drivers/tinyalsa.c @@ -2416,6 +2416,7 @@ static size_t tinyalsa_buffer_size(void *data) audio_driver_t audio_tinyalsa = { tinyalsa_init, /* AUDIO_init */ tinyalsa_write, /* AUDIO_write */ + NULL, /* AUDIO_read */ /*TODO*/ tinyalsa_stop, /* AUDIO_stop */ tinyalsa_start, /* AUDIO_start */ tinyalsa_alive, /* AUDIO_alive */ diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index 5f47fd2dd8c5..e32896ba0e97 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -911,6 +911,7 @@ static size_t wasapi_buffer_size(void *wh) audio_driver_t audio_wasapi = { wasapi_init, wasapi_write, + NULL, wasapi_stop, wasapi_start, wasapi_alive, diff --git a/audio/drivers/wiiu_audio.c b/audio/drivers/wiiu_audio.c index de5d3d93531c..3dc4471d0085 100644 --- a/audio/drivers/wiiu_audio.c +++ b/audio/drivers/wiiu_audio.c @@ -322,6 +322,7 @@ audio_driver_t audio_ax = { ax_audio_init, ax_audio_write, + NULL, /* read */ ax_audio_stop, ax_audio_start, ax_audio_alive, diff --git a/audio/drivers/xaudio.c b/audio/drivers/xaudio.c index 277e8ed671e6..21cfee199d3d 100644 --- a/audio/drivers/xaudio.c +++ b/audio/drivers/xaudio.c @@ -520,6 +520,7 @@ static void *xa_list_new(void *u) audio_driver_t audio_xa = { xa_init, xa_write, + NULL, xa_stop, xa_start, xa_alive, diff --git a/audio/drivers/xenon360_audio.c b/audio/drivers/xenon360_audio.c index 98a71443f267..f0042e6b3ae4 100644 --- a/audio/drivers/xenon360_audio.c +++ b/audio/drivers/xenon360_audio.c @@ -140,6 +140,7 @@ static size_t xenon360_write_avail(void *data) audio_driver_t audio_xenon360 = { xenon360_audio_init, xenon360_audio_write, + NULL, xenon360_audio_stop, xenon360_audio_start, xenon360_audio_alive, From c323e5ae7f53abb463fc08141eb4bc13709c674a Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 13:25:57 -0500 Subject: [PATCH 002/309] Add read_avail callback to audio_driver --- audio/audio_driver.c | 1 + audio/audio_driver.h | 2 ++ audio/drivers/alsa.c | 1 + audio/drivers/alsa_qsa.c | 1 + audio/drivers/alsathread.c | 1 + audio/drivers/audioio.c | 1 + audio/drivers/coreaudio.c | 1 + audio/drivers/ctr_csnd_audio.c | 1 + audio/drivers/ctr_dsp_audio.c | 1 + audio/drivers/ctr_dsp_thread_audio.c | 1 + audio/drivers/dsound.c | 1 + audio/drivers/gx_audio.c | 1 + audio/drivers/jack.c | 1 + audio/drivers/openal.c | 1 + audio/drivers/opensl.c | 1 + audio/drivers/oss.c | 1 + audio/drivers/ps2_audio.c | 1 + audio/drivers/ps3_audio.c | 1 + audio/drivers/psp_audio.c | 1 + audio/drivers/pulse.c | 1 + audio/drivers/roar.c | 1 + audio/drivers/rsound.c | 1 + audio/drivers/rwebaudio.c | 1 + audio/drivers/sdl_audio.c | 1 + audio/drivers/switch_audio.c | 1 + audio/drivers/switch_libnx_audren_audio.c | 1 + audio/drivers/switch_libnx_audren_thread_audio.c | 1 + audio/drivers/switch_thread_audio.c | 1 + audio/drivers/tinyalsa.c | 1 + audio/drivers/wasapi.c | 1 + audio/drivers/wiiu_audio.c | 1 + audio/drivers/xaudio.c | 1 + audio/drivers/xenon360_audio.c | 1 + 33 files changed, 34 insertions(+) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 497c2955baf0..4bfbe13b63b9 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -78,6 +78,7 @@ audio_driver_t audio_null = { NULL, NULL, NULL, /* write_avail */ + NULL, /* read_avail */ NULL }; diff --git a/audio/audio_driver.h b/audio/audio_driver.h index eafb7de57ffa..c7e67703eeea 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -164,6 +164,8 @@ typedef struct audio_driver /* Optional. */ size_t (*write_avail)(void *data); + size_t (*read_avail)(void *data); + size_t (*buffer_size)(void *data); } audio_driver_t; diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 8bf3a1df30f9..85ff0193eda1 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -428,5 +428,6 @@ audio_driver_t audio_alsa = { alsa_device_list_new, alsa_device_list_free, alsa_write_avail, + NULL, alsa_buffer_size, }; diff --git a/audio/drivers/alsa_qsa.c b/audio/drivers/alsa_qsa.c index efd9157ea99e..a72cfa67be04 100644 --- a/audio/drivers/alsa_qsa.c +++ b/audio/drivers/alsa_qsa.c @@ -379,5 +379,6 @@ audio_driver_t audio_alsa = { NULL, NULL, alsa_qsa_write_avail, + NULL, alsa_qsa_buffer_size, }; diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index e67c460f703b..5f368dc7e048 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -416,5 +416,6 @@ audio_driver_t audio_alsathread = { alsa_thread_device_list_new, alsa_thread_device_list_free, alsa_thread_write_avail, + NULL, alsa_thread_buffer_size, }; diff --git a/audio/drivers/audioio.c b/audio/drivers/audioio.c index 00079d02561d..39187c96961c 100644 --- a/audio/drivers/audioio.c +++ b/audio/drivers/audioio.c @@ -209,5 +209,6 @@ audio_driver_t audio_audioio = { NULL, NULL, audioio_write_avail, + NULL, audioio_buffer_size, }; diff --git a/audio/drivers/coreaudio.c b/audio/drivers/coreaudio.c index 033349c073e7..b6f5732a9441 100644 --- a/audio/drivers/coreaudio.c +++ b/audio/drivers/coreaudio.c @@ -466,5 +466,6 @@ audio_driver_t audio_coreaudio = { coreaudio_device_list_new, coreaudio_device_list_free, coreaudio_write_avail, + NULL, coreaudio_buffer_size, }; diff --git a/audio/drivers/ctr_csnd_audio.c b/audio/drivers/ctr_csnd_audio.c index 0c0cab9e7fe6..0a155e073fe1 100644 --- a/audio/drivers/ctr_csnd_audio.c +++ b/audio/drivers/ctr_csnd_audio.c @@ -303,5 +303,6 @@ audio_driver_t audio_ctr_csnd = { NULL, NULL, ctr_csnd_audio_write_avail, + NULL, ctr_csnd_audio_buffer_size }; diff --git a/audio/drivers/ctr_dsp_audio.c b/audio/drivers/ctr_dsp_audio.c index d0c209cbdc03..8bdfafd11158 100644 --- a/audio/drivers/ctr_dsp_audio.c +++ b/audio/drivers/ctr_dsp_audio.c @@ -217,5 +217,6 @@ audio_driver_t audio_ctr_dsp = { NULL, NULL, ctr_dsp_audio_write_avail, + NULL, ctr_dsp_audio_buffer_size }; diff --git a/audio/drivers/ctr_dsp_thread_audio.c b/audio/drivers/ctr_dsp_thread_audio.c index 69e2b7a354f2..cf5913f7c8b9 100644 --- a/audio/drivers/ctr_dsp_thread_audio.c +++ b/audio/drivers/ctr_dsp_thread_audio.c @@ -336,5 +336,6 @@ audio_driver_t audio_ctr_dsp_thread = { NULL, NULL, ctr_dsp_thread_audio_write_avail, + NULL, ctr_dsp_thread_audio_buffer_size }; diff --git a/audio/drivers/dsound.c b/audio/drivers/dsound.c index a1f68d9ad047..32e7a0788870 100644 --- a/audio/drivers/dsound.c +++ b/audio/drivers/dsound.c @@ -613,5 +613,6 @@ audio_driver_t audio_dsound = { dsound_list_new, dsound_device_list_free, dsound_write_avail, + NULL, dsound_buffer_size, }; diff --git a/audio/drivers/gx_audio.c b/audio/drivers/gx_audio.c index 2ce7e4cf46c8..940b2f524a16 100644 --- a/audio/drivers/gx_audio.c +++ b/audio/drivers/gx_audio.c @@ -236,5 +236,6 @@ audio_driver_t audio_gx = { NULL, NULL, gx_audio_write_avail, + NULL, gx_audio_buffer_size, }; diff --git a/audio/drivers/jack.c b/audio/drivers/jack.c index 0bc3bc0bc79d..3991864f4bae 100644 --- a/audio/drivers/jack.c +++ b/audio/drivers/jack.c @@ -382,5 +382,6 @@ audio_driver_t audio_jack = { NULL, NULL, ja_write_avail, + NULL, ja_buffer_size, }; diff --git a/audio/drivers/openal.c b/audio/drivers/openal.c index dabde35abec1..bb5970e4738f 100644 --- a/audio/drivers/openal.c +++ b/audio/drivers/openal.c @@ -274,5 +274,6 @@ audio_driver_t audio_openal = { NULL, NULL, al_write_avail, + NULL, al_buffer_size, }; diff --git a/audio/drivers/opensl.c b/audio/drivers/opensl.c index 20826181844b..f7562eacd77c 100644 --- a/audio/drivers/opensl.c +++ b/audio/drivers/opensl.c @@ -317,5 +317,6 @@ audio_driver_t audio_opensl = { NULL, NULL, sl_write_avail, + NULL, sl_buffer_size, }; diff --git a/audio/drivers/oss.c b/audio/drivers/oss.c index 7dcc4f7a19e8..408a39141094 100644 --- a/audio/drivers/oss.c +++ b/audio/drivers/oss.c @@ -227,5 +227,6 @@ audio_driver_t audio_oss = { NULL, NULL, oss_write_avail, + NULL, oss_buffer_size, }; diff --git a/audio/drivers/ps2_audio.c b/audio/drivers/ps2_audio.c index 20d33cab7bc4..e8fb7d753731 100644 --- a/audio/drivers/ps2_audio.c +++ b/audio/drivers/ps2_audio.c @@ -159,5 +159,6 @@ audio_driver_t audio_ps2 = { NULL, NULL, ps2_audio_write_avail, + NULL, ps2_audio_buffer_size }; diff --git a/audio/drivers/ps3_audio.c b/audio/drivers/ps3_audio.c index 99b427907910..aa97dd40013d 100644 --- a/audio/drivers/ps3_audio.c +++ b/audio/drivers/ps3_audio.c @@ -245,5 +245,6 @@ audio_driver_t audio_ps3 = { NULL, NULL, ps3_audio_write_avail, + NULL, NULL }; diff --git a/audio/drivers/psp_audio.c b/audio/drivers/psp_audio.c index 375efd162dfa..a787910b53e6 100644 --- a/audio/drivers/psp_audio.c +++ b/audio/drivers/psp_audio.c @@ -339,5 +339,6 @@ audio_driver_t audio_psp = { NULL, NULL, psp_write_avail, + NULL, psp_buffer_size }; diff --git a/audio/drivers/pulse.c b/audio/drivers/pulse.c index eed7fef8a192..3a7777d70efb 100644 --- a/audio/drivers/pulse.c +++ b/audio/drivers/pulse.c @@ -366,5 +366,6 @@ audio_driver_t audio_pulse = { NULL, NULL, pulse_write_avail, + NULL, pulse_buffer_size, }; diff --git a/audio/drivers/roar.c b/audio/drivers/roar.c index 64839041700b..93e6b42e5607 100644 --- a/audio/drivers/roar.c +++ b/audio/drivers/roar.c @@ -153,5 +153,6 @@ audio_driver_t audio_roar = { NULL, NULL, ra_write_avail, + NULL, NULL }; diff --git a/audio/drivers/rsound.c b/audio/drivers/rsound.c index 66c6469853e0..99f8ded5fe6d 100644 --- a/audio/drivers/rsound.c +++ b/audio/drivers/rsound.c @@ -239,5 +239,6 @@ audio_driver_t audio_rsound = { NULL, NULL, rs_write_avail, + NULL, rs_buffer_size, }; diff --git a/audio/drivers/rwebaudio.c b/audio/drivers/rwebaudio.c index c38622fd45e9..0eb3ba130a13 100644 --- a/audio/drivers/rwebaudio.c +++ b/audio/drivers/rwebaudio.c @@ -108,5 +108,6 @@ audio_driver_t audio_rwebaudio = { NULL, NULL, rwebaudio_write_avail, + NULL, rwebaudio_buffer_size, }; diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index cb67ef659b34..164758afa37f 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -275,5 +275,6 @@ audio_driver_t audio_sdl = { NULL, NULL, sdl_audio_write_avail, + NULL, NULL }; diff --git a/audio/drivers/switch_audio.c b/audio/drivers/switch_audio.c index c15a98762c74..d3fea49ef080 100644 --- a/audio/drivers/switch_audio.c +++ b/audio/drivers/switch_audio.c @@ -366,5 +366,6 @@ audio_driver_t audio_switch = { NULL, /* device_list_new */ NULL, /* device_list_free */ switch_audio_write_avail, + NULL, switch_audio_buffer_size, /* buffer_size */ }; diff --git a/audio/drivers/switch_libnx_audren_audio.c b/audio/drivers/switch_libnx_audren_audio.c index ca2a1908ae47..04bb06560b9b 100644 --- a/audio/drivers/switch_libnx_audren_audio.c +++ b/audio/drivers/switch_libnx_audren_audio.c @@ -365,5 +365,6 @@ audio_driver_t audio_switch_libnx_audren = { NULL, /* device_list_new */ NULL, /* device_list_free */ libnx_audren_audio_write_avail, + NULL, libnx_audren_audio_buffer_size, }; diff --git a/audio/drivers/switch_libnx_audren_thread_audio.c b/audio/drivers/switch_libnx_audren_thread_audio.c index bef63a9fedbd..5762c875f2e1 100644 --- a/audio/drivers/switch_libnx_audren_thread_audio.c +++ b/audio/drivers/switch_libnx_audren_thread_audio.c @@ -438,5 +438,6 @@ audio_driver_t audio_switch_libnx_audren_thread = { NULL, /* device_list_new */ NULL, /* device_list_free */ libnx_audren_thread_audio_write_avail, + NULL, libnx_audren_thread_audio_buffer_size, }; diff --git a/audio/drivers/switch_thread_audio.c b/audio/drivers/switch_thread_audio.c index 7919deb7cbf4..755f22a6b96f 100644 --- a/audio/drivers/switch_thread_audio.c +++ b/audio/drivers/switch_thread_audio.c @@ -443,6 +443,7 @@ audio_driver_t audio_switch_thread = { NULL, /* device_list_new */ NULL, /* device_list_free */ switch_thread_audio_write_avail, + NULL, switch_thread_audio_buffer_size }; diff --git a/audio/drivers/tinyalsa.c b/audio/drivers/tinyalsa.c index eb40c06b3962..ee60b2fef7ca 100644 --- a/audio/drivers/tinyalsa.c +++ b/audio/drivers/tinyalsa.c @@ -2427,5 +2427,6 @@ audio_driver_t audio_tinyalsa = { NULL, /* AUDIO_device_list_new */ /*TODO*/ NULL, /* AUDIO_device_list_free */ /*TODO*/ tinyalsa_write_avail, /* AUDIO_write_avail */ /*TODO*/ + NULL, /* AUDIO_read_avail */ /*TODO*/ tinyalsa_buffer_size, /* AUDIO_buffer_size */ /*TODO*/ }; diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index e32896ba0e97..3801a9d67f24 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -922,5 +922,6 @@ audio_driver_t audio_wasapi = { mmdevice_list_new, wasapi_device_list_free, wasapi_write_avail, + NULL, wasapi_buffer_size }; diff --git a/audio/drivers/wiiu_audio.c b/audio/drivers/wiiu_audio.c index 3dc4471d0085..b61c8cf654d0 100644 --- a/audio/drivers/wiiu_audio.c +++ b/audio/drivers/wiiu_audio.c @@ -333,5 +333,6 @@ audio_driver_t audio_ax = NULL, /* device_list_new */ NULL, /* device_list_free */ ax_audio_write_avail, + NULL, ax_audio_buffer_size, }; diff --git a/audio/drivers/xaudio.c b/audio/drivers/xaudio.c index 21cfee199d3d..787f674aaf22 100644 --- a/audio/drivers/xaudio.c +++ b/audio/drivers/xaudio.c @@ -531,5 +531,6 @@ audio_driver_t audio_xa = { xa_list_new, xa_device_list_free, xa_write_avail, + NULL, xa_buffer_size, }; diff --git a/audio/drivers/xenon360_audio.c b/audio/drivers/xenon360_audio.c index f0042e6b3ae4..da8aa7ce1441 100644 --- a/audio/drivers/xenon360_audio.c +++ b/audio/drivers/xenon360_audio.c @@ -151,5 +151,6 @@ audio_driver_t audio_xenon360 = { NULL, NULL, xenon360_write_avail, + NULL, NULL }; From 609dc2e41d343daad6bcda5327642d5e23b15d15 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 13:28:03 -0500 Subject: [PATCH 003/309] Don't assume that SDL will only open one audio device - Add a speaker_device field to sdl_audio_t - Use SDL_*AudioDevice functions that don't assume a device ID of 1 --- audio/drivers/sdl_audio.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 164758afa37f..d83a1e222dd4 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -40,6 +40,7 @@ typedef struct sdl_audio fifo_buffer_t *buffer; bool nonblock; bool is_paused; + SDL_AudioDeviceID speaker_device; } sdl_audio_t; static void sdl_audio_cb(void *data, Uint8 *stream, int len) @@ -110,7 +111,8 @@ static void *sdl_audio_init(const char *device, spec.callback = sdl_audio_cb; spec.userdata = sdl; - if (SDL_OpenAudio(&spec, &out) < 0) + sdl->speaker_device = SDL_OpenAudioDevice(NULL, false, &spec, &out, 0); + if (speaker_device == 0) { RARCH_ERR("[SDL audio]: Failed to open SDL audio: %s\n", SDL_GetError()); goto error; @@ -137,7 +139,7 @@ static void *sdl_audio_init(const char *device, free(tmp); } - SDL_PauseAudio(0); + SDL_PauseAudioDevice(sdl->speaker_device, false); return sdl; error: @@ -154,11 +156,11 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) { size_t avail, write_amt; - SDL_LockAudio(); + SDL_LockAudioDevice(sdl->speaker_device); avail = FIFO_WRITE_AVAIL(sdl->buffer); write_amt = avail > size ? size : avail; fifo_write(sdl->buffer, buf, write_amt); - SDL_UnlockAudio(); + SDL_UnlockAudioDevice(sdl->speaker_device); ret = write_amt; } else @@ -169,12 +171,12 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) { size_t avail; - SDL_LockAudio(); + SDL_LockAudioDevice(sdl->speaker_device); avail = FIFO_WRITE_AVAIL(sdl->buffer); if (avail == 0) { - SDL_UnlockAudio(); + SDL_UnlockAudioDevice(sdl->speaker_device); #ifdef HAVE_THREADS slock_lock(sdl->lock); scond_wait(sdl->cond, sdl->lock); @@ -185,7 +187,7 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) { size_t write_amt = size - written > avail ? avail : size - written; fifo_write(sdl->buffer, (const char*)buf + written, write_amt); - SDL_UnlockAudio(); + SDL_UnlockAudioDevice(sdl->speaker_device); written += write_amt; } } @@ -199,7 +201,7 @@ static bool sdl_audio_stop(void *data) { sdl_audio_t *sdl = (sdl_audio_t*)data; sdl->is_paused = true; - SDL_PauseAudio(1); + SDL_PauseAudioDevice(sdl->speaker_device, true); return true; } @@ -216,7 +218,7 @@ static bool sdl_audio_start(void *data, bool is_shutdown) sdl_audio_t *sdl = (sdl_audio_t*)data; sdl->is_paused = false; - SDL_PauseAudio(0); + SDL_PauseAudioDevice(sdl->speaker_device, false); return true; } @@ -231,10 +233,10 @@ static void sdl_audio_free(void *data) { sdl_audio_t *sdl = (sdl_audio_t*)data; - SDL_CloseAudio(); - if (sdl) { + SDL_CloseAudioDevice(sdl->speaker_device); + fifo_free(sdl->buffer); #ifdef HAVE_THREADS slock_free(sdl->lock); From 9fe3dfb3124deaf2f71b88dde8c326b0e59a9551 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 13:35:00 -0500 Subject: [PATCH 004/309] Fix a typo --- audio/drivers/sdl_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index d83a1e222dd4..376b8ed393f6 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -112,7 +112,7 @@ static void *sdl_audio_init(const char *device, spec.userdata = sdl; sdl->speaker_device = SDL_OpenAudioDevice(NULL, false, &spec, &out, 0); - if (speaker_device == 0) + if (sdl->speaker_device == 0) { RARCH_ERR("[SDL audio]: Failed to open SDL audio: %s\n", SDL_GetError()); goto error; From bc6b1e5ccc0385404817b26a46a5f701550f81ad Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 13:45:58 -0500 Subject: [PATCH 005/309] Rename sdl_audio_t.buffer to speaker_buffer --- audio/drivers/sdl_audio.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 376b8ed393f6..21adc19dc1bb 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -37,7 +37,7 @@ typedef struct sdl_audio slock_t *lock; scond_t *cond; #endif - fifo_buffer_t *buffer; + fifo_buffer_t *speaker_buffer; bool nonblock; bool is_paused; SDL_AudioDeviceID speaker_device; @@ -46,10 +46,10 @@ typedef struct sdl_audio static void sdl_audio_cb(void *data, Uint8 *stream, int len) { sdl_audio_t *sdl = (sdl_audio_t*)data; - size_t avail = FIFO_READ_AVAIL(sdl->buffer); + size_t avail = FIFO_READ_AVAIL(sdl->speaker_buffer); size_t write_size = len > (int)avail ? avail : len; - fifo_read(sdl->buffer, stream, write_size); + fifo_read(sdl->speaker_buffer, stream, write_size); #ifdef HAVE_THREADS scond_signal(sdl->cond); #endif @@ -131,11 +131,11 @@ static void *sdl_audio_init(const char *device, /* Create a buffer twice as big as needed and prefill the buffer. */ bufsize = out.samples * 4 * sizeof(int16_t); tmp = calloc(1, bufsize); - sdl->buffer = fifo_new(bufsize); + sdl->speaker_buffer = fifo_new(bufsize); if (tmp) { - fifo_write(sdl->buffer, tmp, bufsize); + fifo_write(sdl->speaker_buffer, tmp, bufsize); free(tmp); } @@ -157,9 +157,9 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) size_t avail, write_amt; SDL_LockAudioDevice(sdl->speaker_device); - avail = FIFO_WRITE_AVAIL(sdl->buffer); + avail = FIFO_WRITE_AVAIL(sdl->speaker_buffer); write_amt = avail > size ? size : avail; - fifo_write(sdl->buffer, buf, write_amt); + fifo_write(sdl->speaker_buffer, buf, write_amt); SDL_UnlockAudioDevice(sdl->speaker_device); ret = write_amt; } @@ -172,7 +172,7 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) size_t avail; SDL_LockAudioDevice(sdl->speaker_device); - avail = FIFO_WRITE_AVAIL(sdl->buffer); + avail = FIFO_WRITE_AVAIL(sdl->speaker_buffer); if (avail == 0) { @@ -186,7 +186,7 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) else { size_t write_amt = size - written > avail ? avail : size - written; - fifo_write(sdl->buffer, (const char*)buf + written, write_amt); + fifo_write(sdl->speaker_buffer, (const char*)buf + written, write_amt); SDL_UnlockAudioDevice(sdl->speaker_device); written += write_amt; } @@ -237,7 +237,7 @@ static void sdl_audio_free(void *data) { SDL_CloseAudioDevice(sdl->speaker_device); - fifo_free(sdl->buffer); + fifo_free(sdl->speaker_buffer); #ifdef HAVE_THREADS slock_free(sdl->lock); scond_free(sdl->cond); From 40746be673762297a393b7a491988683062202ae Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 14:21:25 -0500 Subject: [PATCH 006/309] Initialize and clean up the SDL microphone audio device --- audio/drivers/sdl_audio.c | 68 +++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 21adc19dc1bb..cd2fb79d7809 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -38,12 +38,14 @@ typedef struct sdl_audio scond_t *cond; #endif fifo_buffer_t *speaker_buffer; + fifo_buffer_t *microphone_buffer; bool nonblock; bool is_paused; SDL_AudioDeviceID speaker_device; + SDL_AudioDeviceID microphone_device; } sdl_audio_t; -static void sdl_audio_cb(void *data, Uint8 *stream, int len) +static void sdl_audio_playback_cb(void *data, Uint8 *stream, int len) { sdl_audio_t *sdl = (sdl_audio_t*)data; size_t avail = FIFO_READ_AVAIL(sdl->speaker_buffer); @@ -58,6 +60,11 @@ static void sdl_audio_cb(void *data, Uint8 *stream, int len) memset(stream + write_size, 0, len - write_size); } +static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) +{ + /* TODO: Implement */ +} + static INLINE int find_num_frames(int rate, int latency) { int frames = (rate * latency) / 1000; @@ -104,17 +111,18 @@ static void *sdl_audio_init(const char *device, * SDL double buffers audio and we do as well. */ frames = find_num_frames(rate, latency / 4); + /* First, let's initialize the output device. */ spec.freq = rate; spec.format = AUDIO_S16SYS; spec.channels = 2; spec.samples = frames; /* This is in audio frames, not samples ... :( */ - spec.callback = sdl_audio_cb; + spec.callback = sdl_audio_playback_cb; spec.userdata = sdl; sdl->speaker_device = SDL_OpenAudioDevice(NULL, false, &spec, &out, 0); if (sdl->speaker_device == 0) { - RARCH_ERR("[SDL audio]: Failed to open SDL audio: %s\n", SDL_GetError()); + RARCH_ERR("[SDL audio]: Failed to open SDL audio output device: %s\n", SDL_GetError()); goto error; } @@ -125,7 +133,7 @@ static void *sdl_audio_init(const char *device, sdl->cond = scond_new(); #endif - RARCH_LOG("[SDL audio]: Requested %u ms latency, got %d ms\n", + RARCH_LOG("[SDL audio]: Requested %u ms latency for output device, got %d ms\n", latency, (int)(out.samples * 4 * 1000 / (*new_rate))); /* Create a buffer twice as big as needed and prefill the buffer. */ @@ -140,6 +148,37 @@ static void *sdl_audio_init(const char *device, } SDL_PauseAudioDevice(sdl->speaker_device, false); + + /* Now let's init the microphone */ + spec.callback = sdl_audio_record_cb; /* Microphone should have the same params as speaker */ + + sdl->microphone_device = SDL_OpenAudioDevice(NULL, true, &spec, &out, 0); + if (sdl->microphone_device == 0) + { + RARCH_WARN("[SDL audio]: Failed to open SDL audio input device: %s\n", SDL_GetError()); + RARCH_WARN("[SDL audio]: Either there's no microphone, or it couldn't be found.\n"); + /* Speakers are more common than microphones, so the absence of a microphone + * will not be an error. */ + } + else + { + RARCH_LOG("[SDL audio]: Requested %u ms latency for input device, got %d ms\n", + latency, (int)(out.samples * 4 * 1000 / (out.freq))); + + /* Create a buffer twice as big as needed and prefill the buffer. */ + bufsize = out.samples * 4 * sizeof(int16_t); + tmp = calloc(1, bufsize); + sdl->microphone_buffer = fifo_new(bufsize); + + if (tmp) + { + fifo_write(sdl->microphone_buffer, tmp, bufsize); + free(tmp); + } + + SDL_PauseAudioDevice(sdl->microphone_device, false); + } + return sdl; error: @@ -235,9 +274,26 @@ static void sdl_audio_free(void *data) if (sdl) { - SDL_CloseAudioDevice(sdl->speaker_device); + if (sdl->speaker_device > 0) + { + SDL_CloseAudioDevice(sdl->speaker_device); + } + + if (sdl->speaker_buffer) + { + fifo_free(sdl->speaker_buffer); + } + + if (sdl->microphone_device > 0) + { /* If the microphone was originally initialized successfully... */ + SDL_CloseAudioDevice(sdl->microphone_device); + } + + if (sdl->microphone_buffer) + { + fifo_free(sdl->microphone_buffer); + } - fifo_free(sdl->speaker_buffer); #ifdef HAVE_THREADS slock_free(sdl->lock); scond_free(sdl->cond); From 1703b4c87fd7ec04865ed6c68a790381bfcbc409 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 14:24:07 -0500 Subject: [PATCH 007/309] Start and stop the SDL microphone alongside the speakers --- audio/drivers/sdl_audio.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index cd2fb79d7809..4fab3134bd65 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -241,6 +241,12 @@ static bool sdl_audio_stop(void *data) sdl_audio_t *sdl = (sdl_audio_t*)data; sdl->is_paused = true; SDL_PauseAudioDevice(sdl->speaker_device, true); + + if (sdl->microphone_device) + { + SDL_PauseAudioDevice(sdl->microphone_device, true); + } + return true; } @@ -258,6 +264,12 @@ static bool sdl_audio_start(void *data, bool is_shutdown) sdl->is_paused = false; SDL_PauseAudioDevice(sdl->speaker_device, false); + + if (sdl->microphone_device) + { + SDL_PauseAudioDevice(sdl->microphone_device, false); + } + return true; } From 692663350216a2ee251c5160aea65f0a58119b12 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 14:25:03 -0500 Subject: [PATCH 008/309] Implement a stub sdl_audio_read_avail --- audio/drivers/sdl_audio.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 4fab3134bd65..f6c9ef1034d7 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -327,6 +327,13 @@ static size_t sdl_audio_write_avail(void *data) return 0; } +static size_t sdl_audio_read_avail(void *data) +{ + /* stub */ + (void)data; + return 0; +} + audio_driver_t audio_sdl = { sdl_audio_init, sdl_audio_write, @@ -345,6 +352,6 @@ audio_driver_t audio_sdl = { NULL, NULL, sdl_audio_write_avail, - NULL, + sdl_audio_read_avail, NULL }; From 0405a4fef65bd13e917bc82c76bbeb240aa0c915 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 14:25:26 -0500 Subject: [PATCH 009/309] Implement a stub sdl_audio_read --- audio/drivers/sdl_audio.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index f6c9ef1034d7..a4ab84415185 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -236,6 +236,14 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) return ret; } +static ssize_t sdl_audio_read(void *data, const void *buf, size_t size) +{ + ssize_t ret = 0; + sdl_audio_t *sdl = (sdl_audio_t*)data; + + return -1; +} + static bool sdl_audio_stop(void *data) { sdl_audio_t *sdl = (sdl_audio_t*)data; @@ -337,7 +345,7 @@ static size_t sdl_audio_read_avail(void *data) audio_driver_t audio_sdl = { sdl_audio_init, sdl_audio_write, - NULL, + sdl_audio_read, sdl_audio_stop, sdl_audio_start, sdl_audio_alive, From 590a33db115e8a9144f49bbb2dcc933ad754d6d4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 14 Dec 2022 14:54:10 -0500 Subject: [PATCH 010/309] First draft of sdl_audio_read and sdl_audio_record_cb --- audio/audio_driver.h | 2 +- audio/drivers/sdl_audio.c | 55 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index c7e67703eeea..1330f3186189 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -119,7 +119,7 @@ typedef struct audio_driver * * Read samples from the input driver, e.g. for microphones. */ - ssize_t (*read)(void *data, const void *buf, size_t size); + ssize_t (*read)(void *data, void *buf, size_t size); /* Temporarily pauses the audio driver. */ bool (*stop)(void *data); diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index a4ab84415185..b652cb9273e9 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -62,7 +62,14 @@ static void sdl_audio_playback_cb(void *data, Uint8 *stream, int len) static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) { - /* TODO: Implement */ + sdl_audio_t *sdl = (sdl_audio_t*)data; + size_t avail = FIFO_WRITE_AVAIL(sdl->microphone_buffer); + size_t read_size = len > (int)avail ? avail : len; + + fifo_write(sdl->microphone_buffer, stream, read_size); +#ifdef HAVE_THREADS + scond_signal(sdl->cond); +#endif } static INLINE int find_num_frames(int rate, int latency) @@ -236,12 +243,54 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) return ret; } -static ssize_t sdl_audio_read(void *data, const void *buf, size_t size) +static ssize_t sdl_audio_read(void *data, void *buf, size_t size) { ssize_t ret = 0; sdl_audio_t *sdl = (sdl_audio_t*)data; - return -1; + if (sdl->nonblock) + { + size_t avail, read_amt; + + SDL_LockAudioDevice(sdl->microphone_device); + avail = FIFO_READ_AVAIL(sdl->microphone_buffer); + read_amt = avail > size ? size : avail; + fifo_read(sdl->microphone_buffer, buf, read_amt); + SDL_UnlockAudioDevice(sdl->microphone_device); + ret = read_amt; + } + else + { + size_t read = 0; + + while (read < size) + { + size_t avail; + + SDL_LockAudioDevice(sdl->microphone_device); + avail = FIFO_READ_AVAIL(sdl->microphone_buffer); + + if (avail == 0) + { + SDL_UnlockAudioDevice(sdl->microphone_device); +#ifdef HAVE_THREADS + slock_lock(sdl->lock); + scond_wait(sdl->cond, sdl->lock); + slock_unlock(sdl->lock); +#endif + } + else + { + size_t read_amt = size - read > avail ? avail : size - read; + fifo_read(sdl->microphone_buffer, buf + read, read_amt); + SDL_UnlockAudioDevice(sdl->microphone_device); + read += read_amt; + } + } + ret = read; + } + + return ret; } static bool sdl_audio_stop(void *data) From e62d41df0e965c46d280022fefbfc21b44c517e7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 15 Dec 2022 08:20:28 -0500 Subject: [PATCH 011/309] Fix compiler errors on platforms without SDL 2 - Mic support for the SDL driver requires SDL >= 2.0 - Some legacy platforms are stuck on the 1.x series --- audio/drivers/sdl_audio.c | 59 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index b652cb9273e9..988657e549b6 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -31,6 +31,37 @@ #include "../audio_driver.h" #include "../../verbosity.h" +/** + * We need the SDL_{operation}AudioDevice functions for microphone support, + * but those were introduced in SDL 2.0.0 + * (according to their docs). + * Some legacy build platforms are stuck on 1.x.x, + * so we have to accommodate them. + * That comes in the form of stub implementations of the missing functions + * that delegate to the non-Device versions. + * + * Only three platforms (as of this writing) are stuck on SDL 1.x.x, + * so it's not a big deal to exclude mic support from them. + **/ +#if HAVE_SDL2 +#define SDL_DRIVER_MIC_SUPPORT 1 +#define SDL_DRIVER_DEVICE_FUNCTIONS 1 +#else +typedef Uint32 SDL_AudioDeviceID; + +/** Compatibility stub that defers to SDL_PauseAudio. */ +#define SDL_PauseAudioDevice(dev, pause_on) SDL_PauseAudio(pause_on) + +/** Compatibility stub that defers to SDL_LockAudio. */ +#define SDL_LockAudioDevice(dev) SDL_LockAudio() + +/** Compatibility stub that defers to SDL_UnlockAudio. */ +#define SDL_UnlockAudioDevice(dev) SDL_UnlockAudio() + +/** Compatibility stub that defers to SDL_CloseAudio. */ +#define SDL_CloseAudioDevice(dev) SDL_CloseAudio() +#endif + typedef struct sdl_audio { #ifdef HAVE_THREADS @@ -38,11 +69,14 @@ typedef struct sdl_audio scond_t *cond; #endif fifo_buffer_t *speaker_buffer; - fifo_buffer_t *microphone_buffer; bool nonblock; bool is_paused; SDL_AudioDeviceID speaker_device; + +#ifdef SDL_DRIVER_MIC_SUPPORT + fifo_buffer_t *microphone_buffer; SDL_AudioDeviceID microphone_device; +#endif } sdl_audio_t; static void sdl_audio_playback_cb(void *data, Uint8 *stream, int len) @@ -126,8 +160,17 @@ static void *sdl_audio_init(const char *device, spec.callback = sdl_audio_playback_cb; spec.userdata = sdl; + /* No compatibility stub for SDL_OpenAudioDevice because its return value + * is different from that of SDL_OpenAudio. */ +#if SDL_DRIVER_DEVICE_FUNCTIONS sdl->speaker_device = SDL_OpenAudioDevice(NULL, false, &spec, &out, 0); + if (sdl->speaker_device == 0) +#else + sdl->speaker_device = SDL_OpenAudio(&spec, &out); + + if (sdl->speaker_device < 0) +#endif { RARCH_ERR("[SDL audio]: Failed to open SDL audio output device: %s\n", SDL_GetError()); goto error; @@ -156,6 +199,7 @@ static void *sdl_audio_init(const char *device, SDL_PauseAudioDevice(sdl->speaker_device, false); +#if SDL_DRIVER_MIC_SUPPORT /* Now let's init the microphone */ spec.callback = sdl_audio_record_cb; /* Microphone should have the same params as speaker */ @@ -185,6 +229,7 @@ static void *sdl_audio_init(const char *device, SDL_PauseAudioDevice(sdl->microphone_device, false); } +#endif return sdl; @@ -243,6 +288,7 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) return ret; } +#if SDL_DRIVER_MIC_SUPPORT static ssize_t sdl_audio_read(void *data, void *buf, size_t size) { ssize_t ret = 0; @@ -292,6 +338,7 @@ static ssize_t sdl_audio_read(void *data, void *buf, size_t size) return ret; } +#endif static bool sdl_audio_stop(void *data) { @@ -299,10 +346,12 @@ static bool sdl_audio_stop(void *data) sdl->is_paused = true; SDL_PauseAudioDevice(sdl->speaker_device, true); +#if SDL_DRIVER_MIC_SUPPORT if (sdl->microphone_device) { SDL_PauseAudioDevice(sdl->microphone_device, true); } +#endif return true; } @@ -322,10 +371,12 @@ static bool sdl_audio_start(void *data, bool is_shutdown) SDL_PauseAudioDevice(sdl->speaker_device, false); +#if SDL_DRIVER_MIC_SUPPORT if (sdl->microphone_device) { SDL_PauseAudioDevice(sdl->microphone_device, false); } +#endif return true; } @@ -353,6 +404,7 @@ static void sdl_audio_free(void *data) fifo_free(sdl->speaker_buffer); } +#if SDL_DRIVER_MIC_SUPPORT if (sdl->microphone_device > 0) { /* If the microphone was originally initialized successfully... */ SDL_CloseAudioDevice(sdl->microphone_device); @@ -362,6 +414,7 @@ static void sdl_audio_free(void *data) { fifo_free(sdl->microphone_buffer); } +#endif #ifdef HAVE_THREADS slock_free(sdl->lock); @@ -394,7 +447,11 @@ static size_t sdl_audio_read_avail(void *data) audio_driver_t audio_sdl = { sdl_audio_init, sdl_audio_write, +#if SDL_DRIVER_MIC_SUPPORT sdl_audio_read, +#else + NULL, /* Microphone support for this driver request SDL 2 */ +#endif sdl_audio_stop, sdl_audio_start, sdl_audio_alive, From 3d28fb42b7f85cb15797dee3a1142f96baa54a07 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 15 Dec 2022 08:21:35 -0500 Subject: [PATCH 012/309] Implement audio_thread_read --- audio/audio_thread_wrapper.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/audio/audio_thread_wrapper.c b/audio/audio_thread_wrapper.c index 2b7de2513208..e715bba4b41f 100644 --- a/audio/audio_thread_wrapper.c +++ b/audio/audio_thread_wrapper.c @@ -250,10 +250,31 @@ static ssize_t audio_thread_write(void *data, const void *buf, size_t size) return ret; } +static ssize_t audio_thread_read(void *data, void *buf, size_t size) +{ + ssize_t ret; + audio_thread_t *thr = (audio_thread_t*)data; + + if (!thr) + return 0; + + ret = thr->driver->read(thr->driver_data, buf, size); + + if (ret < 0) + { + slock_lock(thr->lock); + thr->alive = false; + scond_signal(thr->cond); + slock_unlock(thr->lock); + } + + return ret; +} + static const audio_driver_t audio_thread = { NULL, audio_thread_write, - NULL, + audio_thread_read, audio_thread_stop, audio_thread_start, audio_thread_alive, From 432afd8679ec894e743e981905ea163cfbe65515 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 15 Dec 2022 08:49:35 -0500 Subject: [PATCH 013/309] Wrap sdl_audio_record_cb in an #ifdef - Whoops, missed a spot --- audio/drivers/sdl_audio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 988657e549b6..416f416f306c 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -94,6 +94,7 @@ static void sdl_audio_playback_cb(void *data, Uint8 *stream, int len) memset(stream + write_size, 0, len - write_size); } +#ifdef SDL_DRIVER_MIC_SUPPORT static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) { sdl_audio_t *sdl = (sdl_audio_t*)data; @@ -105,6 +106,7 @@ static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) scond_signal(sdl->cond); #endif } +#endif static INLINE int find_num_frames(int rate, int latency) { From 1108da140cd3ec48e5714914bc82ae923b6808ec Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 15 Dec 2022 09:41:19 -0500 Subject: [PATCH 014/309] Set the microphone to provide mono input --- audio/drivers/sdl_audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 416f416f306c..9d30e3fe4b78 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -203,7 +203,8 @@ static void *sdl_audio_init(const char *device, #if SDL_DRIVER_MIC_SUPPORT /* Now let's init the microphone */ - spec.callback = sdl_audio_record_cb; /* Microphone should have the same params as speaker */ + spec.callback = sdl_audio_record_cb; + spec.channels = 1; /* Microphones only usually provide input in mono */ sdl->microphone_device = SDL_OpenAudioDevice(NULL, true, &spec, &out, 0); if (sdl->microphone_device == 0) From 7a22ea6b48a36031fcb5ef8dfb4392ebdd8f9586 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 15 Dec 2022 10:38:56 -0500 Subject: [PATCH 015/309] Add an interface and callback definition for microphones --- libretro-common/include/libretro.h | 90 ++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 4bd3f2f0c7f2..89c733b90313 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1767,6 +1767,16 @@ enum retro_mod * (see enum retro_savestate_context) */ +#define RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE (73 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_microphone_interface * -- + * Returns an interface that can be used to receive audio from + * one or more microphones, depending on what's supported by the + * audio driver. + * + * Will return NULL if the current audio driver or libretro implementation + * doesn't support microphones. + */ + /* VFS functionality */ /* File paths: @@ -3782,6 +3792,86 @@ struct retro_throttle_state float rate; }; +/** + * Enables or disables the microphone at the given index. + * Microphones are disabled by default, + * and must be explicitly enabled. + * Disabled microphones will not process incoming audio samples, + * and will therefore have minimal impact on overall performance. + * You may enable microphones throughout the lifetime of a core, + * or only in instances where they're needed. + * + * A frontend might not support microphones, + * or it might only support one. + * Your core should be able to operate without microphone input; + * we suggest substituting silence in such a case. + * + * @param index The index of the microphone to set the state of. + * Most likely will be 0. + * @param state @c true if the microphone should receive audio input, + * @c false if it should be idle. + * @returns @c true if the microphone's state was successfully set, + * @c false if @c index does not indicate a valid microphone + * or if there was an error. + */ +typedef bool (RETRO_CALLCONV *retro_set_microphone_state_t)(unsigned index, bool state); + +/** + * Queries the state of a microphone at the given index. + * + * @param index The number of the microphone to query. + * Most likely will be 0, + * unless a platform uses multiple microphones for input + * (e.g. per controller). + * @return true if the microphone given by index is active, + * false if not or if index does not indicate a valid microphone. + */ +typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(unsigned index); + +/** + * @return The number of microphones that are currently available. + * 0 indicates that no microphones are available, + * or that the frontend doesn't support them. + */ +typedef unsigned (RETRO_CALLCONV *retro_num_available_microphones_t)(void); + +/** + * Retrieves the input processed by the microphone since the previous frame. + * + * @param index The number of the microphone to query + * @param data The buffer that will be used to store the microphone's data. + * Microphone input is in mono (i.e. one number per sample). + * @param data_length The size of the data buffer, in samples. + * @return The number of samples that were collected this frame. + * Will return 0 if the microphone is invalid or disabled. + */ +typedef size_t (RETRO_CALLCONV *retro_get_microphone_input_t)(unsigned index, int16_t* data, size_t data_length); + +/** + * An interface for querying the microphone and accessing data read from it. + * All fields in this interface are populated by the frontend + * by way of the the RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE callback. + * All fields will be non-NULL, + * even if the frontend (or its audio driver) doesn't support microphones. + */ +struct retro_microphone_interface +{ + /** + * The number of microphones that the frontend supports + * with its current audio driver. + * 0 indicates that the frontend does not support microphones. + * A return value of INT_MAX indicates that the frontend + * supports as many microphones as the system permits. + * I don't know why you'd want arbitrarily many microphones, + * but I guess this is how you'd represent that. + */ + unsigned max_supported_microphones; + retro_num_available_microphones_t num_available_microphones; + retro_set_microphone_state_t set_microphone_state; + retro_get_microphone_state_t get_microphone_state; + retro_get_microphone_input_t get_microphone_input; +}; + /* Callbacks */ /* Environment callback. Gives implementations a way of performing From bea9f1c57ce272cf403eeec732bd6a2809edb684 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 15 Dec 2022 11:15:38 -0500 Subject: [PATCH 016/309] Fix a comment typo --- audio/drivers/sdl_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 9d30e3fe4b78..37593c811bdd 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -453,7 +453,7 @@ audio_driver_t audio_sdl = { #if SDL_DRIVER_MIC_SUPPORT sdl_audio_read, #else - NULL, /* Microphone support for this driver request SDL 2 */ + NULL, /* Microphone support for this driver requires SDL 2 */ #endif sdl_audio_stop, sdl_audio_start, From ce745d428eeb0a375e96edcad9ed44cc99fbe77d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 16 Dec 2022 12:11:07 -0500 Subject: [PATCH 017/309] Add config options for the microphone and its input rate - audio_enable_microphone and audio_in_rate - Also add these options to the UI --- config.def.h | 4 ++++ configuration.c | 2 ++ configuration.h | 2 ++ intl/msg_hash_lbl.h | 8 ++++++++ intl/msg_hash_us.c | 8 ++++++++ intl/msg_hash_us.h | 8 ++++++++ menu/cbs/menu_cbs_sublabel.c | 8 ++++++++ menu/menu_displaylist.c | 8 ++++++++ menu/menu_setting.c | 32 ++++++++++++++++++++++++++++++++ msg_hash.h | 2 ++ retroarch.cfg | 6 ++++++ ui/drivers/qt/qt_options.cpp | 2 ++ 12 files changed, 90 insertions(+) diff --git a/config.def.h b/config.def.h index 764d8ab21c41..07271d4d0a03 100644 --- a/config.def.h +++ b/config.def.h @@ -967,6 +967,7 @@ /* Will enable audio or not. */ #define DEFAULT_AUDIO_ENABLE true +#define DEFAULT_AUDIO_ENABLE_MICROPHONE true /* Enable menu audio sounds. */ #define DEFAULT_AUDIO_ENABLE_MENU false @@ -1052,10 +1053,13 @@ /* Output samplerate. */ #if defined(GEKKO) || defined(MIYOO) #define DEFAULT_OUTPUT_RATE 32000 +#define DEFAULT_INPUT_RATE 32000 #elif defined(_3DS) || defined(RETROFW) #define DEFAULT_OUTPUT_RATE 32730 +#define DEFAULT_INPUT_RATE 32730 #else #define DEFAULT_OUTPUT_RATE 48000 +#define DEFAULT_INPUT_RATE 48000 #endif /* Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults. */ diff --git a/configuration.c b/configuration.c index d9ab8370c4b5..7fabdf530be7 100644 --- a/configuration.c +++ b/configuration.c @@ -1726,6 +1726,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("keyboard_gamepad_enable", &settings->bools.input_keyboard_gamepad_enable, true, true, false); SETTING_BOOL("core_set_supports_no_game_enable", &settings->bools.set_supports_no_game_enable, true, true, false); SETTING_BOOL("audio_enable", &settings->bools.audio_enable, true, DEFAULT_AUDIO_ENABLE, false); + SETTING_BOOL("audio_enable_microphone", &settings->bools.audio_enable_microphone, true, DEFAULT_AUDIO_ENABLE_MICROPHONE, false); SETTING_BOOL("menu_enable_widgets", &settings->bools.menu_enable_widgets, true, DEFAULT_MENU_ENABLE_WIDGETS, false); SETTING_BOOL("menu_show_load_content_animation", &settings->bools.menu_show_load_content_animation, true, DEFAULT_MENU_SHOW_LOAD_CONTENT_ANIMATION, false); SETTING_BOOL("notification_show_autoconfig", &settings->bools.notification_show_autoconfig, true, DEFAULT_NOTIFICATION_SHOW_AUTOCONFIG, false); @@ -2281,6 +2282,7 @@ static struct config_uint_setting *populate_settings_uint( #endif #endif SETTING_UINT("audio_out_rate", &settings->uints.audio_output_sample_rate, true, DEFAULT_OUTPUT_RATE, false); + SETTING_UINT("audio_in_rate", &settings->uints.audio_input_sample_rate, true, DEFAULT_INPUT_RATE, false); SETTING_UINT("custom_viewport_width", &settings->video_viewport_custom.width, false, 0 /* TODO */, false); SETTING_UINT("crt_switch_resolution_super", &settings->uints.crt_switch_resolution_super, true, DEFAULT_CRT_SWITCH_RESOLUTION_SUPER, false); SETTING_UINT("custom_viewport_height", &settings->video_viewport_custom.height, false, 0 /* TODO */, false); diff --git a/configuration.h b/configuration.h index e8fe1ccf9daa..5c95ca134111 100644 --- a/configuration.h +++ b/configuration.h @@ -156,6 +156,7 @@ typedef struct settings unsigned led_map[MAX_LEDS]; unsigned audio_output_sample_rate; + unsigned audio_input_sample_rate; unsigned audio_block_frames; unsigned audio_latency; @@ -586,6 +587,7 @@ typedef struct settings /* Audio */ bool audio_enable; + bool audio_enable_microphone; bool audio_enable_menu; bool audio_enable_menu_ok; bool audio_enable_menu_cancel; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index cfebac44575c..53353e651fa7 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -126,6 +126,10 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_ENABLE, "audio_enable" ) +MSG_HASH( + MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, + "audio_enable_microphone" + ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_FILTER_DIR, "audio_filter_dir" @@ -154,6 +158,10 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE, "audio_output_rate" ) +MSG_HASH( + MENU_ENUM_LABEL_AUDIO_INPUT_RATE, + "audio_input_rate" +) MSG_HASH( MENU_ENUM_LABEL_AUDIO_RATE_CONTROL_DELTA, "audio_rate_control_delta" diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 5d5be2436bcd..81d22d32ec03 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -597,6 +597,10 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Enable audio output."); break; + case MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE: + snprintf(s, len, + "Enable audio input."); + break; case MENU_ENUM_LABEL_AUDIO_SYNC: snprintf(s, len, "Synchronize audio (recommended)."); @@ -715,6 +719,10 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Audio output samplerate."); break; + case MENU_ENUM_LABEL_AUDIO_INPUT_RATE: + snprintf(s, len, + "Audio input samplerate."); + break; case MENU_ENUM_LABEL_VIDEO_SHARED_CONTEXT: snprintf(s, len, "Set to true if hardware-rendered cores \n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 8c8a3db9502d..be7941a63576 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -2260,6 +2260,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_ENABLE, "Enable audio output." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, + "Microphone" +) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MICROPHONE, + "Enable audio input." +) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUDIO_DEVICE, "Device" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index b890c62f421a..42f33e46c9c5 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -515,6 +515,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_always_reload_core_on_run_content, M DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_refresh_rate, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_refresh_rate_polled, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable, MENU_ENUM_SUBLABEL_AUDIO_ENABLE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable_microphone, MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MICROPHONE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable_menu, MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MENU) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_sounds, MENU_ENUM_SUBLABEL_MENU_SOUNDS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_max_timing_skew, MENU_ENUM_SUBLABEL_AUDIO_MAX_TIMING_SKEW) @@ -738,6 +739,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_wallpaper, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_dynamic_wallpaper, MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_device, MENU_ENUM_SUBLABEL_AUDIO_DEVICE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_output_rate, MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_rate, MENU_ENUM_SUBLABEL_AUDIO_INPUT_RATE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_dsp_plugin, MENU_ENUM_SUBLABEL_AUDIO_DSP_PLUGIN) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_dsp_plugin_remove, MENU_ENUM_SUBLABEL_AUDIO_DSP_PLUGIN_REMOVE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_wasapi_exclusive_mode, MENU_ENUM_SUBLABEL_AUDIO_WASAPI_EXCLUSIVE_MODE) @@ -3457,6 +3459,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_output_rate); break; + case MENU_ENUM_LABEL_AUDIO_INPUT_RATE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_rate); + break; case MENU_ENUM_LABEL_AUDIO_DEVICE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_device); break; @@ -4041,6 +4046,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable); break; + case MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable_microphone); + break; case MENU_ENUM_LABEL_AUDIO_ENABLE_MENU: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable_menu); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index f65794b0d71c..99739a292688 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6505,12 +6505,20 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE, PARSE_ONLY_UINT, false) == 0) count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_INPUT_RATE, + PARSE_ONLY_UINT, false) == 0) + count++; break; case DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST: if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_AUDIO_ENABLE, PARSE_ONLY_BOOL, false) == 0) count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, + PARSE_ONLY_BOOL, false) == 0) + count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_AUDIO_DRIVER, PARSE_ONLY_STRING_OPTIONS, false) == 0) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index b1b1bdf125d4..f944d38903bf 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -8184,6 +8184,7 @@ static void general_write_handler(rarch_setting_t *setting) break; case MENU_ENUM_LABEL_AUDIO_LATENCY: case MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE: + case MENU_ENUM_LABEL_AUDIO_INPUT_RATE: case MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE: case MENU_ENUM_LABEL_AUDIO_WASAPI_FLOAT_FORMAT: case MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH: @@ -13094,6 +13095,22 @@ static bool setting_append_list( SD_FLAG_NONE ); + CONFIG_BOOL( + list, list_info, + &settings->bools.audio_enable_microphone, + MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, + MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, + DEFAULT_AUDIO_ENABLE_MICROPHONE, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + CONFIG_BOOL( list, list_info, audio_get_bool_ptr(AUDIO_ACTION_MUTE_ENABLE), @@ -13345,6 +13362,21 @@ static bool setting_append_list( menu_settings_list_current_add_range(list, list_info, 1000, 192000, 100.0, true, true); SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); + CONFIG_UINT( + list, list_info, + &settings->uints.audio_input_sample_rate, + MENU_ENUM_LABEL_AUDIO_INPUT_RATE, + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_RATE, + DEFAULT_INPUT_RATE, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint_special; + menu_settings_list_current_add_range(list, list_info, 1000, 192000, 100.0, true, true); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); + CONFIG_PATH( list, list_info, settings->paths.path_audio_dsp_plugin, diff --git a/msg_hash.h b/msg_hash.h index 1768da63fc13..8af8761100bb 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1879,9 +1879,11 @@ enum msg_hash_enums /* Audio */ MENU_LABEL(AUDIO_ENABLE), + MENU_LABEL(AUDIO_ENABLE_MICROPHONE), MENU_LABEL(AUDIO_ENABLE_MENU), MENU_LABEL(AUDIO_MAX_TIMING_SKEW), MENU_LABEL(AUDIO_OUTPUT_RATE), + MENU_LABEL(AUDIO_INPUT_RATE), MENU_LABEL(AUDIO_DEVICE), MENU_LABEL(AUDIO_BLOCK_FRAMES), MENU_LABEL(AUDIO_DSP_PLUGIN), diff --git a/retroarch.cfg b/retroarch.cfg index ce951b282eb1..cb25801a7d7d 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -295,6 +295,9 @@ # Enable audio. # audio_enable = true +# Enable the microphone. +# audio_enable_microphone = true + # Enable menu audio sounds. # audio_enable_menu = false # audio_enable_menu_ok = false @@ -311,6 +314,9 @@ # Audio output samplerate. # audio_out_rate = 48000 +# Audio input samplerate +# audio_in_rate = 48000 + # Override the default audio device the audio_driver uses. This is driver dependant. E.g. ALSA wants a PCM device, OSS wants a path (e.g. /dev/dsp), Jack wants portnames (e.g. system:playback1,system:playback_2), and so on ... # audio_device = diff --git a/ui/drivers/qt/qt_options.cpp b/ui/drivers/qt/qt_options.cpp index 9f84e66722e7..17bb6c46a17e 100644 --- a/ui/drivers/qt/qt_options.cpp +++ b/ui/drivers/qt/qt_options.cpp @@ -114,6 +114,7 @@ QWidget *AudioPage::widget() QGridLayout *volumeLayout = new QGridLayout(); outputGroup->add(MENU_ENUM_LABEL_AUDIO_ENABLE); + outputGroup->add(MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE); outputGroup->add(MENU_ENUM_LABEL_AUDIO_DRIVER); outputGroup->add(MENU_ENUM_LABEL_AUDIO_DEVICE); outputGroup->add(MENU_ENUM_LABEL_AUDIO_LATENCY); @@ -121,6 +122,7 @@ QWidget *AudioPage::widget() resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_RESAMPLER_DRIVER); resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_RESAMPLER_QUALITY); resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE); + resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_INPUT_RATE); syncGroup->add(MENU_ENUM_LABEL_AUDIO_SYNC); syncGroup->add(MENU_ENUM_LABEL_AUDIO_MAX_TIMING_SKEW); From c37d73d46cd5b29b90f61eff902e0ec73ad66696 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 11:40:20 -0500 Subject: [PATCH 018/309] Implement the audio driver API for microphones for SDL - Separates the microphone initialization from that of the rest of the audio driver - Not the public-facing API yet - I still need to hook up the microphone to the rest of the audio driver - There are still some parameters I need to document --- audio/audio_driver.c | 87 +++++++++ audio/audio_driver.h | 147 +++++++++++++++ audio/drivers/sdl_audio.c | 370 ++++++++++++++++++++++++++------------ 3 files changed, 492 insertions(+), 112 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 4bfbe13b63b9..bc81f51a3623 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1653,6 +1653,93 @@ bool audio_driver_stop(void) audio_driver_st.context_audio_data); } +bool audio_driver_supports_microphone(const audio_driver_t* driver) +{ + return driver && + driver->init_microphone && + driver->free_microphone && + driver->get_microphone && + driver->set_microphone_state && + driver->get_microphone_state && + driver->read_microphone; +} + +bool audio_driver_set_microphone_state(unsigned index, bool state) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + void *context = audio_st->context_audio_data; + const audio_driver_t *audio_driver = audio_st->current_audio; + void *microphone = NULL; + + if (!context || !audio_driver) + return false; + /* If the audio driver isn't initialized... */ + + if (!audio_driver_supports_microphone(audio_driver)) + return false; + /* If the audio driver doesn't support microphones... */ + + /* Audio driver doesn't need to be alive to set the microphone state */ + microphone = audio_driver->get_microphone(context, index); + + if (!microphone) + return false; + + return audio_driver->set_microphone_state(context, microphone, state); +} + +bool audio_driver_get_microphone_state(unsigned index) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + void *context = audio_st->context_audio_data; + const audio_driver_t *audio_driver = audio_st->current_audio; + void *microphone = NULL; + + if (!context || !audio_driver) + return false; + /* If the audio driver isn't initialized... */ + + if (!audio_driver_supports_microphone(audio_driver)) + return false; + /* If the audio driver doesn't support microphones... */ + + /* Audio driver doesn't need to be alive to get the microphone state */ + microphone = audio_driver->get_microphone(context, index); + + if (!microphone) + return false; + + return audio_driver->get_microphone_state(context, microphone); +} + +size_t audio_driver_get_microphone_input(unsigned index, int16_t* data, size_t data_length) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + void *context = audio_st->context_audio_data; + const audio_driver_t *audio_driver = audio_st->current_audio; + void *microphone = NULL; + ssize_t ret = -1; + + if (!context || !audio_driver) + return -1; + /* If the audio driver isn't initialized... */ + + if (!audio_driver_supports_microphone(audio_driver)) + return -1; + /* If the audio driver doesn't support microphones... */ + + if (!audio_driver_alive()) + return -1; + /* If the audio driver isn't currently running... */ + + microphone = audio_driver->get_microphone(context, index); + + if (microphone && audio_driver->get_microphone_state(context, microphone)) + ret = audio_driver->read_microphone(context, microphone, data, data_length); + + return ret; +} + #ifdef HAVE_REWIND void audio_driver_frame_is_reverse(void) { diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 1330f3186189..63fd368b141f 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -167,6 +167,140 @@ typedef struct audio_driver size_t (*read_avail)(void *data); size_t (*buffer_size)(void *data); + + /* The following microphone functions are all optional. + * For a driver to support microphone functionality, + * it must provide all of these functions. + * + * If your driver doesn't support microphones, + * leave these function pointers as NULL. */ + + /** + * Initializes a microphone using the audio driver. + * + * @param data Handle to the driver context + * that was originally returned by ::init. + * @param device A specific device name (or other options) + * to create the microphone with. + * Each audio driver interprets this differently, + * and some may not honor it. + * @param rate The requested sampling rate of the new microphone. + * @param latency TODO + * @param block_frames TODO + * @param new_rate TODO + * @return An opaque handle to the newly-initialized microphone + * if it was successfully created, + * or \c NULL if there was an error. + * May be \c NULL if no microphone is available, + * or if the maximum number of microphones has been created. + * The returned handle should be provided to the \c microphone_context + * parameter of all other microphone functions. + * + * @note Do not return \c NULL to indicate a lack of driver support; + * instead, don't implement this function. + * @note Additionally, don't check the settings for microphone support; + * the frontend's audio layer will do that for you. + */ + void *(*init_microphone)(void *data, const char *device, unsigned rate, + unsigned latency, unsigned block_frames, unsigned *new_rate); + + /** + * Releases the resources used by a particular microphone + * and stops its activity. + * Will do nothing if either \p data or \p microphone_context is \c NULL. + * + * @param data Opaque handle to the audio driver context + * that was used to create the provided microphone. + * Implementations may use this to help in cleaning up the microphone, + * but the driver context itself must \em not be released. + * @param microphone_context Opaque handle to the microphone that will be freed. + * Implementations should stop any recording activity before freeing resources. + * + * @post \p data will still be valid, + * while \p microphone_context will not. + */ + void (*free_microphone)(void *data, void *microphone_context); + + /** + * TODO gets an active microphone + * + * @param data Opaque handle to the audio driver context + * that was used to create the requested microphone. + * @param id TODO + * @return Opaque handle to the microphone context associated with id, + * or NULL if none exists. + */ + void *(*get_microphone)(const void *data, unsigned id); + + /** + * Queries the active state of a microphone. + * Use this to determine if a microphone is currently recording audio + * (i.e. if the mic is "hot"). + * @param data Opaque handle to the audio driver context + * that was used to create the provided microphone. + * @param microphone_context A handle to the microphone that will be freed. + * @return \c true if the provided microphone is actively recording audio. + * \c false if the provided microphone is idle, + * or if either parameter is \c NULL. + * + * @note Implementations should not modify + * the state of the driver or the microphone + * within this function. + */ + bool (*get_microphone_state)(const void *data, const void *microphone_context); + + /** + * @brief Enables or disables a microphone. + * Enabled microphones should record audio input with + * this driver's \c read_microphone implementation. + * Disabled microphones should not be processed, + * and should not impact overall application performance. + * @param data Opaque handle to the audio driver context + * that was used to create the provided microphone. + * @param microphone_context Opaque handle to the microphone + * whose state should be toggled. + * @param enabled The desired state of the provided microphone. + * Provide \c true to enable it and \c false to disable it. + * @return \c true if the microphone's state was successfully altered, + * or if it didn't need to be altered + * (i.e. when disabling an already-idle microphone). + * \c false if there was an error, + * or if the \c microphone_context was invalid. + */ + bool (*set_microphone_state)(void *data, void *microphone_context, bool enabled); + + /** + * @brief Read samples from the input driver, e.g. for microphones. + * + * Write data from the audio driver into the buffer. + * Microphone input is in mono, so a sample is equivalent to a frame + * for our purposes (I.e. 44.1kHz, 16-bit stereo has 44.1k samples/s). + * + * Each element of the provided array is a single sample. + * If the driver returns true in use_float(), a floating point + * format will be used, with range [-1.0, 1.0]. + * If not, signed 16-bit samples in native byte ordering will be used. + * + * + * This function returns the number of samples successfully read. + * If an error occurs, -1 should be returned. + * Note that non-blocking behavior that cannot read at this time + * should return 0 as returning -1 will terminate the driver. + * + * Unless said otherwise with set_nonblock_state(), all reads + * are blocking, and it should block till it has read all frames. + * + * @param[in] driver_context Opaque handle to the audio driver context + * that was used to create the provided microphone. + * @param[in] microphone_context Opaque handle to the microphone + * whose input will be received. + * @param[out] buf Buffer for received audio data. + * Should be large enough to hold one frame's worth of audio samples. + * @param[in] size Size of audio buffer, in samples (\em not bytes). + * @return The number of bytes that were read into \c buf, + * or -1 if there was an error. + */ + ssize_t (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); } audio_driver_t; enum audio_driver_state_flags @@ -304,6 +438,19 @@ bool audio_driver_start(bool is_shutdown); bool audio_driver_stop(void); +/** + * + * @param driver The audio driver whose microphone support you're querying + * @return true if driver defines the necessary methods TODO + */ +bool audio_driver_supports_microphone(const audio_driver_t* driver); + +bool audio_driver_set_microphone_state(unsigned index, bool state); + +bool audio_driver_get_microphone_state(unsigned index); + +size_t audio_driver_get_microphone_input(unsigned index, int16_t* data, size_t data_length); + #ifdef HAVE_TRANSLATE /* TODO/FIXME - Doesn't currently work. Fix this. */ bool audio_driver_is_ai_service_speech_running(void); diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 37593c811bdd..24ff4adbac3e 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -30,6 +30,7 @@ #include "../audio_driver.h" #include "../../verbosity.h" +#include "retro_assert.h" /** * We need the SDL_{operation}AudioDevice functions for microphone support, @@ -62,6 +63,23 @@ typedef Uint32 SDL_AudioDeviceID; #define SDL_CloseAudioDevice(dev) SDL_CloseAudio() #endif +#ifdef SDL_DRIVER_MIC_SUPPORT +typedef struct sdl_audio_microphone +{ +#ifdef HAVE_THREADS + slock_t *lock; + scond_t *cond; +#endif + + fifo_buffer_t *sample_buffer; + bool nonblock; + bool is_paused; + SDL_AudioDeviceID device_id; +} sdl_audio_microphone_t; + +static void sdl_audio_free_microphone(void *data, void *microphone_context); +#endif + typedef struct sdl_audio { #ifdef HAVE_THREADS @@ -74,8 +92,8 @@ typedef struct sdl_audio SDL_AudioDeviceID speaker_device; #ifdef SDL_DRIVER_MIC_SUPPORT - fifo_buffer_t *microphone_buffer; - SDL_AudioDeviceID microphone_device; + /* Only one microphone is supported right now */ + sdl_audio_microphone_t *microphone; #endif } sdl_audio_t; @@ -98,12 +116,12 @@ static void sdl_audio_playback_cb(void *data, Uint8 *stream, int len) static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) { sdl_audio_t *sdl = (sdl_audio_t*)data; - size_t avail = FIFO_WRITE_AVAIL(sdl->microphone_buffer); - size_t read_size = len > (int)avail ? avail : len; + size_t avail = FIFO_WRITE_AVAIL(sdl->microphone->sample_buffer); + size_t read_size = len > (int)avail ? avail : len; - fifo_write(sdl->microphone_buffer, stream, read_size); + fifo_write(sdl->microphone->sample_buffer, stream, read_size); #ifdef HAVE_THREADS - scond_signal(sdl->cond); + scond_signal(sdl->microphone->cond); #endif } #endif @@ -189,8 +207,8 @@ static void *sdl_audio_init(const char *device, latency, (int)(out.samples * 4 * 1000 / (*new_rate))); /* Create a buffer twice as big as needed and prefill the buffer. */ - bufsize = out.samples * 4 * sizeof(int16_t); - tmp = calloc(1, bufsize); + bufsize = out.samples * 4 * sizeof(int16_t); + tmp = calloc(1, bufsize); sdl->speaker_buffer = fifo_new(bufsize); if (tmp) @@ -201,39 +219,6 @@ static void *sdl_audio_init(const char *device, SDL_PauseAudioDevice(sdl->speaker_device, false); -#if SDL_DRIVER_MIC_SUPPORT - /* Now let's init the microphone */ - spec.callback = sdl_audio_record_cb; - spec.channels = 1; /* Microphones only usually provide input in mono */ - - sdl->microphone_device = SDL_OpenAudioDevice(NULL, true, &spec, &out, 0); - if (sdl->microphone_device == 0) - { - RARCH_WARN("[SDL audio]: Failed to open SDL audio input device: %s\n", SDL_GetError()); - RARCH_WARN("[SDL audio]: Either there's no microphone, or it couldn't be found.\n"); - /* Speakers are more common than microphones, so the absence of a microphone - * will not be an error. */ - } - else - { - RARCH_LOG("[SDL audio]: Requested %u ms latency for input device, got %d ms\n", - latency, (int)(out.samples * 4 * 1000 / (out.freq))); - - /* Create a buffer twice as big as needed and prefill the buffer. */ - bufsize = out.samples * 4 * sizeof(int16_t); - tmp = calloc(1, bufsize); - sdl->microphone_buffer = fifo_new(bufsize); - - if (tmp) - { - fifo_write(sdl->microphone_buffer, tmp, bufsize); - free(tmp); - } - - SDL_PauseAudioDevice(sdl->microphone_device, false); - } -#endif - return sdl; error: @@ -291,58 +276,6 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) return ret; } -#if SDL_DRIVER_MIC_SUPPORT -static ssize_t sdl_audio_read(void *data, void *buf, size_t size) -{ - ssize_t ret = 0; - sdl_audio_t *sdl = (sdl_audio_t*)data; - - if (sdl->nonblock) - { - size_t avail, read_amt; - - SDL_LockAudioDevice(sdl->microphone_device); - avail = FIFO_READ_AVAIL(sdl->microphone_buffer); - read_amt = avail > size ? size : avail; - fifo_read(sdl->microphone_buffer, buf, read_amt); - SDL_UnlockAudioDevice(sdl->microphone_device); - ret = read_amt; - } - else - { - size_t read = 0; - - while (read < size) - { - size_t avail; - - SDL_LockAudioDevice(sdl->microphone_device); - avail = FIFO_READ_AVAIL(sdl->microphone_buffer); - - if (avail == 0) - { - SDL_UnlockAudioDevice(sdl->microphone_device); -#ifdef HAVE_THREADS - slock_lock(sdl->lock); - scond_wait(sdl->cond, sdl->lock); - slock_unlock(sdl->lock); -#endif - } - else - { - size_t read_amt = size - read > avail ? avail : size - read; - fifo_read(sdl->microphone_buffer, buf + read, read_amt); - SDL_UnlockAudioDevice(sdl->microphone_device); - read += read_amt; - } - } - ret = read; - } - - return ret; -} -#endif - static bool sdl_audio_stop(void *data) { sdl_audio_t *sdl = (sdl_audio_t*)data; @@ -350,9 +283,12 @@ static bool sdl_audio_stop(void *data) SDL_PauseAudioDevice(sdl->speaker_device, true); #if SDL_DRIVER_MIC_SUPPORT - if (sdl->microphone_device) + if (sdl->microphone) { - SDL_PauseAudioDevice(sdl->microphone_device, true); + /* Stop the microphone independently of whether it's paused; + * note that upon sdl_audio_start, the microphone might not resume + * if it was paused */ + SDL_PauseAudioDevice(sdl->microphone->device_id, true); } #endif @@ -375,9 +311,11 @@ static bool sdl_audio_start(void *data, bool is_shutdown) SDL_PauseAudioDevice(sdl->speaker_device, false); #if SDL_DRIVER_MIC_SUPPORT - if (sdl->microphone_device) + if (sdl->microphone) { - SDL_PauseAudioDevice(sdl->microphone_device, false); + if (!sdl->microphone->is_paused) + SDL_PauseAudioDevice(sdl->microphone->device_id, false); + /* If the microphone wasn't paused beforehand... */ } #endif @@ -408,15 +346,8 @@ static void sdl_audio_free(void *data) } #if SDL_DRIVER_MIC_SUPPORT - if (sdl->microphone_device > 0) - { /* If the microphone was originally initialized successfully... */ - SDL_CloseAudioDevice(sdl->microphone_device); - } - - if (sdl->microphone_buffer) - { - fifo_free(sdl->microphone_buffer); - } + if (sdl->microphone) + sdl_audio_free_microphone(sdl, sdl->microphone); #endif #ifdef HAVE_THREADS @@ -447,14 +378,214 @@ static size_t sdl_audio_read_avail(void *data) return 0; } +#if SDL_DRIVER_MIC_SUPPORT +static void *sdl_audio_init_microphone(void *data, + const char *device, + unsigned rate, + unsigned latency, + unsigned block_frames, + unsigned *new_rate) +{ + int frames; + size_t bufsize; + sdl_audio_t *sdl = (sdl_audio_t*)data; + sdl_audio_microphone_t *microphone = NULL; + SDL_AudioSpec desired_spec = {0}; + SDL_AudioSpec out; + void *tmp = NULL; + + if (!sdl || !SDL_WasInit(SDL_INIT_AUDIO)) + { /* If the audio driver wasn't initialized yet... */ + RARCH_ERR("[SDL audio]: Attempted to initialize input device before initializing the audio driver\n"); + return NULL; + } + + if (sdl->microphone) + { + RARCH_ERR("[SDL audio]: Attempted to initialize a second microphone, but the driver only supports one right now\n"); + return NULL; + } + + microphone = (sdl_audio_microphone_t *)calloc(1, sizeof(sdl_audio_microphone_t)); + if (!microphone) + return NULL; + + /* We have to buffer up some data ourselves, so we let SDL + * carry approximately half of the latency. + * + * SDL double buffers audio and we do as well. */ + frames = find_num_frames(rate, latency / 4); + + desired_spec.freq = rate; + desired_spec.format = AUDIO_S16SYS; + desired_spec.channels = 1; /* Microphones only usually provide input in mono */ + desired_spec.samples = frames; + desired_spec.userdata = sdl; + desired_spec.callback = sdl_audio_record_cb; + + microphone->device_id = SDL_OpenAudioDevice(NULL, true, &desired_spec, &out, 0); + if (microphone->device_id == 0) + { + RARCH_ERR("[SDL audio]: Failed to open SDL audio input device: %s\n", SDL_GetError()); + goto error; + } + + *new_rate = out.freq; +#ifdef HAVE_THREADS + microphone->lock = slock_new(); + microphone->cond = scond_new(); +#endif + + RARCH_LOG("[SDL audio]: Requested %u ms latency for input device, got %d ms\n", + latency, (int)(out.samples * 4 * 1000 / (*new_rate))); + + /* Create a buffer twice as big as needed and prefill the buffer. */ + bufsize = out.samples * 4 * sizeof(int16_t); + tmp = calloc(1, bufsize); + microphone->sample_buffer = fifo_new(bufsize); + + if (tmp) + { + fifo_write(microphone->sample_buffer, tmp, bufsize); + free(tmp); + } + + sdl->microphone = microphone; + return microphone; + +error: + free(microphone); + return NULL; +} + +static void sdl_audio_free_microphone(void *data, void *microphone_context) +{ + sdl_audio_t *sdl = (sdl_audio_t*)data; + sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t *)microphone_context; + + if (sdl && microphone) + { + retro_assert(sdl->microphone == microphone); + /* Driver only supports one microphone for now; when multi-mic support + * is added, you may still want to assert that the microphone + * was indeed created by the driver */ + + if (microphone->device_id > 0) + { /* If the microphone was originally initialized successfully... */ + SDL_CloseAudioDevice(microphone->device_id); + } + + if (microphone->sample_buffer) + { + fifo_free(microphone->sample_buffer); + } + +#ifdef HAVE_THREADS + slock_free(microphone->lock); + scond_free(microphone->cond); +#endif + + sdl->microphone = NULL; + free(microphone); + } +} + +static void * sdl_audio_get_microphone(const void *data, unsigned id) +{ + const sdl_audio_t *sdl = (const sdl_audio_t*)data; + + if (id == 0 && sdl->microphone) + return sdl->microphone; + /* This driver only supports one microphone right now */ + + return NULL; +} + +static bool sdl_audio_microphone_get_state(const void *data, const void *microphone_context) +{ + const sdl_audio_t *sdl = (const sdl_audio_t*)data; + const sdl_audio_microphone_t *microphone = (const sdl_audio_microphone_t*)microphone_context; + + if (!sdl || !microphone) + return false; + /* Both params must be non-null */ + + return !microphone->is_paused; + /* The mic might be paused due to app requirements, + * or it might be stopped because the entire audio driver is stopped. */ +} + +static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, bool enabled) +{ + sdl_audio_t *sdl = (sdl_audio_t*)data; + sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t*)microphone_context; + + if (!sdl || !microphone) + return false; + + microphone->is_paused = !enabled; + return true; +} + +static ssize_t sdl_audio_read(void *data, void *microphone_context, void *buf, size_t size) +{ + ssize_t ret = 0; + sdl_audio_t *sdl = (sdl_audio_t*)data; + sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t*)microphone_context; + + if (!sdl || !microphone || !buf) + return -1; + + if (sdl->nonblock) + { + size_t avail, read_amt; + + SDL_LockAudioDevice(microphone->device_id); + avail = FIFO_READ_AVAIL(microphone->sample_buffer); + read_amt = avail > size ? size : avail; + fifo_read(microphone->sample_buffer, buf, read_amt); + SDL_UnlockAudioDevice(microphone->device_id); + ret = read_amt; + } + else + { + size_t read = 0; + + while (read < size) + { + size_t avail; + + SDL_LockAudioDevice(microphone->device_id); + avail = FIFO_READ_AVAIL(microphone->sample_buffer); + + if (avail == 0) + { + SDL_UnlockAudioDevice(microphone->device_id); +#ifdef HAVE_THREADS + slock_lock(sdl->lock); + scond_wait(sdl->cond, sdl->lock); + slock_unlock(sdl->lock); +#endif + } + else + { + size_t read_amt = size - read > avail ? avail : size - read; + fifo_read(microphone->sample_buffer, buf + read, read_amt); + SDL_UnlockAudioDevice(microphone->device_id); + read += read_amt; + } + } + ret = read; + } + + return ret; +} + +#endif + audio_driver_t audio_sdl = { sdl_audio_init, sdl_audio_write, -#if SDL_DRIVER_MIC_SUPPORT - sdl_audio_read, -#else - NULL, /* Microphone support for this driver requires SDL 2 */ -#endif sdl_audio_stop, sdl_audio_start, sdl_audio_alive, @@ -470,5 +601,20 @@ audio_driver_t audio_sdl = { NULL, sdl_audio_write_avail, sdl_audio_read_avail, - NULL + NULL, +#if SDL_DRIVER_MIC_SUPPORT + sdl_audio_init_microphone, + sdl_audio_free_microphone, + sdl_audio_get_microphone, + sdl_audio_microphone_get_state, + sdl_audio_microphone_set_state, + sdl_audio_read +#else + NULL, /* Microphone support for this driver requires SDL 2 */ + NULL, + NULL, + NULL, + NULL, + NULL +#endif }; From 2c99fb4a4251f84aaee4f3c662f3c8f0fb13b182 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 11:51:45 -0500 Subject: [PATCH 019/309] Update the documentation for parts of audio_driver.h --- audio/audio_driver.h | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 63fd368b141f..ae829ab3cd08 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -112,19 +112,23 @@ typedef struct audio_driver */ ssize_t (*write)(void *data, const void *buf, size_t size); - /* - * @data : Pointer to audio data handle. - * @buf : Buffer for received audio data. - * @size : Size of audio buffer. + /** + * Temporarily pauses the audio driver, including microphones. + * Microphone "paused" state will not be updated, + * but they will stop recording until start is called. * - * Read samples from the input driver, e.g. for microphones. - */ - ssize_t (*read)(void *data, void *buf, size_t size); - - /* Temporarily pauses the audio driver. */ + * @param data Opaque handle to the audio driver context + * that was returned by \c init. + * @return \c true if the audio driver was successfully paused, + * \c false if there was an error. + **/ bool (*stop)(void *data); - /* Resumes audio driver from the paused state. */ + /** + * Resumes audio driver from the paused state. + * Microphones will resume recording \em if they were already active + * before the driver was stopped. + **/ bool (*start)(void *data, bool is_shutdown); /* Is the audio driver currently running? */ @@ -139,7 +143,7 @@ typedef struct audio_driver * */ void (*set_nonblock_state)(void *data, bool toggle); - /* Stops and frees driver data. */ + /* Stops and frees driver data, including microphones. */ void (*free)(void *data); /* Defines if driver will take standard floating point samples, @@ -332,6 +336,10 @@ typedef struct const retro_resampler_t *resampler; void *resampler_data; + + /** + * The current audio driver. + */ const audio_driver_t *current_audio; void *context_audio_data; float *input_data; From 076af574d5eb0396ff7c8308692965e403e74a00 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 13:53:57 -0500 Subject: [PATCH 020/309] Add AUDIO_FLAG_MIC_ACTIVE flag --- audio/audio_driver.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index ae829ab3cd08..4072cca60679 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -314,7 +314,8 @@ enum audio_driver_state_flags AUDIO_FLAG_SUSPENDED = (1 << 2), AUDIO_FLAG_MIXER_ACTIVE = (1 << 3), AUDIO_FLAG_HARD_DISABLE = (1 << 4), - AUDIO_FLAG_CONTROL = (1 << 5) + AUDIO_FLAG_CONTROL = (1 << 5), + AUDIO_FLAG_MIC_ACTIVE = (1 << 6) }; typedef struct From fa3fc1beb8e3d9253dbde8b00cb1f0aa4fa45f40 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 14:05:18 -0500 Subject: [PATCH 021/309] Allocate memory for the microphone - Memory is allocated in audio_driver_init_internal - If mic support is disabled or not provided by the driver, memory won't be allocated for it - Asserts about mic memory won't fail if the mic is disabled --- audio/audio_driver.c | 54 ++++++++++++++++++++++++++++++++++++-------- audio/audio_driver.h | 10 ++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index bc81f51a3623..350a2cf8a4ba 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -283,6 +283,10 @@ static bool audio_driver_deinit_internal(bool audio_enable) memalign_free(audio_st->output_samples_conv_buf); audio_st->output_samples_conv_buf = NULL; + if (audio_st->input_samples_conv_buf) + memalign_free(audio_st->input_samples_conv_buf); + audio_st->input_samples_conv_buf = NULL; + if (audio_st->input_data) memalign_free(audio_st->input_data); @@ -308,6 +312,10 @@ static bool audio_driver_deinit_internal(bool audio_enable) memalign_free(audio_st->output_samples_buf); audio_st->output_samples_buf = NULL; + if (audio_st->input_samples_buf) + memalign_free(audio_st->input_samples_buf); + audio_st->input_samples_buf = NULL; + #ifdef HAVE_DSP_FILTER audio_driver_dsp_filter_free(); #endif @@ -563,10 +571,12 @@ bool audio_driver_init_internal( bool audio_cb_inited) { unsigned new_rate = 0; - float *samples_buf = NULL; + float *out_samples_buf = NULL; + float *in_samples_buf = NULL; settings_t *settings = (settings_t*)settings_data; size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2; bool audio_enable = settings->bools.audio_enable; + bool audio_enable_microphone = settings->bools.audio_enable_microphone; bool audio_sync = settings->bools.audio_sync; bool audio_rate_control = settings->bools.audio_rate_control; float slowmotion_ratio = settings->floats.slowmotion_ratio; @@ -579,7 +589,10 @@ bool audio_driver_init_internal( #endif /* Accomodate rewind since at some point we might have two full buffers. */ size_t outsamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * AUDIO_MAX_RATIO * slowmotion_ratio; - int16_t *conv_buf = (int16_t*)memalign_alloc(64, outsamples_max * sizeof(int16_t)); + size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; + int16_t *out_conv_buf = (int16_t*)memalign_alloc(64, outsamples_max * sizeof(int16_t)); + int16_t *in_conv_buf = audio_enable_microphone ? + (int16_t*)memalign_alloc(64, insamples_max * sizeof(int16_t)) : NULL; float *audio_buf = (float*)memalign_alloc(64, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float)); bool verbosity_enabled = verbosity_is_enabled(); @@ -587,16 +600,20 @@ bool audio_driver_init_internal( convert_float_to_s16_init_simd(); /* Used for recording even if audio isn't enabled. */ - retro_assert(conv_buf != NULL); + retro_assert(out_conv_buf != NULL); retro_assert(audio_buf != NULL); + if (audio_enable_microphone) + retro_assert(in_conv_buf != NULL); - if (!conv_buf || !audio_buf) + if (!out_conv_buf || !audio_buf || (audio_enable_microphone && !in_conv_buf)) goto error; + /* It's not an error for in_conv_buf to be null if we didn't ask for a mic */ memset(audio_buf, 0, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float)); audio_driver_st.input_data = audio_buf; - audio_driver_st.output_samples_conv_buf = conv_buf; + audio_driver_st.output_samples_conv_buf = out_conv_buf; + audio_driver_st.input_samples_conv_buf = in_conv_buf; audio_driver_st.chunk_block_size = AUDIO_CHUNK_SIZE_BLOCKING; audio_driver_st.chunk_nonblock_size = AUDIO_CHUNK_SIZE_NONBLOCKING; audio_driver_st.chunk_size = audio_driver_st.chunk_block_size; @@ -620,6 +637,10 @@ bool audio_driver_init_internal( return false; } + if (!audio_enable_microphone) + audio_driver_st.flags &= ~AUDIO_FLAG_MIC_ACTIVE; + /* Not an error if the mic is disabled */ + if (!(audio_driver_find_driver(settings, "audio driver", verbosity_enabled))) { @@ -634,6 +655,14 @@ bool audio_driver_init_internal( return false; } + if ((audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) + && !audio_driver_supports_microphone(audio_driver_st.current_audio)) + { + RARCH_WARN("[Audio]: Microphone support is enabled, but the %s driver doesn't support it. Will continue without it.\n", + audio_driver_st.current_audio->ident); + audio_driver_st.flags &= ~AUDIO_FLAG_MIC_ACTIVE; + } + #ifdef HAVE_THREADS if (audio_cb_inited) { @@ -732,14 +761,21 @@ bool audio_driver_init_internal( retro_assert(settings->uints.audio_output_sample_rate < audio_driver_st.input * AUDIO_MAX_RATIO); - samples_buf = (float*)memalign_alloc(64, outsamples_max * sizeof(float)); + out_samples_buf = (float*)memalign_alloc(64, outsamples_max * sizeof(float)); + in_samples_buf = (audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) ? + (float*)memalign_alloc(64, insamples_max * sizeof(float)) : NULL; + + retro_assert(out_samples_buf != NULL); - retro_assert(samples_buf != NULL); + if (audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) + retro_assert(in_samples_buf != NULL); + /* It's not an error for in_samples_buf to be NULL if we don't want the mic */ - if (!samples_buf) + if (!out_samples_buf || ((audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) && !in_samples_buf)) goto error; - audio_driver_st.output_samples_buf = (float*)samples_buf; + audio_driver_st.output_samples_buf = (float*)out_samples_buf; + audio_driver_st.input_samples_buf = (float*)in_samples_buf; audio_driver_st.flags &= ~AUDIO_FLAG_CONTROL; if ( diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 4072cca60679..c60f2f802575 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -327,10 +327,12 @@ typedef struct struct string_list *devices_list; float *output_samples_buf; + float *input_samples_buf; #ifdef HAVE_REWIND int16_t *rewind_buf; #endif int16_t *output_samples_conv_buf; + int16_t *input_samples_conv_buf; #ifdef HAVE_DSP_FILTER retro_dsp_filter_t *dsp; #endif @@ -342,6 +344,14 @@ typedef struct * The current audio driver. */ const audio_driver_t *current_audio; + + /** + * The handle to the created microphone, if any. + * The libretro API is designed to enable multiple microphones, + * but RetroArch and its drivers only supports one at a time for now. + * PRs welcome! + */ + void *context_microphone_data; void *context_audio_data; float *input_data; #ifdef HAVE_AUDIOMIXER From 6c6c419e6ef1ad7beb27041842f5981a130ee8e3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 20:03:22 -0500 Subject: [PATCH 022/309] Remove some microphone callbacks from audio_driver - They weren't necessary --- audio/audio_driver.c | 1 - audio/audio_driver.h | 13 ------------ audio/drivers/alsa.c | 2 -- audio/drivers/alsa_qsa.c | 2 -- audio/drivers/alsathread.c | 2 -- audio/drivers/audioio.c | 2 -- audio/drivers/coreaudio.c | 2 -- audio/drivers/ctr_csnd_audio.c | 2 -- audio/drivers/ctr_dsp_audio.c | 2 -- audio/drivers/ctr_dsp_thread_audio.c | 2 -- audio/drivers/dsound.c | 2 -- audio/drivers/gx_audio.c | 2 -- audio/drivers/jack.c | 2 -- audio/drivers/openal.c | 2 -- audio/drivers/opensl.c | 2 -- audio/drivers/oss.c | 2 -- audio/drivers/ps2_audio.c | 2 -- audio/drivers/ps3_audio.c | 2 -- audio/drivers/psp_audio.c | 2 -- audio/drivers/pulse.c | 2 -- audio/drivers/roar.c | 2 -- audio/drivers/rsound.c | 2 -- audio/drivers/rwebaudio.c | 2 -- audio/drivers/sdl_audio.c | 21 ------------------- audio/drivers/switch_audio.c | 2 -- audio/drivers/switch_libnx_audren_audio.c | 2 -- .../switch_libnx_audren_thread_audio.c | 2 -- audio/drivers/switch_thread_audio.c | 2 -- audio/drivers/tinyalsa.c | 2 -- audio/drivers/wasapi.c | 2 -- audio/drivers/wiiu_audio.c | 2 -- audio/drivers/xaudio.c | 2 -- audio/drivers/xenon360_audio.c | 2 -- 33 files changed, 95 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 350a2cf8a4ba..e654bd0fc1c0 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1694,7 +1694,6 @@ bool audio_driver_supports_microphone(const audio_driver_t* driver) return driver && driver->init_microphone && driver->free_microphone && - driver->get_microphone && driver->set_microphone_state && driver->get_microphone_state && driver->read_microphone; diff --git a/audio/audio_driver.h b/audio/audio_driver.h index c60f2f802575..9c30bdf3f414 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -168,8 +168,6 @@ typedef struct audio_driver /* Optional. */ size_t (*write_avail)(void *data); - size_t (*read_avail)(void *data); - size_t (*buffer_size)(void *data); /* The following microphone functions are all optional. @@ -225,17 +223,6 @@ typedef struct audio_driver */ void (*free_microphone)(void *data, void *microphone_context); - /** - * TODO gets an active microphone - * - * @param data Opaque handle to the audio driver context - * that was used to create the requested microphone. - * @param id TODO - * @return Opaque handle to the microphone context associated with id, - * or NULL if none exists. - */ - void *(*get_microphone)(const void *data, unsigned id); - /** * Queries the active state of a microphone. * Use this to determine if a microphone is currently recording audio diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 85ff0193eda1..8a604324846b 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -417,7 +417,6 @@ static void alsa_device_list_free(void *data, void *array_list_data) audio_driver_t audio_alsa = { alsa_init, alsa_write, - NULL, alsa_stop, alsa_start, alsa_alive, @@ -428,6 +427,5 @@ audio_driver_t audio_alsa = { alsa_device_list_new, alsa_device_list_free, alsa_write_avail, - NULL, alsa_buffer_size, }; diff --git a/audio/drivers/alsa_qsa.c b/audio/drivers/alsa_qsa.c index a72cfa67be04..f534a7a7c69f 100644 --- a/audio/drivers/alsa_qsa.c +++ b/audio/drivers/alsa_qsa.c @@ -368,7 +368,6 @@ static size_t alsa_qsa_buffer_size(void *data) audio_driver_t audio_alsa = { alsa_qsa_init, alsa_qsa_write, - NULL, alsa_qsa_stop, alsa_qsa_start, alsa_qsa_alive, @@ -379,6 +378,5 @@ audio_driver_t audio_alsa = { NULL, NULL, alsa_qsa_write_avail, - NULL, alsa_qsa_buffer_size, }; diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 5f368dc7e048..1e239ef119fa 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -405,7 +405,6 @@ static void alsa_thread_device_list_free(void *data, void *array_list_data) audio_driver_t audio_alsathread = { alsa_thread_init, alsa_thread_write, - NULL, alsa_thread_stop, alsa_thread_start, alsa_thread_alive, @@ -416,6 +415,5 @@ audio_driver_t audio_alsathread = { alsa_thread_device_list_new, alsa_thread_device_list_free, alsa_thread_write_avail, - NULL, alsa_thread_buffer_size, }; diff --git a/audio/drivers/audioio.c b/audio/drivers/audioio.c index 39187c96961c..8176a0d7b4d3 100644 --- a/audio/drivers/audioio.c +++ b/audio/drivers/audioio.c @@ -198,7 +198,6 @@ static bool audioio_use_float(void *data) audio_driver_t audio_audioio = { audioio_init, audioio_write, - NULL, audioio_stop, audioio_start, audioio_alive, @@ -209,6 +208,5 @@ audio_driver_t audio_audioio = { NULL, NULL, audioio_write_avail, - NULL, audioio_buffer_size, }; diff --git a/audio/drivers/coreaudio.c b/audio/drivers/coreaudio.c index c637f0670e2e..4363d44651cd 100644 --- a/audio/drivers/coreaudio.c +++ b/audio/drivers/coreaudio.c @@ -445,7 +445,6 @@ static void coreaudio_device_list_free(void *data, void *array_list_data) audio_driver_t audio_coreaudio = { coreaudio_init, coreaudio_write, - NULL, coreaudio_stop, coreaudio_start, coreaudio_alive, @@ -456,6 +455,5 @@ audio_driver_t audio_coreaudio = { coreaudio_device_list_new, coreaudio_device_list_free, coreaudio_write_avail, - NULL, coreaudio_buffer_size, }; diff --git a/audio/drivers/ctr_csnd_audio.c b/audio/drivers/ctr_csnd_audio.c index 0a155e073fe1..eb7986f90b97 100644 --- a/audio/drivers/ctr_csnd_audio.c +++ b/audio/drivers/ctr_csnd_audio.c @@ -292,7 +292,6 @@ static size_t ctr_csnd_audio_buffer_size(void *data) audio_driver_t audio_ctr_csnd = { ctr_csnd_audio_init, ctr_csnd_audio_write, - NULL, ctr_csnd_audio_stop, ctr_csnd_audio_start, ctr_csnd_audio_alive, @@ -303,6 +302,5 @@ audio_driver_t audio_ctr_csnd = { NULL, NULL, ctr_csnd_audio_write_avail, - NULL, ctr_csnd_audio_buffer_size }; diff --git a/audio/drivers/ctr_dsp_audio.c b/audio/drivers/ctr_dsp_audio.c index 8bdfafd11158..e25f0bede472 100644 --- a/audio/drivers/ctr_dsp_audio.c +++ b/audio/drivers/ctr_dsp_audio.c @@ -206,7 +206,6 @@ static size_t ctr_dsp_audio_buffer_size(void *data) audio_driver_t audio_ctr_dsp = { ctr_dsp_audio_init, ctr_dsp_audio_write, - NULL, ctr_dsp_audio_stop, ctr_dsp_audio_start, ctr_dsp_audio_alive, @@ -217,6 +216,5 @@ audio_driver_t audio_ctr_dsp = { NULL, NULL, ctr_dsp_audio_write_avail, - NULL, ctr_dsp_audio_buffer_size }; diff --git a/audio/drivers/ctr_dsp_thread_audio.c b/audio/drivers/ctr_dsp_thread_audio.c index cf5913f7c8b9..4fbbf5f6fa6b 100644 --- a/audio/drivers/ctr_dsp_thread_audio.c +++ b/audio/drivers/ctr_dsp_thread_audio.c @@ -325,7 +325,6 @@ static size_t ctr_dsp_thread_audio_buffer_size(void *data) audio_driver_t audio_ctr_dsp_thread = { ctr_dsp_thread_audio_init, ctr_dsp_thread_audio_write, - NULL, ctr_dsp_thread_audio_stop, ctr_dsp_thread_audio_start, ctr_dsp_thread_audio_alive, @@ -336,6 +335,5 @@ audio_driver_t audio_ctr_dsp_thread = { NULL, NULL, ctr_dsp_thread_audio_write_avail, - NULL, ctr_dsp_thread_audio_buffer_size }; diff --git a/audio/drivers/dsound.c b/audio/drivers/dsound.c index 32e7a0788870..000540540f39 100644 --- a/audio/drivers/dsound.c +++ b/audio/drivers/dsound.c @@ -602,7 +602,6 @@ static void dsound_device_list_free(void *u, void *slp) audio_driver_t audio_dsound = { dsound_init, dsound_write, - NULL, dsound_stop, dsound_start, dsound_alive, @@ -613,6 +612,5 @@ audio_driver_t audio_dsound = { dsound_list_new, dsound_device_list_free, dsound_write_avail, - NULL, dsound_buffer_size, }; diff --git a/audio/drivers/gx_audio.c b/audio/drivers/gx_audio.c index 940b2f524a16..f594863b9815 100644 --- a/audio/drivers/gx_audio.c +++ b/audio/drivers/gx_audio.c @@ -225,7 +225,6 @@ static bool gx_audio_use_float(void *data) audio_driver_t audio_gx = { gx_audio_init, gx_audio_write, - NULL, gx_audio_stop, gx_audio_start, gx_audio_alive, @@ -236,6 +235,5 @@ audio_driver_t audio_gx = { NULL, NULL, gx_audio_write_avail, - NULL, gx_audio_buffer_size, }; diff --git a/audio/drivers/jack.c b/audio/drivers/jack.c index 3991864f4bae..6d199067e252 100644 --- a/audio/drivers/jack.c +++ b/audio/drivers/jack.c @@ -371,7 +371,6 @@ static size_t ja_buffer_size(void *data) audio_driver_t audio_jack = { ja_init, ja_write, - NULL, ja_stop, ja_start, ja_alive, @@ -382,6 +381,5 @@ audio_driver_t audio_jack = { NULL, NULL, ja_write_avail, - NULL, ja_buffer_size, }; diff --git a/audio/drivers/openal.c b/audio/drivers/openal.c index bb5970e4738f..606274847e18 100644 --- a/audio/drivers/openal.c +++ b/audio/drivers/openal.c @@ -263,7 +263,6 @@ static bool al_use_float(void *data) audio_driver_t audio_openal = { al_init, al_write, - NULL, al_stop, al_start, al_alive, @@ -274,6 +273,5 @@ audio_driver_t audio_openal = { NULL, NULL, al_write_avail, - NULL, al_buffer_size, }; diff --git a/audio/drivers/opensl.c b/audio/drivers/opensl.c index f7562eacd77c..70f584653821 100644 --- a/audio/drivers/opensl.c +++ b/audio/drivers/opensl.c @@ -306,7 +306,6 @@ static bool sl_use_float(void *data) audio_driver_t audio_opensl = { sl_init, sl_write, - NULL, sl_stop, sl_start, sl_alive, @@ -317,6 +316,5 @@ audio_driver_t audio_opensl = { NULL, NULL, sl_write_avail, - NULL, sl_buffer_size, }; diff --git a/audio/drivers/oss.c b/audio/drivers/oss.c index 408a39141094..52b525ac34a6 100644 --- a/audio/drivers/oss.c +++ b/audio/drivers/oss.c @@ -216,7 +216,6 @@ static bool oss_use_float(void *data) audio_driver_t audio_oss = { oss_init, oss_write, - NULL, oss_stop, oss_start, oss_alive, @@ -227,6 +226,5 @@ audio_driver_t audio_oss = { NULL, NULL, oss_write_avail, - NULL, oss_buffer_size, }; diff --git a/audio/drivers/ps2_audio.c b/audio/drivers/ps2_audio.c index e8fb7d753731..786b2a49b429 100644 --- a/audio/drivers/ps2_audio.c +++ b/audio/drivers/ps2_audio.c @@ -148,7 +148,6 @@ static size_t ps2_audio_buffer_size(void *data) audio_driver_t audio_ps2 = { ps2_audio_init, ps2_audio_write, - NULL, ps2_audio_stop, ps2_audio_start, ps2_audio_alive, @@ -159,6 +158,5 @@ audio_driver_t audio_ps2 = { NULL, NULL, ps2_audio_write_avail, - NULL, ps2_audio_buffer_size }; diff --git a/audio/drivers/ps3_audio.c b/audio/drivers/ps3_audio.c index aa97dd40013d..1b40ed645f95 100644 --- a/audio/drivers/ps3_audio.c +++ b/audio/drivers/ps3_audio.c @@ -234,7 +234,6 @@ static size_t ps3_audio_write_avail(void *data) audio_driver_t audio_ps3 = { ps3_audio_init, ps3_audio_write, - NULL, ps3_audio_stop, ps3_audio_start, ps3_audio_alive, @@ -245,6 +244,5 @@ audio_driver_t audio_ps3 = { NULL, NULL, ps3_audio_write_avail, - NULL, NULL }; diff --git a/audio/drivers/psp_audio.c b/audio/drivers/psp_audio.c index a787910b53e6..6e9a394672a5 100644 --- a/audio/drivers/psp_audio.c +++ b/audio/drivers/psp_audio.c @@ -322,7 +322,6 @@ static size_t psp_buffer_size(void *data) audio_driver_t audio_psp = { psp_audio_init, psp_audio_write, - NULL, psp_audio_stop, psp_audio_start, psp_audio_alive, @@ -339,6 +338,5 @@ audio_driver_t audio_psp = { NULL, NULL, psp_write_avail, - NULL, psp_buffer_size }; diff --git a/audio/drivers/pulse.c b/audio/drivers/pulse.c index 3a7777d70efb..fd27ca7b8fcd 100644 --- a/audio/drivers/pulse.c +++ b/audio/drivers/pulse.c @@ -355,7 +355,6 @@ static size_t pulse_buffer_size(void *data) audio_driver_t audio_pulse = { pulse_init, pulse_write, - NULL, pulse_stop, pulse_start, pulse_alive, @@ -366,6 +365,5 @@ audio_driver_t audio_pulse = { NULL, NULL, pulse_write_avail, - NULL, pulse_buffer_size, }; diff --git a/audio/drivers/roar.c b/audio/drivers/roar.c index 087a72305501..9110fdfb10c2 100644 --- a/audio/drivers/roar.c +++ b/audio/drivers/roar.c @@ -140,7 +140,6 @@ static size_t ra_write_avail(void *data) audio_driver_t audio_roar = { ra_init, ra_write, - NULL, ra_stop, ra_start, ra_alive, @@ -151,6 +150,5 @@ audio_driver_t audio_roar = { NULL, NULL, ra_write_avail, - NULL, NULL }; diff --git a/audio/drivers/rsound.c b/audio/drivers/rsound.c index 99f8ded5fe6d..fbc6f749a76a 100644 --- a/audio/drivers/rsound.c +++ b/audio/drivers/rsound.c @@ -228,7 +228,6 @@ static bool rs_use_float(void *data) audio_driver_t audio_rsound = { rs_init, rs_write, - NULL, rs_stop, rs_start, rs_alive, @@ -239,6 +238,5 @@ audio_driver_t audio_rsound = { NULL, NULL, rs_write_avail, - NULL, rs_buffer_size, }; diff --git a/audio/drivers/rwebaudio.c b/audio/drivers/rwebaudio.c index 0eb3ba130a13..41cf2d1ea6bb 100644 --- a/audio/drivers/rwebaudio.c +++ b/audio/drivers/rwebaudio.c @@ -97,7 +97,6 @@ static bool rwebaudio_use_float(void *data) { return true; } audio_driver_t audio_rwebaudio = { rwebaudio_init, rwebaudio_write, - NULL, rwebaudio_stop, rwebaudio_start, rwebaudio_alive, @@ -108,6 +107,5 @@ audio_driver_t audio_rwebaudio = { NULL, NULL, rwebaudio_write_avail, - NULL, rwebaudio_buffer_size, }; diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 24ff4adbac3e..6e94f57ae2bf 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -371,13 +371,6 @@ static size_t sdl_audio_write_avail(void *data) return 0; } -static size_t sdl_audio_read_avail(void *data) -{ - /* stub */ - (void)data; - return 0; -} - #if SDL_DRIVER_MIC_SUPPORT static void *sdl_audio_init_microphone(void *data, const char *device, @@ -490,17 +483,6 @@ static void sdl_audio_free_microphone(void *data, void *microphone_context) } } -static void * sdl_audio_get_microphone(const void *data, unsigned id) -{ - const sdl_audio_t *sdl = (const sdl_audio_t*)data; - - if (id == 0 && sdl->microphone) - return sdl->microphone; - /* This driver only supports one microphone right now */ - - return NULL; -} - static bool sdl_audio_microphone_get_state(const void *data, const void *microphone_context) { const sdl_audio_t *sdl = (const sdl_audio_t*)data; @@ -600,12 +582,10 @@ audio_driver_t audio_sdl = { NULL, NULL, sdl_audio_write_avail, - sdl_audio_read_avail, NULL, #if SDL_DRIVER_MIC_SUPPORT sdl_audio_init_microphone, sdl_audio_free_microphone, - sdl_audio_get_microphone, sdl_audio_microphone_get_state, sdl_audio_microphone_set_state, sdl_audio_read @@ -614,7 +594,6 @@ audio_driver_t audio_sdl = { NULL, NULL, NULL, - NULL, NULL #endif }; diff --git a/audio/drivers/switch_audio.c b/audio/drivers/switch_audio.c index d3fea49ef080..a2b6291ccbfc 100644 --- a/audio/drivers/switch_audio.c +++ b/audio/drivers/switch_audio.c @@ -355,7 +355,6 @@ static void *switch_audio_init(const char *device, audio_driver_t audio_switch = { switch_audio_init, switch_audio_write, - NULL, switch_audio_stop, switch_audio_start, switch_audio_alive, @@ -366,6 +365,5 @@ audio_driver_t audio_switch = { NULL, /* device_list_new */ NULL, /* device_list_free */ switch_audio_write_avail, - NULL, switch_audio_buffer_size, /* buffer_size */ }; diff --git a/audio/drivers/switch_libnx_audren_audio.c b/audio/drivers/switch_libnx_audren_audio.c index 04bb06560b9b..998a25a6a270 100644 --- a/audio/drivers/switch_libnx_audren_audio.c +++ b/audio/drivers/switch_libnx_audren_audio.c @@ -354,7 +354,6 @@ static void libnx_audren_audio_set_nonblock_state(void *data, bool state) audio_driver_t audio_switch_libnx_audren = { libnx_audren_audio_init, libnx_audren_audio_write, - NULL, libnx_audren_audio_stop, libnx_audren_audio_start, libnx_audren_audio_alive, @@ -365,6 +364,5 @@ audio_driver_t audio_switch_libnx_audren = { NULL, /* device_list_new */ NULL, /* device_list_free */ libnx_audren_audio_write_avail, - NULL, libnx_audren_audio_buffer_size, }; diff --git a/audio/drivers/switch_libnx_audren_thread_audio.c b/audio/drivers/switch_libnx_audren_thread_audio.c index 5762c875f2e1..286dbd1dc637 100644 --- a/audio/drivers/switch_libnx_audren_thread_audio.c +++ b/audio/drivers/switch_libnx_audren_thread_audio.c @@ -427,7 +427,6 @@ static void libnx_audren_thread_audio_set_nonblock_state(void *data, bool state) audio_driver_t audio_switch_libnx_audren_thread = { libnx_audren_thread_audio_init, libnx_audren_thread_audio_write, - NULL, libnx_audren_thread_audio_stop, libnx_audren_thread_audio_start, libnx_audren_thread_audio_alive, @@ -438,6 +437,5 @@ audio_driver_t audio_switch_libnx_audren_thread = { NULL, /* device_list_new */ NULL, /* device_list_free */ libnx_audren_thread_audio_write_avail, - NULL, libnx_audren_thread_audio_buffer_size, }; diff --git a/audio/drivers/switch_thread_audio.c b/audio/drivers/switch_thread_audio.c index 755f22a6b96f..3a530b24f07d 100644 --- a/audio/drivers/switch_thread_audio.c +++ b/audio/drivers/switch_thread_audio.c @@ -432,7 +432,6 @@ size_t switch_thread_audio_buffer_size(void *data) audio_driver_t audio_switch_thread = { switch_thread_audio_init, switch_thread_audio_write, - NULL, switch_thread_audio_stop, switch_thread_audio_start, switch_thread_audio_alive, @@ -443,7 +442,6 @@ audio_driver_t audio_switch_thread = { NULL, /* device_list_new */ NULL, /* device_list_free */ switch_thread_audio_write_avail, - NULL, switch_thread_audio_buffer_size }; diff --git a/audio/drivers/tinyalsa.c b/audio/drivers/tinyalsa.c index ee60b2fef7ca..fee417477709 100644 --- a/audio/drivers/tinyalsa.c +++ b/audio/drivers/tinyalsa.c @@ -2416,7 +2416,6 @@ static size_t tinyalsa_buffer_size(void *data) audio_driver_t audio_tinyalsa = { tinyalsa_init, /* AUDIO_init */ tinyalsa_write, /* AUDIO_write */ - NULL, /* AUDIO_read */ /*TODO*/ tinyalsa_stop, /* AUDIO_stop */ tinyalsa_start, /* AUDIO_start */ tinyalsa_alive, /* AUDIO_alive */ @@ -2427,6 +2426,5 @@ audio_driver_t audio_tinyalsa = { NULL, /* AUDIO_device_list_new */ /*TODO*/ NULL, /* AUDIO_device_list_free */ /*TODO*/ tinyalsa_write_avail, /* AUDIO_write_avail */ /*TODO*/ - NULL, /* AUDIO_read_avail */ /*TODO*/ tinyalsa_buffer_size, /* AUDIO_buffer_size */ /*TODO*/ }; diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index 3801a9d67f24..5f47fd2dd8c5 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -911,7 +911,6 @@ static size_t wasapi_buffer_size(void *wh) audio_driver_t audio_wasapi = { wasapi_init, wasapi_write, - NULL, wasapi_stop, wasapi_start, wasapi_alive, @@ -922,6 +921,5 @@ audio_driver_t audio_wasapi = { mmdevice_list_new, wasapi_device_list_free, wasapi_write_avail, - NULL, wasapi_buffer_size }; diff --git a/audio/drivers/wiiu_audio.c b/audio/drivers/wiiu_audio.c index b61c8cf654d0..de5d3d93531c 100644 --- a/audio/drivers/wiiu_audio.c +++ b/audio/drivers/wiiu_audio.c @@ -322,7 +322,6 @@ audio_driver_t audio_ax = { ax_audio_init, ax_audio_write, - NULL, /* read */ ax_audio_stop, ax_audio_start, ax_audio_alive, @@ -333,6 +332,5 @@ audio_driver_t audio_ax = NULL, /* device_list_new */ NULL, /* device_list_free */ ax_audio_write_avail, - NULL, ax_audio_buffer_size, }; diff --git a/audio/drivers/xaudio.c b/audio/drivers/xaudio.c index 787f674aaf22..277e8ed671e6 100644 --- a/audio/drivers/xaudio.c +++ b/audio/drivers/xaudio.c @@ -520,7 +520,6 @@ static void *xa_list_new(void *u) audio_driver_t audio_xa = { xa_init, xa_write, - NULL, xa_stop, xa_start, xa_alive, @@ -531,6 +530,5 @@ audio_driver_t audio_xa = { xa_list_new, xa_device_list_free, xa_write_avail, - NULL, xa_buffer_size, }; diff --git a/audio/drivers/xenon360_audio.c b/audio/drivers/xenon360_audio.c index da8aa7ce1441..98a71443f267 100644 --- a/audio/drivers/xenon360_audio.c +++ b/audio/drivers/xenon360_audio.c @@ -140,7 +140,6 @@ static size_t xenon360_write_avail(void *data) audio_driver_t audio_xenon360 = { xenon360_audio_init, xenon360_audio_write, - NULL, xenon360_audio_stop, xenon360_audio_start, xenon360_audio_alive, @@ -151,6 +150,5 @@ audio_driver_t audio_xenon360 = { NULL, NULL, xenon360_write_avail, - NULL, NULL }; From c04525e08f44a6262373a3bde78e9a285f6cf13a Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 20:05:06 -0500 Subject: [PATCH 023/309] Add a microphone_device config item --- config.def.h | 1 + configuration.c | 6 ++++++ configuration.h | 1 + 3 files changed, 8 insertions(+) diff --git a/config.def.h b/config.def.h index 07271d4d0a03..686fbb3ff658 100644 --- a/config.def.h +++ b/config.def.h @@ -1064,6 +1064,7 @@ /* Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults. */ #define DEFAULT_AUDIO_DEVICE NULL +#define DEFAULT_MICROPHONE_DEVICE NULL /* Desired audio latency in milliseconds. Might not be honored * if driver can't provide given latency. */ diff --git a/configuration.c b/configuration.c index 7fabdf530be7..00f5734d9f90 100644 --- a/configuration.c +++ b/configuration.c @@ -1416,6 +1416,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings SETTING_ARRAY("menu_driver", settings->arrays.menu_driver, false, NULL, true); #endif SETTING_ARRAY("audio_device", settings->arrays.audio_device, false, NULL, true); + SETTING_ARRAY("microphone_device", settings->arrays.microphone_device, false, NULL, true); SETTING_ARRAY("camera_device", settings->arrays.camera_device, false, NULL, true); #ifdef HAVE_CHEEVOS SETTING_ARRAY("cheevos_custom_host", settings->arrays.cheevos_custom_host, false, NULL, true); @@ -2672,6 +2673,11 @@ void config_set_defaults(void *data) settings->arrays.audio_device, DEFAULT_AUDIO_DEVICE); + if (DEFAULT_MICROPHONE_DEVICE) + configuration_set_string(settings, + settings->arrays.microphone_device, + DEFAULT_MICROPHONE_DEVICE); + if (!g_defaults.settings_out_latency) g_defaults.settings_out_latency = DEFAULT_OUT_LATENCY; diff --git a/configuration.h b/configuration.h index 5c95ca134111..af560cea76dc 100644 --- a/configuration.h +++ b/configuration.h @@ -431,6 +431,7 @@ typedef struct settings char input_keyboard_layout[64]; char audio_device[255]; + char microphone_device[255]; char camera_device[255]; char netplay_mitm_server[255]; From 314f89780e8cedd458f6ca6cd4a2b56e5cf6ebbb Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 20:05:44 -0500 Subject: [PATCH 024/309] Clarify a comment in sdl_audio --- audio/drivers/sdl_audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 6e94f57ae2bf..983c55a66548 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -286,8 +286,8 @@ static bool sdl_audio_stop(void *data) if (sdl->microphone) { /* Stop the microphone independently of whether it's paused; - * note that upon sdl_audio_start, the microphone might not resume - * if it was paused */ + * note that upon sdl_audio_start, the microphone won't resume + * if it was previously paused */ SDL_PauseAudioDevice(sdl->microphone->device_id, true); } #endif From 7a74e501611aced4946bf46ebaea6bdb95b8607d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 20:06:16 -0500 Subject: [PATCH 025/309] Allow sdl_audio_init_microphone to accept a NULL new_rate --- audio/drivers/sdl_audio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 983c55a66548..dd7bf6d6c838 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -423,14 +423,16 @@ static void *sdl_audio_init_microphone(void *data, goto error; } - *new_rate = out.freq; + if (new_rate) + *new_rate = out.freq; + #ifdef HAVE_THREADS microphone->lock = slock_new(); microphone->cond = scond_new(); #endif RARCH_LOG("[SDL audio]: Requested %u ms latency for input device, got %d ms\n", - latency, (int)(out.samples * 4 * 1000 / (*new_rate))); + latency, (int)(out.samples * 4 * 1000 / out.freq)); /* Create a buffer twice as big as needed and prefill the buffer. */ bufsize = out.samples * 4 * sizeof(int16_t); From 89eccc2c4d94575636ba7c52cce69d1b9f707eec Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 20:25:45 -0500 Subject: [PATCH 026/309] Revise the microphone-related parts of the audio driver API - Use opaque handles (pointers, in practice) instead of indexes - More checks against NULL pointers or other invalid state --- audio/audio_driver.c | 107 +++++++++++++++++++++++++------------------ audio/audio_driver.h | 17 +++++-- 2 files changed, 75 insertions(+), 49 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index e654bd0fc1c0..17a4039677fe 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1699,80 +1699,97 @@ bool audio_driver_supports_microphone(const audio_driver_t* driver) driver->read_microphone; } -bool audio_driver_set_microphone_state(unsigned index, bool state) +retro_microphone_t *audio_driver_init_microphone(void) { audio_driver_state_t *audio_st = &audio_driver_st; void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - void *microphone = NULL; + const settings_t *settings = config_get_ptr(); - if (!context || !audio_driver) - return false; - /* If the audio driver isn't initialized... */ - - if (!audio_driver_supports_microphone(audio_driver)) - return false; - /* If the audio driver doesn't support microphones... */ - - /* Audio driver doesn't need to be alive to set the microphone state */ - microphone = audio_driver->get_microphone(context, index); + if ( context && + audio_driver && + settings && + audio_driver->init_microphone) + { + unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; + unsigned setting_audio_latency = settings->uints.audio_latency; + unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? + runloop_audio_latency : setting_audio_latency; + audio_driver->init_microphone(context, + *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, + settings->uints.audio_input_sample_rate, + audio_latency, + settings->uints.audio_block_frames, + NULL); + } + else + return NULL; +} - if (!microphone) - return false; +void audio_driver_free_microphone(retro_microphone_t *microphone) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + void *context = audio_st->context_audio_data; + const audio_driver_t *audio_driver = audio_st->current_audio; - return audio_driver->set_microphone_state(context, microphone, state); + if ( microphone && + context && + audio_driver && + audio_driver->free_microphone) + audio_driver->free_microphone(context, microphone); } -bool audio_driver_get_microphone_state(unsigned index) +bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool state) { audio_driver_state_t *audio_st = &audio_driver_st; void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - void *microphone = NULL; - if (!context || !audio_driver) + if ( microphone && + context && + audio_driver && + audio_driver->set_microphone_state) + return audio_driver->set_microphone_state(context, microphone, state); + else return false; - /* If the audio driver isn't initialized... */ - if (!audio_driver_supports_microphone(audio_driver)) - return false; - /* If the audio driver doesn't support microphones... */ + /* Audio driver doesn't need to be alive to set the microphone state */ +} - /* Audio driver doesn't need to be alive to get the microphone state */ - microphone = audio_driver->get_microphone(context, index); +bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + void *context = audio_st->context_audio_data; + const audio_driver_t *audio_driver = audio_st->current_audio; - if (!microphone) + if ( microphone && + context && + audio_driver && + audio_driver->get_microphone_state) + return audio_driver->get_microphone_state(context, microphone); + else return false; - return audio_driver->get_microphone_state(context, microphone); + /* Audio driver doesn't need to be alive to get the microphone state */ } -size_t audio_driver_get_microphone_input(unsigned index, int16_t* data, size_t data_length) +ssize_t audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length) { audio_driver_state_t *audio_st = &audio_driver_st; void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - void *microphone = NULL; ssize_t ret = -1; - if (!context || !audio_driver) - return -1; - /* If the audio driver isn't initialized... */ - - if (!audio_driver_supports_microphone(audio_driver)) - return -1; - /* If the audio driver doesn't support microphones... */ - - if (!audio_driver_alive()) + if ( audio_driver_alive() && + microphone && + context && + audio_driver && + audio_driver->get_microphone_state && + audio_driver->get_microphone_state(context, microphone) && + audio_driver->read_microphone) + return audio_driver->read_microphone(context, microphone, data, data_length); + else return -1; - /* If the audio driver isn't currently running... */ - - microphone = audio_driver->get_microphone(context, index); - - if (microphone && audio_driver->get_microphone_state(context, microphone)) - ret = audio_driver->read_microphone(context, microphone, data, data_length); - - return ret; } #ifdef HAVE_REWIND diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 9c30bdf3f414..35a8bde41fac 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -179,6 +179,8 @@ typedef struct audio_driver /** * Initializes a microphone using the audio driver. + * Cores that use microphone functionality will call this via + * retro_microphone_interface::init_microphone. * * @param data Handle to the driver context * that was originally returned by ::init. @@ -189,7 +191,10 @@ typedef struct audio_driver * @param rate The requested sampling rate of the new microphone. * @param latency TODO * @param block_frames TODO - * @param new_rate TODO + * @param new_rate Pointer to the actual sample frequency, + * if the microphone couldn't be initialized with the value given by rate. + * If NULL, then the value will not be reported to the client; + * this is not an error. * @return An opaque handle to the newly-initialized microphone * if it was successfully created, * or \c NULL if there was an error. @@ -451,11 +456,15 @@ bool audio_driver_stop(void); */ bool audio_driver_supports_microphone(const audio_driver_t* driver); -bool audio_driver_set_microphone_state(unsigned index, bool state); +retro_microphone_t *audio_driver_init_microphone(void); -bool audio_driver_get_microphone_state(unsigned index); +void audio_driver_free_microphone(retro_microphone_t *microphone); -size_t audio_driver_get_microphone_input(unsigned index, int16_t* data, size_t data_length); +bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool state); + +bool audio_driver_get_microphone_state(const retro_microphone_t *microphone); + +ssize_t audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length); #ifdef HAVE_TRANSLATE /* TODO/FIXME - Doesn't currently work. Fix this. */ From 3e50d74d52441729fe918d67f45808da88187637 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 18 Dec 2022 20:28:38 -0500 Subject: [PATCH 027/309] Revise the microphone parts of the libretro API - Use handles instead of indexes - Add callbacks for initializing and freeing microphones - Remove callbacks for counting microphones (some drivers may not be able to provide that info) - Add RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED - Implement RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE and RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED --- libretro-common/include/libretro.h | 126 +++++++++++++++++++---------- runloop.c | 38 +++++++++ 2 files changed, 120 insertions(+), 44 deletions(-) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 89c733b90313..fa42904ed3bf 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1769,14 +1769,20 @@ enum retro_mod #define RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE (73 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* struct retro_microphone_interface * -- - * Returns an interface that can be used to receive audio from - * one or more microphones, depending on what's supported by the - * audio driver. + * Returns an interface that can be used to receive audio from the audio driver. * - * Will return NULL if the current audio driver or libretro implementation - * doesn't support microphones. + * Create the interface and this callback will populate it. + * + * If the frontend and audio driver support microphones, + * all function pointers will be non-NULL. + * Otherwise, all function pointers will be NULL. */ +#define RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED (74 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* bool * -- + * Returns true if the user has enabled the microphone, + * regardless of whether the current audio driver supports it. + */ /* VFS functionality */ /* File paths: @@ -3793,59 +3799,95 @@ struct retro_throttle_state }; /** - * Enables or disables the microphone at the given index. - * Microphones are disabled by default, - * and must be explicitly enabled. + * Opaque handle to a microphone that's been opened for use. + * You don't access microphone objects directly; + * use the retro_microphone_interface. + */ +typedef void retro_microphone_t; + +/** + * Initializes a new microphone. + * Assuming that microphone support is enabled and provided by the frontend, + * you can call this whenever your core needs it. + * You could call this to keep a microphone throughout your core's lifetime, + * or you could call this when a microphone is plugged in to the emulated device. + * + * @returns \c NULL if a microphone couldn't be initialized. + * This likely means that no microphone is plugged in and recognized, + * or the maximum number of supported microphones has been reached. + */ +typedef retro_microphone_t *(RETRO_CALLCONV *retro_init_microphone_t)(void); + +/** + * Closes a microphone that was initialized with \c retro_init_microphone. + * Calling this function will stop all microphone activity + * and free up the resources that it allocated. + * Afterwards, the handle is invalid and must not be used. + * + * @param microphone Pointer to the microphone that was allocated by init_microphone. + * If \c NULL, this function does nothing. + */ +typedef void (RETRO_CALLCONV *retro_free_microphone_t)(retro_microphone_t *microphone); + +/** + * Enables or disables the given microphone. + * Microphones are disabled by default + * and must be explicitly enabled before they can be used. * Disabled microphones will not process incoming audio samples, * and will therefore have minimal impact on overall performance. * You may enable microphones throughout the lifetime of a core, * or only in instances where they're needed. * - * A frontend might not support microphones, - * or it might only support one. * Your core should be able to operate without microphone input; - * we suggest substituting silence in such a case. + * we suggest substituting silence in this case. + * + * Calling this function while the audio driver is paused + * \em will update the state of the microphone, + * but it won't reactivate it. + * You'll have to resume the audio driver for that. * - * @param index The index of the microphone to set the state of. - * Most likely will be 0. - * @param state @c true if the microphone should receive audio input, + * @param microphone Opaque handle to the microphone + * whose state will be adjusted. + * This will have been provided by \c init_microphone. + * @param state \c true if the microphone should receive audio input, * @c false if it should be idle. - * @returns @c true if the microphone's state was successfully set, - * @c false if @c index does not indicate a valid microphone + * @returns \c true if the microphone's state was successfully set, + * \c false if \c microphone is invalid * or if there was an error. */ -typedef bool (RETRO_CALLCONV *retro_set_microphone_state_t)(unsigned index, bool state); +typedef bool (RETRO_CALLCONV *retro_set_microphone_state_t)(retro_microphone_t *microphone, bool state); /** * Queries the state of a microphone at the given index. + * Will return whether the microphone is enabled, + * even if the driver is paused. * - * @param index The number of the microphone to query. - * Most likely will be 0, - * unless a platform uses multiple microphones for input - * (e.g. per controller). - * @return true if the microphone given by index is active, - * false if not or if index does not indicate a valid microphone. + * @param microphone Opaque handle to the microphone + * whose state will be queried. + * @return true if the provided \c microphone is valid and active, + * false if not or if there was an error. */ -typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(unsigned index); - -/** - * @return The number of microphones that are currently available. - * 0 indicates that no microphones are available, - * or that the frontend doesn't support them. - */ -typedef unsigned (RETRO_CALLCONV *retro_num_available_microphones_t)(void); +typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(const retro_microphone_t *microphone); /** * Retrieves the input processed by the microphone since the previous frame. + * If called while the microphone or the audio driver are disabled, + * then nothing will be copied into data. * - * @param index The number of the microphone to query + * @param microphone Opaque handle to the microphone + * whose recent input will be retrieved. * @param data The buffer that will be used to store the microphone's data. * Microphone input is in mono (i.e. one number per sample). - * @param data_length The size of the data buffer, in samples. + * Should be large enough to accommodate the expected number of samples per frame; + * for example, a 44.1kHz sample rate at 60 FPS would require space for 735 samples. + * @param data_length The size of the data buffer, in samples (\em not bytes). + * * @return The number of samples that were collected this frame. - * Will return 0 if the microphone is invalid or disabled. + * Will return 0 if the microphone is disabled. + * Will return -1 if the audio driver is paused, + * or if there was an error. */ -typedef size_t (RETRO_CALLCONV *retro_get_microphone_input_t)(unsigned index, int16_t* data, size_t data_length); +typedef ssize_t (RETRO_CALLCONV *retro_get_microphone_input_t)(retro_microphone_t *microphone, int16_t* data, size_t data_length); /** * An interface for querying the microphone and accessing data read from it. @@ -3857,16 +3899,12 @@ typedef size_t (RETRO_CALLCONV *retro_get_microphone_input_t)(unsigned index, in struct retro_microphone_interface { /** - * The number of microphones that the frontend supports - * with its current audio driver. - * 0 indicates that the frontend does not support microphones. - * A return value of INT_MAX indicates that the frontend - * supports as many microphones as the system permits. - * I don't know why you'd want arbitrarily many microphones, - * but I guess this is how you'd represent that. + * True if the driver and frontend support microphones. + * Set by the frontend. */ - unsigned max_supported_microphones; - retro_num_available_microphones_t num_available_microphones; + bool supported; + retro_init_microphone_t init_microphone; + retro_free_microphone_t free_microphone; retro_set_microphone_state_t set_microphone_state; retro_get_microphone_state_t get_microphone_state; retro_get_microphone_input_t get_microphone_input; diff --git a/runloop.c b/runloop.c index 0f0ee5094cb1..381257105154 100644 --- a/runloop.c +++ b/runloop.c @@ -3319,7 +3319,45 @@ bool runloop_environment_cb(unsigned cmd, void *data) } } break; + case RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE: + { + struct retro_microphone_interface* microphone = (struct retro_microphone_interface *)data; + const audio_driver_t *audio_driver = audio_state_get_ptr()->current_audio; + bool driver_supports_microphones = audio_driver_supports_microphone(audio_driver); + + if (!microphone) + return false; + /* User didn't provide a pointer for a response, what can we do? */ + + if (driver_supports_microphones) + { + microphone->supported = driver_supports_microphones; + microphone->init_microphone = audio_driver_init_microphone; + microphone->free_microphone = audio_driver_free_microphone; + microphone->set_microphone_state = audio_driver_set_microphone_state; + microphone->get_microphone_state = audio_driver_get_microphone_state; + microphone->get_microphone_input = audio_driver_get_microphone_input; + } + else + { + memset(microphone, 0, sizeof(*microphone)); + /* Clears all function pointers and sets supported to false */ + + return false; + } + } + break; + case RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED: + { + bool *microphone_enabled = (bool *)data; + if (microphone_enabled == NULL) + return false; + /* User didn't provide a pointer for a response, what can we do? */ + + *microphone_enabled = settings->bools.audio_enable_microphone; + return true; + } default: RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd); return false; From 9121249fc8a423e14c58a74401d577ac78e9e2c6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 19 Dec 2022 10:43:20 -0500 Subject: [PATCH 028/309] Read input from the microphone --- audio/audio_driver.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 17a4039677fe..b3809a56b957 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -400,8 +400,10 @@ bool audio_driver_find_driver( * @data : pointer to audio buffer. * @right : amount of samples to write. * - * Writes audio samples to audio driver. Will first - * perform DSP processing (if enabled) and resampling. + * Writes audio samples to audio driver's output, + * and reads samples from the driver's input + * (if mic support is enabled). + * Will first perform DSP processing (if enabled) and resampling. **/ static void audio_driver_flush( audio_driver_state_t *audio_st, @@ -545,6 +547,30 @@ static void audio_driver_flush( audio_st->current_audio->write(audio_st->context_audio_data, output_data, output_frames * 2); } + + if ( audio_st->current_audio->read_microphone && + audio_st->flags & AUDIO_FLAG_MIC_ACTIVE && + audio_st->context_microphone_data && + audio_st->current_audio->get_microphone_state(audio_st->current_audio, + audio_st->context_microphone_data)) + { /* If mic support is enabled and available, and the mic is enabled... */ + void *input_data = audio_st->input_data; + unsigned input_frames = (unsigned)src_data.input_frames; + + if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) + input_frames *= sizeof(float); + else + { + convert_float_to_s16(audio_st->input_samples_conv_buf, + (const float*)input_data, input_frames); + + input_data = audio_st->input_samples_conv_buf; + input_frames *= sizeof(int16_t); + } + + audio_st->current_audio->read_microphone(audio_st->context_audio_data, + audio_st->context_microphone_data, input_data, input_frames); + } } #ifdef HAVE_AUDIOMIXER From 62d659a3eb18b094b5f5e8447fd73236e746be23 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 09:17:08 -0500 Subject: [PATCH 029/309] Fix some compiler errors - Turns out that ssize_t is non-standard --- audio/audio_driver.c | 3 +-- audio/audio_driver.h | 2 +- audio/drivers/sdl_audio.c | 4 ++-- libretro-common/include/libretro.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index b3809a56b957..7de1bffbec8c 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1799,12 +1799,11 @@ bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) /* Audio driver doesn't need to be alive to get the microphone state */ } -ssize_t audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length) +int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length) { audio_driver_state_t *audio_st = &audio_driver_st; void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - ssize_t ret = -1; if ( audio_driver_alive() && microphone && diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 35a8bde41fac..4894003fda2e 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -296,7 +296,7 @@ typedef struct audio_driver * @return The number of bytes that were read into \c buf, * or -1 if there was an error. */ - ssize_t (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); + int (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); } audio_driver_t; enum audio_driver_state_flags diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index dd7bf6d6c838..d9b993b63065 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -511,9 +511,9 @@ static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, return true; } -static ssize_t sdl_audio_read(void *data, void *microphone_context, void *buf, size_t size) +static int sdl_audio_read(void *data, void *microphone_context, void *buf, size_t size) { - ssize_t ret = 0; + int ret = 0; sdl_audio_t *sdl = (sdl_audio_t*)data; sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t*)microphone_context; diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index fa42904ed3bf..b21b3324433c 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -3887,7 +3887,7 @@ typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(const retro_micropho * Will return -1 if the audio driver is paused, * or if there was an error. */ -typedef ssize_t (RETRO_CALLCONV *retro_get_microphone_input_t)(retro_microphone_t *microphone, int16_t* data, size_t data_length); +typedef int (RETRO_CALLCONV *retro_get_microphone_input_t)(retro_microphone_t *microphone, int16_t* data, size_t data_length); /** * An interface for querying the microphone and accessing data read from it. From 2d1dafb747866c29f88a9bfd3da88e63990c0bdd Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 09:18:42 -0500 Subject: [PATCH 030/309] Missed a spot --- audio/audio_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 4894003fda2e..f0fb84ac4532 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -464,7 +464,7 @@ bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool stat bool audio_driver_get_microphone_state(const retro_microphone_t *microphone); -ssize_t audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length); +int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length); #ifdef HAVE_TRANSLATE /* TODO/FIXME - Doesn't currently work. Fix this. */ From 95e90f26ef639cfe114b4ca7ba01e83085532040 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 09:19:20 -0500 Subject: [PATCH 031/309] Add some logging --- audio/audio_driver.c | 1 + audio/drivers/sdl_audio.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 7de1bffbec8c..771dd95d0e9c 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -717,6 +717,7 @@ bool audio_driver_init_internal( audio_latency, settings->uints.audio_block_frames, &new_rate); + RARCH_LOG("[Audio]: Started synchronous audio driver\n"); } if (new_rate != 0) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index d9b993b63065..3840053473eb 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -422,6 +422,7 @@ static void *sdl_audio_init_microphone(void *data, RARCH_ERR("[SDL audio]: Failed to open SDL audio input device: %s\n", SDL_GetError()); goto error; } + RARCH_LOG("[SDL audio]: Opened SDL audio input device with ID %u\n", microphone->device_id); if (new_rate) *new_rate = out.freq; @@ -439,6 +440,8 @@ static void *sdl_audio_init_microphone(void *data, tmp = calloc(1, bufsize); microphone->sample_buffer = fifo_new(bufsize); + RARCH_DBG("[SDL audio]: Initialized microphone sample buffer with %u bytes\n", bufsize); + if (tmp) { fifo_write(microphone->sample_buffer, tmp, bufsize); @@ -446,6 +449,8 @@ static void *sdl_audio_init_microphone(void *data, } sdl->microphone = microphone; + + RARCH_LOG("[SDL audio]: Initialized microphone with device ID %u\n", microphone->device_id); return microphone; error: @@ -480,6 +485,7 @@ static void sdl_audio_free_microphone(void *data, void *microphone_context) scond_free(microphone->cond); #endif + RARCH_LOG("[SDL audio]: Freed microphone with former device ID %u\n", microphone->device_id); sdl->microphone = NULL; free(microphone); } @@ -508,6 +514,8 @@ static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, return false; microphone->is_paused = !enabled; + RARCH_LOG("[SDL audio]: Set state of microphone %u to %s\n", + microphone->device_id, enabled ? "enabled" : "disabled"); return true; } From d4e4d5a1e2c7fc5bde509f2db2ecb9c33fb9d0b4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 09:19:41 -0500 Subject: [PATCH 032/309] Return the microphone that was created - Whoops --- audio/audio_driver.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 771dd95d0e9c..2a5799a5a4d0 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1742,12 +1742,21 @@ retro_microphone_t *audio_driver_init_microphone(void) unsigned setting_audio_latency = settings->uints.audio_latency; unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? runloop_audio_latency : setting_audio_latency; - audio_driver->init_microphone(context, + + retro_microphone_t *microphone = audio_driver->init_microphone(context, *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, settings->uints.audio_input_sample_rate, audio_latency, settings->uints.audio_block_frames, NULL); + + if (microphone) + RARCH_DBG("[Audio] Initialized microphone (sample rate: %dHz)\n", + settings->uints.audio_input_sample_rate); + else + RARCH_DBG("[Audio] Driver did not return a microphone handle\n"); + + return microphone; } else return NULL; From bc271e16b82018007227bc53d405b3847cd496b8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 09:20:30 -0500 Subject: [PATCH 033/309] Update how the microphone is retrieved --- runloop.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/runloop.c b/runloop.c index 381257105154..720605ff28fb 100644 --- a/runloop.c +++ b/runloop.c @@ -3323,15 +3323,20 @@ bool runloop_environment_cb(unsigned cmd, void *data) { struct retro_microphone_interface* microphone = (struct retro_microphone_interface *)data; const audio_driver_t *audio_driver = audio_state_get_ptr()->current_audio; - bool driver_supports_microphones = audio_driver_supports_microphone(audio_driver); + + if (!audio_driver) + { + RARCH_DBG("[Environ]: Couldn't initialize microphone interface, driver is not initialized\n"); + return false; + } if (!microphone) return false; /* User didn't provide a pointer for a response, what can we do? */ - if (driver_supports_microphones) + if (audio_driver_supports_microphone(audio_driver)) { - microphone->supported = driver_supports_microphones; + microphone->supported = true; microphone->init_microphone = audio_driver_init_microphone; microphone->free_microphone = audio_driver_free_microphone; microphone->set_microphone_state = audio_driver_set_microphone_state; From 79f0b879c4ed1093a517bb3d930e32c55616c992 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 09:20:59 -0500 Subject: [PATCH 034/309] Remove a stray line from audio_null --- audio/audio_driver.c | 1 - 1 file changed, 1 deletion(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 2a5799a5a4d0..4d771b79a5a8 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -67,7 +67,6 @@ audio_driver_t audio_null = { NULL, /* init */ NULL, /* write */ - NULL, /* read */ NULL, /* stop */ NULL, /* start */ NULL, /* alive */ From c18949c4876029c642307fc32877300b0e64113e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 11:53:46 -0500 Subject: [PATCH 035/309] Receive microphone input --- audio/audio_driver.c | 157 ++++++++++++++++++++++++----- audio/audio_driver.h | 48 ++++++++- libretro-common/include/libretro.h | 10 +- runloop.c | 1 + 4 files changed, 185 insertions(+), 31 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 4d771b79a5a8..2a73b96d07d5 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -547,11 +547,14 @@ static void audio_driver_flush( output_data, output_frames * 2); } + /* Only one mic is supported right now. If you add support for multiple mics, + * read through all of them here. */ if ( audio_st->current_audio->read_microphone && - audio_st->flags & AUDIO_FLAG_MIC_ACTIVE && - audio_st->context_microphone_data && + (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && + audio_st->current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_READY && + audio_st->current_microphone.microphone_context && audio_st->current_audio->get_microphone_state(audio_st->current_audio, - audio_st->context_microphone_data)) + audio_st->current_microphone.microphone_context)) { /* If mic support is enabled and available, and the mic is enabled... */ void *input_data = audio_st->input_data; unsigned input_frames = (unsigned)src_data.input_frames; @@ -568,7 +571,7 @@ static void audio_driver_flush( } audio_st->current_audio->read_microphone(audio_st->context_audio_data, - audio_st->context_microphone_data, input_data, input_frames); + audio_st->current_microphone.microphone_context, input_data, input_frames); } } @@ -662,7 +665,9 @@ bool audio_driver_init_internal( return false; } - if (!audio_enable_microphone) + if (audio_enable_microphone) + audio_driver_st.flags |= AUDIO_FLAG_MIC_ACTIVE; + else audio_driver_st.flags &= ~AUDIO_FLAG_MIC_ACTIVE; /* Not an error if the mic is disabled */ @@ -831,6 +836,15 @@ bool audio_driver_init_internal( audio_mixer_init(settings->uints.audio_output_sample_rate); #endif + if (( audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) && + audio_driver_st.current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_PENDING) + { /* If the core requested a microphone before the driver was able to provide one...*/ + audio_driver_init_microphone(); + + if (audio_driver_st.current_microphone.state != AUDIO_DRIVER_MICROPHONE_STATE_READY) + RARCH_ERR("[Audio]: Failed to initialize a previously pending microphone\n"); + } + /* Threaded driver is initially stopped. */ if ( (audio_driver_st.flags & AUDIO_FLAG_ACTIVE) @@ -1725,40 +1739,90 @@ bool audio_driver_supports_microphone(const audio_driver_t* driver) driver->read_microphone; } +/* If you add support for multiple microphones, start here. */ retro_microphone_t *audio_driver_init_microphone(void) { audio_driver_state_t *audio_st = &audio_driver_st; - void *context = audio_st->context_audio_data; - const audio_driver_t *audio_driver = audio_st->current_audio; const settings_t *settings = config_get_ptr(); + const audio_driver_t *audio_driver = audio_st->current_audio; + void *context = audio_st->context_audio_data; - if ( context && - audio_driver && - settings && - audio_driver->init_microphone) + if (!settings) + { + RARCH_ERR("[Audio]: Failed to initialize microphone due to uninitialized config\n"); + return NULL; + } + + if (!audio_driver) { + RARCH_ERR("[Audio]: Failed to initialize microphone due to uninitialized audio driver\n"); + return NULL; + } + + if (!(audio_st->flags & AUDIO_FLAG_MIC_ACTIVE)) + { + RARCH_WARN("[Audio]: Refused to initialize microphone because it's disabled\n"); + return NULL; + } + + if (!audio_driver->init_microphone) + { + RARCH_ERR("[Audio]: Failed to initialize microphone due to lack of support in %s driver\n", + audio_driver->ident); + return NULL; + } + + if (audio_st->current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_READY) + { /* If we already have an active mic... */ + RARCH_ERR("[Audio]: Failed to initialize a second microphone, frontend only supports one at a time right now\n"); + return NULL; + } + + /* Cores might ask for a microphone before the audio driver is ready to provide them; + * if that happens, we have to initialize the microphones later. + * But the user still wants a handle, so we'll give them one. + */ + if (context) + { /* If the audio driver is ready to create a microphone... */ unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; unsigned setting_audio_latency = settings->uints.audio_latency; unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? runloop_audio_latency : setting_audio_latency; - retro_microphone_t *microphone = audio_driver->init_microphone(context, + audio_st->current_microphone.microphone_context = audio_driver->init_microphone(context, *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, settings->uints.audio_input_sample_rate, audio_latency, settings->uints.audio_block_frames, NULL); - if (microphone) - RARCH_DBG("[Audio] Initialized microphone (sample rate: %dHz)\n", + if (audio_st->current_microphone.microphone_context) + { /* If the microphone was successfully created... */ + RARCH_LOG("[Audio]: Initialized microphone (sample rate: %dHz)\n", settings->uints.audio_input_sample_rate); - else - RARCH_DBG("[Audio] Driver did not return a microphone handle\n"); - return microphone; + if (audio_st->current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_PENDING) + RARCH_DBG("[Audio]: Microphone was previously pending, but now it's ready\n"); + + audio_st->current_microphone.state = AUDIO_DRIVER_MICROPHONE_STATE_READY; + + return &audio_st->current_microphone; + } + else + { + RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); + audio_st->current_microphone.state = AUDIO_DRIVER_MICROPHONE_STATE_INVALID; + return NULL; + } } else - return NULL; + { + RARCH_LOG("[Audio]: Microphone requested before audio context was ready; will create it later\n"); + audio_st->current_microphone.state = AUDIO_DRIVER_MICROPHONE_STATE_PENDING; + audio_st->current_microphone.microphone_context = NULL; + + return &audio_st->current_microphone; + } } void audio_driver_free_microphone(retro_microphone_t *microphone) @@ -1771,7 +1835,24 @@ void audio_driver_free_microphone(retro_microphone_t *microphone) context && audio_driver && audio_driver->free_microphone) - audio_driver->free_microphone(context, microphone); + { + /* Not checking microphone->microphone_context for NULL because even if it is, + * we still want to set the state to AUDIO_DRIVER_MICROPHONE_STATE_INVALID; + * revisit this if we add support for multiple mics */ + + audio_driver->free_microphone(context, microphone->microphone_context); + microphone->state = AUDIO_DRIVER_MICROPHONE_STATE_INVALID; + microphone->microphone_context = NULL; + + /* DO NOT free the retro_microphone_t pointer right now; + * it's currently embedded directly in the audio_driver_state_t as a struct member. + * Once support for multiple mics is added and the retro_microphone_t's need to be + * allocated dynamically, *then* you can call free on them. + * + * Note that freeing the microphone *context* is fair game, since that was + * created by the driver. + */ + } } bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool state) @@ -1783,12 +1864,14 @@ bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool stat if ( microphone && context && audio_driver && - audio_driver->set_microphone_state) - return audio_driver->set_microphone_state(context, microphone, state); + audio_driver->set_microphone_state && + microphone->microphone_context) + return audio_driver->set_microphone_state(context, microphone->microphone_context, state); else return false; - /* Audio driver doesn't need to be alive to set the microphone state */ + /* Audio driver doesn't need to be alive to set the microphone state, + * but the mic itself needs to be initialized */ } bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) @@ -1800,12 +1883,28 @@ bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) if ( microphone && context && audio_driver && - audio_driver->get_microphone_state) - return audio_driver->get_microphone_state(context, microphone); + audio_driver->get_microphone_state && + microphone->microphone_context) + return audio_driver->get_microphone_state(context, microphone->microphone_context); else return false; - /* Audio driver doesn't need to be alive to get the microphone state */ + /* Audio driver doesn't need to be alive to get the microphone state, + * but the mic itself needs to be initialized */ +} + +bool audio_driver_is_microphone_ready(const retro_microphone_t *microphone) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + void *context = audio_st->context_audio_data; + const audio_driver_t *audio_driver = audio_st->current_audio; + + if ( microphone && + context && + audio_driver) + return microphone->state == AUDIO_DRIVER_MICROPHONE_STATE_READY; + else + return false; } int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length) @@ -1819,9 +1918,11 @@ int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* d context && audio_driver && audio_driver->get_microphone_state && - audio_driver->get_microphone_state(context, microphone) && - audio_driver->read_microphone) - return audio_driver->read_microphone(context, microphone, data, data_length); + audio_driver->get_microphone_state(context, microphone->microphone_context) && + audio_driver->read_microphone && + microphone->state == AUDIO_DRIVER_MICROPHONE_STATE_READY && + microphone->microphone_context) + return audio_driver->read_microphone(context, microphone->microphone_context, data, data_length); else return -1; } diff --git a/audio/audio_driver.h b/audio/audio_driver.h index f0fb84ac4532..def1cc0bd155 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -76,6 +76,50 @@ typedef struct audio_mixer_stream_params } audio_mixer_stream_params_t; #endif +/** + * The core might request a microphone before + * the audio driver is ready to provide one, + * so we have to keep track of when it's ready to be used. + */ +enum audio_driver_microphone_state +{ + /** + * This microphone is not ready to be used. + */ + AUDIO_DRIVER_MICROPHONE_STATE_INVALID, + + /** + * The microphone has been requested, + * but the audio driver isn't ready to provide it yet. + * This state is only used if the microphone is requested + * early in the core's lifetime (e.g. in retro_init); + * if the microphone is requested after the driver is initialized, + * then it should be ready immediately. + */ + AUDIO_DRIVER_MICROPHONE_STATE_PENDING, + + /** + * This microphone is initialized and ready to use. + */ + AUDIO_DRIVER_MICROPHONE_STATE_READY +}; + +/** + * Driver object that tracks a microphone's state. + * Pointers to this object are provided to cores, + * to be used as opaque handles. + */ +struct retro_microphone +{ + enum audio_driver_microphone_state state; + + /** + * Pointer to the context object created by the underlying driver. + * Will be non-NULL if and only if state == AUDIO_DRIVER_MICROPHONE_STATE_READY. + */ + void *microphone_context; +}; + typedef struct audio_driver { /* Creates and initializes handle to audio driver. @@ -340,10 +384,10 @@ typedef struct /** * The handle to the created microphone, if any. * The libretro API is designed to enable multiple microphones, - * but RetroArch and its drivers only supports one at a time for now. + * but RetroArch only supports one at a time for now. * PRs welcome! */ - void *context_microphone_data; + retro_microphone_t current_microphone; void *context_audio_data; float *input_data; #ifdef HAVE_AUDIOMIXER diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index b21b3324433c..2aa8a207d062 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -3803,7 +3803,7 @@ struct retro_throttle_state * You don't access microphone objects directly; * use the retro_microphone_interface. */ -typedef void retro_microphone_t; +typedef struct retro_microphone retro_microphone_t; /** * Initializes a new microphone. @@ -3869,6 +3869,13 @@ typedef bool (RETRO_CALLCONV *retro_set_microphone_state_t)(retro_microphone_t * */ typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(const retro_microphone_t *microphone); +/** + * Tests if the microphone is ready + * + * TODO docs + */ +typedef bool (RETRO_CALLCONV *retro_is_microphone_ready_t)(const retro_microphone_t *microphone); + /** * Retrieves the input processed by the microphone since the previous frame. * If called while the microphone or the audio driver are disabled, @@ -3907,6 +3914,7 @@ struct retro_microphone_interface retro_free_microphone_t free_microphone; retro_set_microphone_state_t set_microphone_state; retro_get_microphone_state_t get_microphone_state; + retro_is_microphone_ready_t is_microphone_ready; retro_get_microphone_input_t get_microphone_input; }; diff --git a/runloop.c b/runloop.c index 720605ff28fb..a9e1c0cbfb5c 100644 --- a/runloop.c +++ b/runloop.c @@ -3342,6 +3342,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) microphone->set_microphone_state = audio_driver_set_microphone_state; microphone->get_microphone_state = audio_driver_get_microphone_state; microphone->get_microphone_input = audio_driver_get_microphone_input; + microphone->is_microphone_ready = audio_driver_is_microphone_ready; } else { From f949b744e5552a8d49ab1a44cf408e4ca47f50e7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 20 Dec 2022 17:23:53 -0500 Subject: [PATCH 036/309] Refactor deferred microphone activation - Simpler to understand - Allow the enabled/disabled state of a pending mic to be set --- audio/audio_driver.c | 288 ++++++++++++++++++----------- audio/audio_driver.h | 38 +--- libretro-common/include/libretro.h | 14 +- runloop.c | 4 +- 4 files changed, 192 insertions(+), 152 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 2a73b96d07d5..94f794466404 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -549,12 +549,13 @@ static void audio_driver_flush( /* Only one mic is supported right now. If you add support for multiple mics, * read through all of them here. */ - if ( audio_st->current_audio->read_microphone && - (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && - audio_st->current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_READY && - audio_st->current_microphone.microphone_context && + if ( (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && + audio_st->current_microphone && + audio_st->current_microphone->microphone_context && + audio_st->current_audio->read_microphone && + audio_st->current_audio->get_microphone_state && audio_st->current_audio->get_microphone_state(audio_st->current_audio, - audio_st->current_microphone.microphone_context)) + audio_st->current_microphone->microphone_context)) { /* If mic support is enabled and available, and the mic is enabled... */ void *input_data = audio_st->input_data; unsigned input_frames = (unsigned)src_data.input_frames; @@ -571,10 +572,12 @@ static void audio_driver_flush( } audio_st->current_audio->read_microphone(audio_st->context_audio_data, - audio_st->current_microphone.microphone_context, input_data, input_frames); + audio_st->current_microphone->microphone_context, input_data, input_frames); } } +static void audio_driver_init_microphone_internal(retro_microphone_t* microphone); + #ifdef HAVE_AUDIOMIXER audio_mixer_stream_t *audio_driver_mixer_get_stream(unsigned i) { @@ -836,13 +839,21 @@ bool audio_driver_init_internal( audio_mixer_init(settings->uints.audio_output_sample_rate); #endif - if (( audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) && - audio_driver_st.current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_PENDING) + if ( audio_driver_st.current_microphone && + !audio_driver_st.current_microphone->microphone_context) { /* If the core requested a microphone before the driver was able to provide one...*/ - audio_driver_init_microphone(); + /* Now that the driver and driver context are ready, let's initialize the mid */ + audio_driver_init_microphone_internal(audio_driver_st.current_microphone); + + if (audio_driver_st.current_microphone->microphone_context) + RARCH_DBG("[Audio]: Initialized a previously-pending microphone\n"); + else + { + RARCH_ERR("[Audio]: Failed to initialize a previously pending microphone; microphone will not be used\n"); + + audio_driver_free_microphone(audio_driver_st.current_microphone); + } - if (audio_driver_st.current_microphone.state != AUDIO_DRIVER_MICROPHONE_STATE_READY) - RARCH_ERR("[Audio]: Failed to initialize a previously pending microphone\n"); } /* Threaded driver is initially stopped. */ @@ -1739,13 +1750,94 @@ bool audio_driver_supports_microphone(const audio_driver_t* driver) driver->read_microphone; } -/* If you add support for multiple microphones, start here. */ +static retro_microphone_t *audio_driver_microphone_handle_init(void *microphone_context) +{ + retro_microphone_t *microphone = malloc(sizeof(retro_microphone_t)); + + if (microphone) + { + microphone->microphone_context = microphone_context; + microphone->pending_enabled = false; + } + + return microphone; +} + +static void audio_driver_microphone_handle_free(retro_microphone_t* microphone) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + const audio_driver_t *audio_driver = audio_st->current_audio; + void *context = audio_st->context_audio_data; + + if (!microphone) + return; + + if (!context) + RARCH_WARN("[Audio]: Attempted to free a microphone without an active driver context\n"); + + if (microphone->microphone_context) + audio_driver->free_microphone(context, microphone->microphone_context); + + free(microphone); +} + +/** + * + * @param microphone Handle to the microphone to init with a context + * Check the value of microphone->microphone_context to see if this function succeeded + */ +static void audio_driver_init_microphone_internal(retro_microphone_t* microphone) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + settings_t *settings = config_get_ptr(); + const audio_driver_t *audio_driver = audio_st->current_audio; + void *context = audio_st->context_audio_data; + unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; + unsigned setting_audio_latency = settings->uints.audio_latency; + unsigned actual_sample_rate = 0; + unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? + runloop_audio_latency : setting_audio_latency; + + if (!microphone || !audio_driver) + return; + + void *microphone_context = audio_driver->init_microphone(context, + *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, + settings->uints.audio_input_sample_rate, + audio_latency, + settings->uints.audio_block_frames, + &actual_sample_rate); + + if (microphone_context) + { /* If the microphone context was successfully created... */ + microphone->microphone_context = microphone_context; + + RARCH_LOG("[Audio]: Initialized microphone (sample rate: %dHz)\n", actual_sample_rate); + + audio_driver_set_microphone_state(microphone, microphone->pending_enabled); + + if (actual_sample_rate != 0) + configuration_set_uint(settings, settings->uints.audio_input_sample_rate, actual_sample_rate); + } + else + { + RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); + microphone->pending_enabled = false; + microphone->microphone_context = NULL; + } +} + +/* NOTE: The core may request a microphone before the driver is ready. + * A pending handle will be provided in that case, and the frontend will + * initialize the microphone when the time is right; + * do not call this function twice on the same mic. */ retro_microphone_t *audio_driver_init_microphone(void) { audio_driver_state_t *audio_st = &audio_driver_st; const settings_t *settings = config_get_ptr(); const audio_driver_t *audio_driver = audio_st->current_audio; void *context = audio_st->context_audio_data; + retro_microphone_t *microphone = NULL; if (!settings) { @@ -1772,9 +1864,16 @@ retro_microphone_t *audio_driver_init_microphone(void) return NULL; } - if (audio_st->current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_READY) - { /* If we already have an active mic... */ + if (audio_st->current_microphone) + { /* If the core has requested a second microphone... */ RARCH_ERR("[Audio]: Failed to initialize a second microphone, frontend only supports one at a time right now\n"); + if (audio_st->current_microphone->microphone_context) + /* If that mic is initialized... */ + RARCH_ERR("[Audio]: An initialized microphone exists\n"); + else + /* That mic is pending */ + RARCH_ERR("[Audio]: A microphone is pending initialization\n"); + return NULL; } @@ -1782,47 +1881,34 @@ retro_microphone_t *audio_driver_init_microphone(void) * if that happens, we have to initialize the microphones later. * But the user still wants a handle, so we'll give them one. */ + microphone = audio_driver_microphone_handle_init(context); + /* If context is null, the handle won't have a valid microphone context (but we'll create one later) */ + + if (!microphone) + return NULL; + if (context) - { /* If the audio driver is ready to create a microphone... */ - unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; - unsigned setting_audio_latency = settings->uints.audio_latency; - unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? - runloop_audio_latency : setting_audio_latency; - - audio_st->current_microphone.microphone_context = audio_driver->init_microphone(context, - *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, - settings->uints.audio_input_sample_rate, - audio_latency, - settings->uints.audio_block_frames, - NULL); - - if (audio_st->current_microphone.microphone_context) - { /* If the microphone was successfully created... */ - RARCH_LOG("[Audio]: Initialized microphone (sample rate: %dHz)\n", - settings->uints.audio_input_sample_rate); - - if (audio_st->current_microphone.state == AUDIO_DRIVER_MICROPHONE_STATE_PENDING) - RARCH_DBG("[Audio]: Microphone was previously pending, but now it's ready\n"); - - audio_st->current_microphone.state = AUDIO_DRIVER_MICROPHONE_STATE_READY; - - return &audio_st->current_microphone; - } + { /* If the audio driver is ready to initialize a microphone... */ + audio_driver_init_microphone_internal(microphone); + + if (microphone->microphone_context) /* If the microphone was successfully initialized... */ + RARCH_LOG("[Audio]: Initialized the requested microphone successfully\n"); else - { - RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); - audio_st->current_microphone.state = AUDIO_DRIVER_MICROPHONE_STATE_INVALID; - return NULL; - } + goto mic_context_init_failed; } else - { - RARCH_LOG("[Audio]: Microphone requested before audio context was ready; will create it later\n"); - audio_st->current_microphone.state = AUDIO_DRIVER_MICROPHONE_STATE_PENDING; - audio_st->current_microphone.microphone_context = NULL; - - return &audio_st->current_microphone; + { /* If the audio driver isn't ready to create a microphone... */ + RARCH_LOG("[Audio]: Microphone requested before audio context was ready; deferring initialization\n"); } + + audio_st->current_microphone = microphone; + + return microphone; +mic_context_init_failed: + audio_driver_microphone_handle_free(microphone); + audio_st->current_microphone = NULL; + + return NULL; } void audio_driver_free_microphone(retro_microphone_t *microphone) @@ -1836,22 +1922,9 @@ void audio_driver_free_microphone(retro_microphone_t *microphone) audio_driver && audio_driver->free_microphone) { - /* Not checking microphone->microphone_context for NULL because even if it is, - * we still want to set the state to AUDIO_DRIVER_MICROPHONE_STATE_INVALID; - * revisit this if we add support for multiple mics */ - audio_driver->free_microphone(context, microphone->microphone_context); - microphone->state = AUDIO_DRIVER_MICROPHONE_STATE_INVALID; - microphone->microphone_context = NULL; - - /* DO NOT free the retro_microphone_t pointer right now; - * it's currently embedded directly in the audio_driver_state_t as a struct member. - * Once support for multiple mics is added and the retro_microphone_t's need to be - * allocated dynamically, *then* you can call free on them. - * - * Note that freeing the microphone *context* is fair game, since that was - * created by the driver. - */ + free(microphone); + audio_st->current_microphone = NULL; } } @@ -1861,50 +1934,51 @@ bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool stat void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - if ( microphone && - context && - audio_driver && - audio_driver->set_microphone_state && - microphone->microphone_context) - return audio_driver->set_microphone_state(context, microphone->microphone_context, state); - else + if (!microphone || !audio_driver || !audio_driver->set_microphone_state) return false; - /* Audio driver doesn't need to be alive to set the microphone state, - * but the mic itself needs to be initialized */ -} + if (context && microphone->microphone_context) + { /* If the driver is initialized... */ + bool success = audio_driver->set_microphone_state(context, microphone->microphone_context, state); -bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - void *context = audio_st->context_audio_data; - const audio_driver_t *audio_driver = audio_st->current_audio; + if (success) + RARCH_DBG("[Audio]: Set initialized microphone state to %s\n", + state ? "enabled" : "disabled"); + else + RARCH_ERR("[Audio]: Failed to set initialized microphone state to %s\n", + state ? "enabled" : "disabled"); - if ( microphone && - context && - audio_driver && - audio_driver->get_microphone_state && - microphone->microphone_context) - return audio_driver->get_microphone_state(context, microphone->microphone_context); + return success; + } else - return false; - - /* Audio driver doesn't need to be alive to get the microphone state, - * but the mic itself needs to be initialized */ + { /* The driver's not ready yet, so we'll make a note + * of what the mic's state should be */ + microphone->pending_enabled = state; + RARCH_DBG("[Audio]: Set pending microphone state to %s\n", + state ? "enabled" : "disabled"); + return true; + /* This isn't an error */ + } } -bool audio_driver_is_microphone_ready(const retro_microphone_t *microphone) +bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) { audio_driver_state_t *audio_st = &audio_driver_st; void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - if ( microphone && - context && - audio_driver) - return microphone->state == AUDIO_DRIVER_MICROPHONE_STATE_READY; - else + if (!microphone || !audio_driver || !audio_driver->get_microphone_state) return false; + + if (context && microphone->microphone_context) + { /* If the driver is initialized... */ + return audio_driver->get_microphone_state(context, microphone->microphone_context); + } + else + { /* The driver's not ready yet, + * so we'll use that note we made of the mic's active state */ + return microphone->pending_enabled; + } } int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length) @@ -1913,18 +1987,18 @@ int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* d void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - if ( audio_driver_alive() && - microphone && - context && - audio_driver && - audio_driver->get_microphone_state && - audio_driver->get_microphone_state(context, microphone->microphone_context) && - audio_driver->read_microphone && - microphone->state == AUDIO_DRIVER_MICROPHONE_STATE_READY && - microphone->microphone_context) - return audio_driver->read_microphone(context, microphone->microphone_context, data, data_length); - else + /* TODO: Should mic use on a pending microphone driver be an error? */ + if ( !microphone || + !audio_driver_alive() || + !audio_driver->get_microphone_state || + !audio_driver->read_microphone || + !microphone->microphone_context) return -1; + /* If the provided mic is invalid, + * or the audio driver isn't active, + * or mic support isn't fully implemented... */ + + return audio_driver->read_microphone(context, microphone->microphone_context, data, data_length); } #ifdef HAVE_REWIND diff --git a/audio/audio_driver.h b/audio/audio_driver.h index def1cc0bd155..f329ac37b3fa 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -76,34 +76,6 @@ typedef struct audio_mixer_stream_params } audio_mixer_stream_params_t; #endif -/** - * The core might request a microphone before - * the audio driver is ready to provide one, - * so we have to keep track of when it's ready to be used. - */ -enum audio_driver_microphone_state -{ - /** - * This microphone is not ready to be used. - */ - AUDIO_DRIVER_MICROPHONE_STATE_INVALID, - - /** - * The microphone has been requested, - * but the audio driver isn't ready to provide it yet. - * This state is only used if the microphone is requested - * early in the core's lifetime (e.g. in retro_init); - * if the microphone is requested after the driver is initialized, - * then it should be ready immediately. - */ - AUDIO_DRIVER_MICROPHONE_STATE_PENDING, - - /** - * This microphone is initialized and ready to use. - */ - AUDIO_DRIVER_MICROPHONE_STATE_READY -}; - /** * Driver object that tracks a microphone's state. * Pointers to this object are provided to cores, @@ -111,13 +83,13 @@ enum audio_driver_microphone_state */ struct retro_microphone { - enum audio_driver_microphone_state state; - /** * Pointer to the context object created by the underlying driver. - * Will be non-NULL if and only if state == AUDIO_DRIVER_MICROPHONE_STATE_READY. */ void *microphone_context; + + /* May be enabled even before the driver is ready */ + bool pending_enabled; }; typedef struct audio_driver @@ -383,11 +355,11 @@ typedef struct /** * The handle to the created microphone, if any. - * The libretro API is designed to enable multiple microphones, + * The libretro API is designed to expose multiple microphones, * but RetroArch only supports one at a time for now. * PRs welcome! */ - retro_microphone_t current_microphone; + retro_microphone_t *current_microphone; void *context_audio_data; float *input_data; #ifdef HAVE_AUDIOMIXER diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 2aa8a207d062..0bed307941bf 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -3869,13 +3869,6 @@ typedef bool (RETRO_CALLCONV *retro_set_microphone_state_t)(retro_microphone_t * */ typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(const retro_microphone_t *microphone); -/** - * Tests if the microphone is ready - * - * TODO docs - */ -typedef bool (RETRO_CALLCONV *retro_is_microphone_ready_t)(const retro_microphone_t *microphone); - /** * Retrieves the input processed by the microphone since the previous frame. * If called while the microphone or the audio driver are disabled, @@ -3890,9 +3883,9 @@ typedef bool (RETRO_CALLCONV *retro_is_microphone_ready_t)(const retro_microphon * @param data_length The size of the data buffer, in samples (\em not bytes). * * @return The number of samples that were collected this frame. - * Will return 0 if the microphone is disabled. - * Will return -1 if the audio driver is paused, - * or if there was an error. + * Will return -1 if the microphone is disabled, + * the audio driver is paused, + * or there was an error. */ typedef int (RETRO_CALLCONV *retro_get_microphone_input_t)(retro_microphone_t *microphone, int16_t* data, size_t data_length); @@ -3914,7 +3907,6 @@ struct retro_microphone_interface retro_free_microphone_t free_microphone; retro_set_microphone_state_t set_microphone_state; retro_get_microphone_state_t get_microphone_state; - retro_is_microphone_ready_t is_microphone_ready; retro_get_microphone_input_t get_microphone_input; }; diff --git a/runloop.c b/runloop.c index a9e1c0cbfb5c..8068a1c8e1b6 100644 --- a/runloop.c +++ b/runloop.c @@ -3334,6 +3334,9 @@ bool runloop_environment_cb(unsigned cmd, void *data) return false; /* User didn't provide a pointer for a response, what can we do? */ + if (!settings->bools.audio_enable_microphone) + return false; + if (audio_driver_supports_microphone(audio_driver)) { microphone->supported = true; @@ -3342,7 +3345,6 @@ bool runloop_environment_cb(unsigned cmd, void *data) microphone->set_microphone_state = audio_driver_set_microphone_state; microphone->get_microphone_state = audio_driver_get_microphone_state; microphone->get_microphone_input = audio_driver_get_microphone_input; - microphone->is_microphone_ready = audio_driver_is_microphone_ready; } else { From e3ff355c9e58c083eec0055f4425dbdd7761e28e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 18:44:42 -0500 Subject: [PATCH 037/309] Properly pause the microphone in the SDL driver --- audio/drivers/sdl_audio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 3840053473eb..a16ec58c8f4f 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -514,6 +514,10 @@ static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, return false; microphone->is_paused = !enabled; + if (!sdl->is_paused) + { /* If the entire audio driver isn't paused... */ + SDL_PauseAudioDevice(microphone->device_id, microphone->is_paused); + } RARCH_LOG("[SDL audio]: Set state of microphone %u to %s\n", microphone->device_id, enabled ? "enabled" : "disabled"); return true; From b074388a29ccf135683bea9db6253404b51708eb Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 18:44:51 -0500 Subject: [PATCH 038/309] Update a comment --- audio/drivers/sdl_audio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index a16ec58c8f4f..d60f1bd2c073 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -92,7 +92,9 @@ typedef struct sdl_audio SDL_AudioDeviceID speaker_device; #ifdef SDL_DRIVER_MIC_SUPPORT - /* Only one microphone is supported right now */ + /* Only one microphone is supported right now; + * the driver state should track multiple microphone handles, + * but the driver *context* should track multiple microphone contexts */ sdl_audio_microphone_t *microphone; #endif } sdl_audio_t; From aaafb9284a042efcd5bd9f670fbae41fb3c8ebd3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 18:45:09 -0500 Subject: [PATCH 039/309] Update some more comments --- libretro-common/include/libretro.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 0bed307941bf..02ff1c9ac30b 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1776,6 +1776,8 @@ enum retro_mod * If the frontend and audio driver support microphones, * all function pointers will be non-NULL. * Otherwise, all function pointers will be NULL. + * + * Returns false if mic support is disabled */ #define RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED (74 | RETRO_ENVIRONMENT_EXPERIMENTAL) @@ -3812,6 +3814,9 @@ typedef struct retro_microphone retro_microphone_t; * You could call this to keep a microphone throughout your core's lifetime, * or you could call this when a microphone is plugged in to the emulated device. * + * The returned handle will be valid until it's freed, + * even if the audio driver is reinitialized. + * * @returns \c NULL if a microphone couldn't be initialized. * This likely means that no microphone is plugged in and recognized, * or the maximum number of supported microphones has been reached. @@ -3858,7 +3863,7 @@ typedef void (RETRO_CALLCONV *retro_free_microphone_t)(retro_microphone_t *micro typedef bool (RETRO_CALLCONV *retro_set_microphone_state_t)(retro_microphone_t *microphone, bool state); /** - * Queries the state of a microphone at the given index. + * Queries the active state of a microphone at the given index. * Will return whether the microphone is enabled, * even if the driver is paused. * From e735908892c8d666e779c3337a18bdef53ea68aa Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 18:47:12 -0500 Subject: [PATCH 040/309] Refactor how microphones are allocated - They're embedded into the audio state - Allows the user-facing mic handle to survive driver reinits --- audio/audio_driver.c | 97 ++++++++++++++++++++++++-------------------- audio/audio_driver.h | 5 ++- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 94f794466404..c402ef7949d7 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -231,6 +231,24 @@ static bool audio_driver_free_devices_list(void) return true; } +/* TODO: When adding support for multiple microphones, + * make sure you clean them all up in here. */ +static bool audio_driver_free_microphones(void) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + const audio_driver_t *audio_driver = audio_st->current_audio; + + if ( !audio_driver || + !audio_driver->free_microphone || + !audio_st->context_audio_data || + !audio_st->current_microphone.active) + return false; + + audio_driver_free_microphone(&audio_st->current_microphone); + + return true; +} + #ifdef DEBUG static void report_audio_buffer_statistics(void) { @@ -302,6 +320,7 @@ static bool audio_driver_deinit_internal(bool audio_enable) if (!audio_enable) { audio_st->flags &= ~AUDIO_FLAG_ACTIVE; + audio_st->flags &= ~AUDIO_FLAG_MIC_ACTIVE; return false; } @@ -349,7 +368,7 @@ bool audio_driver_deinit(void) audio_driver_mixer_deinit(); #endif audio_driver_free_devices_list(); - + audio_driver_free_microphones(); return audio_driver_deinit_internal( settings->bools.audio_enable); } @@ -550,12 +569,12 @@ static void audio_driver_flush( /* Only one mic is supported right now. If you add support for multiple mics, * read through all of them here. */ if ( (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && - audio_st->current_microphone && - audio_st->current_microphone->microphone_context && + audio_st->current_microphone.active && + audio_st->current_microphone.microphone_context && audio_st->current_audio->read_microphone && audio_st->current_audio->get_microphone_state && audio_st->current_audio->get_microphone_state(audio_st->current_audio, - audio_st->current_microphone->microphone_context)) + audio_st->current_microphone.microphone_context)) { /* If mic support is enabled and available, and the mic is enabled... */ void *input_data = audio_st->input_data; unsigned input_frames = (unsigned)src_data.input_frames; @@ -572,7 +591,7 @@ static void audio_driver_flush( } audio_st->current_audio->read_microphone(audio_st->context_audio_data, - audio_st->current_microphone->microphone_context, input_data, input_frames); + audio_st->current_microphone.microphone_context, input_data, input_frames); } } @@ -839,19 +858,19 @@ bool audio_driver_init_internal( audio_mixer_init(settings->uints.audio_output_sample_rate); #endif - if ( audio_driver_st.current_microphone && - !audio_driver_st.current_microphone->microphone_context) + if ( audio_driver_st.current_microphone.active && + !audio_driver_st.current_microphone.microphone_context) { /* If the core requested a microphone before the driver was able to provide one...*/ /* Now that the driver and driver context are ready, let's initialize the mid */ - audio_driver_init_microphone_internal(audio_driver_st.current_microphone); + audio_driver_init_microphone_internal(&audio_driver_st.current_microphone); - if (audio_driver_st.current_microphone->microphone_context) + if (audio_driver_st.current_microphone.microphone_context) RARCH_DBG("[Audio]: Initialized a previously-pending microphone\n"); else { RARCH_ERR("[Audio]: Failed to initialize a previously pending microphone; microphone will not be used\n"); - audio_driver_free_microphone(audio_driver_st.current_microphone); + audio_driver_free_microphone(&audio_driver_st.current_microphone); } } @@ -1750,20 +1769,17 @@ bool audio_driver_supports_microphone(const audio_driver_t* driver) driver->read_microphone; } -static retro_microphone_t *audio_driver_microphone_handle_init(void *microphone_context) +static void audio_driver_microphone_handle_init(retro_microphone_t *microphone, void *microphone_context) { - retro_microphone_t *microphone = malloc(sizeof(retro_microphone_t)); - if (microphone) { microphone->microphone_context = microphone_context; microphone->pending_enabled = false; + microphone->active = true; } - - return microphone; } -static void audio_driver_microphone_handle_free(retro_microphone_t* microphone) +static void audio_driver_microphone_handle_free(retro_microphone_t *microphone) { audio_driver_state_t *audio_st = &audio_driver_st; const audio_driver_t *audio_driver = audio_st->current_audio; @@ -1776,9 +1792,14 @@ static void audio_driver_microphone_handle_free(retro_microphone_t* microphone) RARCH_WARN("[Audio]: Attempted to free a microphone without an active driver context\n"); if (microphone->microphone_context) + { audio_driver->free_microphone(context, microphone->microphone_context); + microphone->microphone_context = NULL; + } - free(microphone); + microphone->active = false; + microphone->pending_enabled = false; + /* Do NOT free the microphone handle itself! It's allocated statically! */ } /** @@ -1801,17 +1822,15 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone if (!microphone || !audio_driver) return; - void *microphone_context = audio_driver->init_microphone(context, + microphone->microphone_context = audio_driver->init_microphone(context, *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, settings->uints.audio_input_sample_rate, audio_latency, settings->uints.audio_block_frames, &actual_sample_rate); - if (microphone_context) + if (microphone->microphone_context) { /* If the microphone context was successfully created... */ - microphone->microphone_context = microphone_context; - RARCH_LOG("[Audio]: Initialized microphone (sample rate: %dHz)\n", actual_sample_rate); audio_driver_set_microphone_state(microphone, microphone->pending_enabled); @@ -1821,9 +1840,9 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone } else { + audio_driver_microphone_handle_free(microphone); RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); - microphone->pending_enabled = false; - microphone->microphone_context = NULL; + microphone->pending_enabled = false; /* Disable the microphone */ } } @@ -1837,7 +1856,6 @@ retro_microphone_t *audio_driver_init_microphone(void) const settings_t *settings = config_get_ptr(); const audio_driver_t *audio_driver = audio_st->current_audio; void *context = audio_st->context_audio_data; - retro_microphone_t *microphone = NULL; if (!settings) { @@ -1864,10 +1882,10 @@ retro_microphone_t *audio_driver_init_microphone(void) return NULL; } - if (audio_st->current_microphone) + if (audio_st->current_microphone.active) { /* If the core has requested a second microphone... */ RARCH_ERR("[Audio]: Failed to initialize a second microphone, frontend only supports one at a time right now\n"); - if (audio_st->current_microphone->microphone_context) + if (audio_st->current_microphone.microphone_context) /* If that mic is initialized... */ RARCH_ERR("[Audio]: An initialized microphone exists\n"); else @@ -1881,17 +1899,14 @@ retro_microphone_t *audio_driver_init_microphone(void) * if that happens, we have to initialize the microphones later. * But the user still wants a handle, so we'll give them one. */ - microphone = audio_driver_microphone_handle_init(context); - /* If context is null, the handle won't have a valid microphone context (but we'll create one later) */ - - if (!microphone) - return NULL; + audio_driver_microphone_handle_init(&audio_st->current_microphone, context); + /* If context is NULL, the handle won't have a valid microphone context (but we'll create one later) */ if (context) { /* If the audio driver is ready to initialize a microphone... */ - audio_driver_init_microphone_internal(microphone); + audio_driver_init_microphone_internal(&audio_st->current_microphone); - if (microphone->microphone_context) /* If the microphone was successfully initialized... */ + if (audio_st->current_microphone.microphone_context) /* If the microphone was successfully initialized... */ RARCH_LOG("[Audio]: Initialized the requested microphone successfully\n"); else goto mic_context_init_failed; @@ -1901,12 +1916,9 @@ retro_microphone_t *audio_driver_init_microphone(void) RARCH_LOG("[Audio]: Microphone requested before audio context was ready; deferring initialization\n"); } - audio_st->current_microphone = microphone; - - return microphone; + return &audio_st->current_microphone; mic_context_init_failed: - audio_driver_microphone_handle_free(microphone); - audio_st->current_microphone = NULL; + audio_driver_microphone_handle_free(&audio_st->current_microphone); return NULL; } @@ -1922,9 +1934,7 @@ void audio_driver_free_microphone(retro_microphone_t *microphone) audio_driver && audio_driver->free_microphone) { - audio_driver->free_microphone(context, microphone->microphone_context); - free(microphone); - audio_st->current_microphone = NULL; + audio_driver_microphone_handle_free(microphone); } } @@ -1934,7 +1944,7 @@ bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool stat void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - if (!microphone || !audio_driver || !audio_driver->set_microphone_state) + if (!microphone || !microphone->active || !audio_driver || !audio_driver->set_microphone_state) return false; if (context && microphone->microphone_context) @@ -1967,7 +1977,7 @@ bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) void *context = audio_st->context_audio_data; const audio_driver_t *audio_driver = audio_st->current_audio; - if (!microphone || !audio_driver || !audio_driver->get_microphone_state) + if (!microphone || !microphone->active || !audio_driver || !audio_driver->get_microphone_state) return false; if (context && microphone->microphone_context) @@ -1989,6 +1999,7 @@ int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* d /* TODO: Should mic use on a pending microphone driver be an error? */ if ( !microphone || + !microphone->active || !audio_driver_alive() || !audio_driver->get_microphone_state || !audio_driver->read_microphone || diff --git a/audio/audio_driver.h b/audio/audio_driver.h index f329ac37b3fa..2026a1b8010a 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -90,6 +90,9 @@ struct retro_microphone /* May be enabled even before the driver is ready */ bool pending_enabled; + + /* true if this object represents a valid or pending microphone */ + bool active; }; typedef struct audio_driver @@ -359,7 +362,7 @@ typedef struct * but RetroArch only supports one at a time for now. * PRs welcome! */ - retro_microphone_t *current_microphone; + retro_microphone_t current_microphone; void *context_audio_data; float *input_data; #ifdef HAVE_AUDIOMIXER From c39f310e433e61fb5178b8b79cb64eca96de0fa9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 20:59:22 -0500 Subject: [PATCH 041/309] Rename sdl_audio_read to sdl_audio_read_microphone - To stay consistent with the naming --- audio/drivers/sdl_audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index d60f1bd2c073..9317880ad9f8 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -525,7 +525,7 @@ static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, return true; } -static int sdl_audio_read(void *data, void *microphone_context, void *buf, size_t size) +static int sdl_audio_read_microphone(void *data, void *microphone_context, void *buf, size_t size) { int ret = 0; sdl_audio_t *sdl = (sdl_audio_t*)data; @@ -604,7 +604,7 @@ audio_driver_t audio_sdl = { sdl_audio_free_microphone, sdl_audio_microphone_get_state, sdl_audio_microphone_set_state, - sdl_audio_read + sdl_audio_read_microphone #else NULL, /* Microphone support for this driver requires SDL 2 */ NULL, From 15030a3891455a64e281a79fae4a4d22c6a60e4b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 21:00:06 -0500 Subject: [PATCH 042/309] Log the requested and received SDL audio spec for mics --- audio/drivers/sdl_audio.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 9317880ad9f8..abc78b3a63df 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -424,7 +424,26 @@ static void *sdl_audio_init_microphone(void *data, RARCH_ERR("[SDL audio]: Failed to open SDL audio input device: %s\n", SDL_GetError()); goto error; } - RARCH_LOG("[SDL audio]: Opened SDL audio input device with ID %u\n", microphone->device_id); + RARCH_DBG("[SDL audio]: Opened SDL audio input device with ID %u\n", + microphone->device_id); + RARCH_DBG("[SDL audio]: Requested a microphone frequency of %u Hz, got %u Hz\n", + desired_spec.freq, out.freq); + RARCH_DBG("[SDL audio]: Requested %u channels for microphone, got %u\n", + desired_spec.channels, out.channels); + RARCH_DBG("[SDL audio]: Requested a %u-sample microphone buffer, got %u samples (%u bytes)\n", + frames, out.samples, out.size); + RARCH_DBG("[SDL audio]: Got a microphone silence value of %u\n", out.silence); + RARCH_DBG("[SDL audio]: Requested microphone audio format: %u-bit %s %s %s endian\n", + SDL_AUDIO_BITSIZE(desired_spec.format), + SDL_AUDIO_ISSIGNED(desired_spec.format) ? "signed" : "unsigned", + SDL_AUDIO_ISFLOAT(desired_spec.format) ? "floating-point" : "integer", + SDL_AUDIO_ISBIGENDIAN(desired_spec.format) ? "big" : "little"); + + RARCH_DBG("[SDL audio]: Received microphone audio format: %u-bit %s %s %s endian\n", + SDL_AUDIO_BITSIZE(desired_spec.format), + SDL_AUDIO_ISSIGNED(desired_spec.format) ? "signed" : "unsigned", + SDL_AUDIO_ISFLOAT(desired_spec.format) ? "floating-point" : "integer", + SDL_AUDIO_ISBIGENDIAN(desired_spec.format) ? "big" : "little"); if (new_rate) *new_rate = out.freq; From 43c101400e9d3b0a964f2ebedef1a6cd416d745e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 21:17:16 -0500 Subject: [PATCH 043/309] Halve the buffer size given to the microphone --- audio/drivers/sdl_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index abc78b3a63df..7c3c1bdc1132 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -457,7 +457,7 @@ static void *sdl_audio_init_microphone(void *data, latency, (int)(out.samples * 4 * 1000 / out.freq)); /* Create a buffer twice as big as needed and prefill the buffer. */ - bufsize = out.samples * 4 * sizeof(int16_t); + bufsize = out.samples * 2 * sizeof(int16_t); tmp = calloc(1, bufsize); microphone->sample_buffer = fifo_new(bufsize); From 93d12364cc3c9e0f76c7afc1d187ff57a7de6fa3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 21 Dec 2022 21:18:11 -0500 Subject: [PATCH 044/309] Don't unset pending_enabled twice - Why would you? --- audio/audio_driver.c | 1 - 1 file changed, 1 deletion(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index c402ef7949d7..ef01c6daa919 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1842,7 +1842,6 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone { audio_driver_microphone_handle_free(microphone); RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); - microphone->pending_enabled = false; /* Disable the microphone */ } } From 17daebc7eee981421048224bf96299aa9625be4e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 22 Dec 2022 14:52:53 -0500 Subject: [PATCH 045/309] Remove an unused attribute --- audio/drivers/sdl_audio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 7c3c1bdc1132..f891c37b8837 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -72,7 +72,6 @@ typedef struct sdl_audio_microphone #endif fifo_buffer_t *sample_buffer; - bool nonblock; bool is_paused; SDL_AudioDeviceID device_id; } sdl_audio_microphone_t; From 3c84259ad98d64e22c6941d7769591ea4358b147 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 22 Dec 2022 14:54:31 -0500 Subject: [PATCH 046/309] Add some comments - Writing these helped me understand the code --- audio/audio_driver.c | 34 +++++++++++++------ audio/audio_driver.h | 15 ++++++++ audio/drivers/sdl_audio.c | 13 +++++++ .../include/audio/audio_resampler.h | 20 +++++++++++ libretro-common/include/audio/dsp_filter.h | 3 ++ 5 files changed, 74 insertions(+), 11 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index ef01c6daa919..784cb8914855 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -438,37 +438,46 @@ static void audio_driver_flush( src_data.data_out = NULL; src_data.output_frames = 0; + /* We'll assign a proper output to the resampler later in this function */ convert_s16_to_float(audio_st->input_data, data, samples, audio_volume_gain); + /* The resampler operates on floating-point frames, + * so we gotta convert the input first */ src_data.data_in = audio_st->input_data; src_data.input_frames = samples >> 1; + /* Remember, we allocated buffers that are twice as big as needed. + * (see audio_driver_init) */ #ifdef HAVE_DSP_FILTER if (audio_st->dsp) - { + { /* If we want to process our audio for reasons besides resampling... */ struct retro_dsp_data dsp_data; - dsp_data.input = NULL; - dsp_data.input_frames = 0; - dsp_data.output = NULL; - dsp_data.output_frames = 0; - dsp_data.input = audio_st->input_data; dsp_data.input_frames = (unsigned)(samples >> 1); + dsp_data.output = NULL; + dsp_data.output_frames = 0; + /* Initialize the DSP input/output. + * Our DSP implementations generally operate directly on the input buffer, + * so the output/output_frames attributes here are zero; + * the DSP filter will set them to useful values, + * most likely to be the same as the inputs. */ retro_dsp_filter_process(audio_st->dsp, &dsp_data); if (dsp_data.output) - { + { /* If the DSP filter succeeded... */ src_data.data_in = dsp_data.output; src_data.input_frames = dsp_data.output_frames; + /* Then let's pass the DSP's output to the resampler's input */ } } #endif src_data.data_out = audio_st->output_samples_buf; + /* Now the resampler will write to the driver state's scratch buffer */ if (audio_st->flags & AUDIO_FLAG_CONTROL) { @@ -547,26 +556,29 @@ static void audio_driver_flush( } #endif + /* Now we write our processed audio output to the driver. + * It may not be played immediately, depending on the driver implementation. */ { const void *output_data = audio_st->output_samples_buf; - unsigned output_frames = (unsigned)src_data.output_frames; + unsigned output_frames = (unsigned)src_data.output_frames; /* Unit: frames */ if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) - output_frames *= sizeof(float); + output_frames *= sizeof(float); /* Unit: bytes */ else { convert_float_to_s16(audio_st->output_samples_conv_buf, (const float*)output_data, output_frames * 2); output_data = audio_st->output_samples_conv_buf; - output_frames *= sizeof(int16_t); + output_frames *= sizeof(int16_t); /* Unit: bytes */ } audio_st->current_audio->write(audio_st->context_audio_data, output_data, output_frames * 2); } - /* Only one mic is supported right now. If you add support for multiple mics, + /* Now let's process the microphone input, if any. + * Only one mic is supported right now. If you add support for multiple mics, * read through all of them here. */ if ( (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && audio_st->current_microphone.active && diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 2026a1b8010a..010b11c11eda 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -337,11 +337,22 @@ typedef struct uint64_t free_samples_count; struct string_list *devices_list; + + /** + * A scratch buffer for audio output to be processed, + * up to (but excluding) the point where it's converted to 16-bit audio + * to give to the driver. + */ float *output_samples_buf; float *input_samples_buf; #ifdef HAVE_REWIND int16_t *rewind_buf; #endif + + /** + * A scratch buffer for processed audio output to be converted to 16-bit, + * so that it can be sent to the driver. + */ int16_t *output_samples_conv_buf; int16_t *input_samples_conv_buf; #ifdef HAVE_DSP_FILTER @@ -364,6 +375,10 @@ typedef struct */ retro_microphone_t current_microphone; void *context_audio_data; + + /** + * Scratch buffer for preparing data for the resampler + */ float *input_data; #ifdef HAVE_AUDIOMIXER struct audio_mixer_stream diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index f891c37b8837..2dc1716c5208 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -71,6 +71,12 @@ typedef struct sdl_audio_microphone scond_t *cond; #endif + /** + * The queue used to store incoming samples from the driver. + * Audio from the microphone is stored here, + * the first stop before the audio driver processes it + * and makes it ready for the core. + */ fifo_buffer_t *sample_buffer; bool is_paused; SDL_AudioDeviceID device_id; @@ -85,6 +91,11 @@ typedef struct sdl_audio slock_t *lock; scond_t *cond; #endif + /** + * The queue used to store outgoing samples to be played by the driver. + * Audio from the core ultimately makes its way here, + * the last stop before the driver plays it. + */ fifo_buffer_t *speaker_buffer; bool nonblock; bool is_paused; @@ -119,6 +130,8 @@ static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) sdl_audio_t *sdl = (sdl_audio_t*)data; size_t avail = FIFO_WRITE_AVAIL(sdl->microphone->sample_buffer); size_t read_size = len > (int)avail ? avail : len; + /* If the sample buffer is almost full, + * just write as much as we can into it*/ fifo_write(sdl->microphone->sample_buffer, stream, read_size); #ifdef HAVE_THREADS diff --git a/libretro-common/include/audio/audio_resampler.h b/libretro-common/include/audio/audio_resampler.h index d728bf58d9eb..57e21fee1108 100644 --- a/libretro-common/include/audio/audio_resampler.h +++ b/libretro-common/include/audio/audio_resampler.h @@ -65,12 +65,32 @@ typedef unsigned resampler_simd_mask_t; #define RESAMPLER_API_VERSION 1 +/** + * A struct that groups the input and output of a resampler. + */ struct resampler_data { + /** + * The buffer containing the data to be resampled. + */ const float *data_in; + + /** + * The buffer that will be used to store resampled output. + * Must be allocated in advance, and must not be the same as data_in. + */ float *data_out; + /** + * The size of ::data_in, in frames (\em not bytes or samples). + * For example, 32-bit stereo frames would consist of 8 bytes + * (two 4-byte floats per frame). + */ size_t input_frames; + + /** + * The size of data_out, in frames (\em not bytes or samples); + */ size_t output_frames; double ratio; diff --git a/libretro-common/include/audio/dsp_filter.h b/libretro-common/include/audio/dsp_filter.h index 172c29a7535d..fc5be2094cfe 100644 --- a/libretro-common/include/audio/dsp_filter.h +++ b/libretro-common/include/audio/dsp_filter.h @@ -34,6 +34,9 @@ retro_dsp_filter_t *retro_dsp_filter_new(const char *filter_config, void retro_dsp_filter_free(retro_dsp_filter_t *dsp); +/** + * A struct that groups the input and output of a DSP filter. + */ struct retro_dsp_data { float *input; From bf4c92f0e7b3c9c711324d711bc09e0a571b4758 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 22 Dec 2022 16:42:21 -0500 Subject: [PATCH 047/309] Fix incorrect behavior - sdl_audio_read_microphone should've been returning a count of samples, not bytes --- audio/audio_driver.h | 2 +- audio/drivers/sdl_audio.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 010b11c11eda..9f19262c4e12 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -312,7 +312,7 @@ typedef struct audio_driver * @param[out] buf Buffer for received audio data. * Should be large enough to hold one frame's worth of audio samples. * @param[in] size Size of audio buffer, in samples (\em not bytes). - * @return The number of bytes that were read into \c buf, + * @return The number of samples that were read into \c buf, * or -1 if there was an error. */ int (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 2dc1716c5208..c6ad6b472a66 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -607,7 +607,9 @@ static int sdl_audio_read_microphone(void *data, void *microphone_context, void ret = read; } - return ret; + return ret / sizeof(int16_t); + /* Because the function should return the number of *samples* processed, not bytes + * (which is what the FIFO queues operate on) */ } #endif From a0331d160203d0ed976523b37f4ba86455dc0e2b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 22 Dec 2022 16:46:10 -0500 Subject: [PATCH 048/309] Optimize reading microphone input - Previously, audio_driver_get_microphone_input would call audio_driver->read_microphone - audio_driver->read_microphone may use locks or synchronization - Now, audio_driver_get_microphone_input is only called within audio_driver_flush --- audio/audio_driver.c | 124 ++++++++++++++++++++++++++++++------------- audio/audio_driver.h | 20 +++++++ 2 files changed, 107 insertions(+), 37 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 784cb8914855..635f96eda558 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -583,27 +583,53 @@ static void audio_driver_flush( if ( (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && audio_st->current_microphone.active && audio_st->current_microphone.microphone_context && + audio_st->current_microphone.sample_buffer && + audio_st->current_microphone.sample_buffer_length && audio_st->current_audio->read_microphone && audio_st->current_audio->get_microphone_state && audio_st->current_audio->get_microphone_state(audio_st->current_audio, audio_st->current_microphone.microphone_context)) { /* If mic support is enabled and available, and the mic is enabled... */ - void *input_data = audio_st->input_data; - unsigned input_frames = (unsigned)src_data.input_frames; - - if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) - input_frames *= sizeof(float); + void *input_data = audio_st->input_samples_buf; + unsigned input_frames = (unsigned)src_data.input_frames; + retro_microphone_t *microphone = &audio_st->current_microphone; + size_t sample_buffer_length = microphone->sample_buffer_length; + ssize_t samples_read = audio_st->current_audio->read_microphone( + audio_st->context_audio_data, + microphone->microphone_context, + input_data, + input_frames); + /* First, get the most recent mic data */ + + if (samples_read < 0) + { /* If there was an error... */ + microphone->error = true; + microphone->most_recent_read_length = 0; + } + else if (samples_read == 0) + { + microphone->error = false; + microphone->most_recent_read_length = 0; + } else { - convert_float_to_s16(audio_st->input_samples_conv_buf, - (const float*)input_data, input_frames); + microphone->error = false; + microphone->most_recent_read_length = samples_read; - input_data = audio_st->input_samples_conv_buf; - input_frames *= sizeof(int16_t); - } + if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) + input_frames *= sizeof(float); + else + { + convert_float_to_s16(audio_st->input_samples_conv_buf, + (const float*)input_data, input_frames); - audio_st->current_audio->read_microphone(audio_st->context_audio_data, - audio_st->current_microphone.microphone_context, input_data, input_frames); + input_data = audio_st->input_samples_conv_buf; + input_frames *= sizeof(int16_t); + } + + memcpy(microphone->sample_buffer, input_data, + MIN(input_frames, sample_buffer_length)); + } } } @@ -1785,9 +1811,13 @@ static void audio_driver_microphone_handle_init(retro_microphone_t *microphone, { if (microphone) { - microphone->microphone_context = microphone_context; - microphone->pending_enabled = false; - microphone->active = true; + microphone->microphone_context = microphone_context; + microphone->pending_enabled = false; + microphone->active = true; + microphone->sample_buffer = NULL; + microphone->sample_buffer_length = 0; + microphone->most_recent_read_length = 0; + microphone->error = false; } } @@ -1809,8 +1839,13 @@ static void audio_driver_microphone_handle_free(retro_microphone_t *microphone) microphone->microphone_context = NULL; } - microphone->active = false; - microphone->pending_enabled = false; + if (microphone->sample_buffer) + { + memalign_free(microphone->sample_buffer); + microphone->sample_buffer = NULL; + } + + memset(microphone, 0, sizeof(*microphone)); /* Do NOT free the microphone handle itself! It's allocated statically! */ } @@ -1830,10 +1865,21 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone unsigned actual_sample_rate = 0; unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? runloop_audio_latency : setting_audio_latency; + float slowmotion_ratio = settings->floats.slowmotion_ratio; + size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; if (!microphone || !audio_driver) return; + microphone->sample_buffer_length = insamples_max * sizeof(int16_t); + microphone->error = false; + microphone->most_recent_read_length = 0; + microphone->sample_buffer = + (int16_t*)memalign_alloc(64, microphone->sample_buffer_length); + + if (!microphone->sample_buffer) + goto error; + microphone->microphone_context = audio_driver->init_microphone(context, *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, settings->uints.audio_input_sample_rate, @@ -1841,20 +1887,20 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone settings->uints.audio_block_frames, &actual_sample_rate); - if (microphone->microphone_context) - { /* If the microphone context was successfully created... */ - RARCH_LOG("[Audio]: Initialized microphone (sample rate: %dHz)\n", actual_sample_rate); + if (!microphone->microphone_context) + goto error; - audio_driver_set_microphone_state(microphone, microphone->pending_enabled); + audio_driver_set_microphone_state(microphone, microphone->pending_enabled); - if (actual_sample_rate != 0) - configuration_set_uint(settings, settings->uints.audio_input_sample_rate, actual_sample_rate); - } - else - { - audio_driver_microphone_handle_free(microphone); - RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); - } + if (actual_sample_rate != 0) + configuration_set_uint(settings, settings->uints.audio_input_sample_rate, actual_sample_rate); + + RARCH_LOG("[Audio]: Initialized microphone (sample rate: %dHz)\n", actual_sample_rate); + + return; +error: + audio_driver_microphone_handle_free(microphone); + RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); } /* NOTE: The core may request a microphone before the driver is ready. @@ -2004,23 +2050,27 @@ bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length) { - audio_driver_state_t *audio_st = &audio_driver_st; - void *context = audio_st->context_audio_data; - const audio_driver_t *audio_driver = audio_st->current_audio; - /* TODO: Should mic use on a pending microphone driver be an error? */ if ( !microphone || + microphone->error || !microphone->active || + !microphone->microphone_context || + !microphone->sample_buffer || + !microphone->sample_buffer_length || !audio_driver_alive() || - !audio_driver->get_microphone_state || - !audio_driver->read_microphone || - !microphone->microphone_context) + !audio_driver_get_microphone_state(microphone)) return -1; /* If the provided mic is invalid, * or the audio driver isn't active, * or mic support isn't fully implemented... */ - return audio_driver->read_microphone(context, microphone->microphone_context, data, data_length); + if (microphone->most_recent_read_length == 0) + return 0; + + int copy_length = MIN(data_length, microphone->most_recent_read_length); + memcpy(data, microphone->sample_buffer, copy_length * 2); + + return copy_length; } #ifdef HAVE_REWIND diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 9f19262c4e12..4b67b101b910 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -88,11 +88,31 @@ struct retro_microphone */ void *microphone_context; + /** + * Pointer to the data that will be copied to cores. + */ + int16_t* sample_buffer; + + /** + * Length of sample_buffer in samples, \em not bytes. + */ + size_t sample_buffer_length; + + /** + * Length of the most recent read into sample_buffer, in samples. + */ + size_t most_recent_read_length; + /* May be enabled even before the driver is ready */ bool pending_enabled; /* true if this object represents a valid or pending microphone */ bool active; + + /** + * True if the most recent read attempt ended in an error. + * Mostly just used */ + bool error; }; typedef struct audio_driver From 73c4c4982271061111bc9162461ec22d45a987d9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 22 Dec 2022 16:47:42 -0500 Subject: [PATCH 049/309] audio_driver.read_microphone now returns a ssize_t - To be consistent with audio_driver.write --- audio/audio_driver.h | 2 +- audio/drivers/sdl_audio.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 4b67b101b910..3334e5dfdd35 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -335,7 +335,7 @@ typedef struct audio_driver * @return The number of samples that were read into \c buf, * or -1 if there was an error. */ - int (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); + ssize_t (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); } audio_driver_t; enum audio_driver_state_flags diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index c6ad6b472a66..719dd95b9d9f 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -556,9 +556,9 @@ static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, return true; } -static int sdl_audio_read_microphone(void *data, void *microphone_context, void *buf, size_t size) +static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, void *buf, size_t size) { - int ret = 0; + ssize_t ret = 0; sdl_audio_t *sdl = (sdl_audio_t*)data; sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t*)microphone_context; From d0cc143c397232fcb058b2bf4853efc27a6905f8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 22 Dec 2022 16:49:21 -0500 Subject: [PATCH 050/309] Add skeletal implementations of mic functions to audio_thread driver - Not thread-safe yet - May break in unpredictable ways --- audio/audio_thread_wrapper.c | 85 ++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/audio/audio_thread_wrapper.c b/audio/audio_thread_wrapper.c index e715bba4b41f..f83a4306980f 100644 --- a/audio/audio_thread_wrapper.c +++ b/audio/audio_thread_wrapper.c @@ -250,15 +250,86 @@ static ssize_t audio_thread_write(void *data, const void *buf, size_t size) return ret; } -static ssize_t audio_thread_read(void *data, void *buf, size_t size) +static void *audio_thread_init_microphone(void *data, const char *device, unsigned rate, + unsigned latency, unsigned block_frames, unsigned *new_rate) { - ssize_t ret; + // TODO: Implement properly + audio_thread_t *thr = (audio_thread_t*)data; + void *microphone = NULL; + + if (!thr) + return NULL; + + if ( thr->driver && + thr->driver_data && + thr->driver->init_microphone) + microphone = thr->driver->init_microphone(thr->driver_data, + device, + rate, + latency, + block_frames, + new_rate + ); + + return microphone; +} + +static void audio_thread_free_microphone(void *data, void *microphone_context) +{ + // TODO: Implement properly + audio_thread_t *thr = (audio_thread_t*)data; + + if (!thr) + return; + + if ( thr->driver && + thr->driver_data && + thr->driver->free_microphone && + microphone_context) + thr->driver->free_microphone(thr->driver_data, microphone_context); +} + +static bool audio_thread_get_microphone_state(const void *data, const void *microphone_context) +{ + // TODO: Implement properly + audio_thread_t *thr = (audio_thread_t*)data; + + if (!thr) + return false; + + if ( thr->driver && + thr->driver_data && + thr->driver->get_microphone_state) + return thr->driver->get_microphone_state(thr->driver_data, microphone_context); + + return false; +} + +static bool audio_thread_set_microphone_state(void *data, void *microphone_context, bool enabled) +{ + // TODO: Implement properly + audio_thread_t *thr = (audio_thread_t*)data; + + if (!thr) + return false; + + if ( thr->driver && + thr->driver_data && + thr->driver->set_microphone_state) + return thr->driver->set_microphone_state(thr->driver_data, microphone_context, enabled); + + return false; +} + +static ssize_t audio_thread_read_microphone(void *data, void *microphone_context, void *buf, size_t size) +{ + int ret; audio_thread_t *thr = (audio_thread_t*)data; if (!thr) return 0; - ret = thr->driver->read(thr->driver_data, buf, size); + ret = thr->driver->read_microphone(thr->driver_data, microphone_context, buf, size); if (ret < 0) { @@ -274,7 +345,6 @@ static ssize_t audio_thread_read(void *data, void *buf, size_t size) static const audio_driver_t audio_thread = { NULL, audio_thread_write, - audio_thread_read, audio_thread_stop, audio_thread_start, audio_thread_alive, @@ -284,6 +354,13 @@ static const audio_driver_t audio_thread = { "audio-thread", NULL, /* No point in using rate control with threaded audio. */ NULL, + NULL, + NULL, + audio_thread_init_microphone, + audio_thread_free_microphone, + audio_thread_get_microphone_state, + audio_thread_set_microphone_state, + audio_thread_read_microphone }; /** From dee3df16693639f39a344180a0c2dab1fe939841 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 22 Dec 2022 16:49:39 -0500 Subject: [PATCH 051/309] Update a comment --- audio/audio_driver.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 3334e5dfdd35..95d29cc969c5 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -325,6 +325,10 @@ typedef struct audio_driver * Unless said otherwise with set_nonblock_state(), all reads * are blocking, and it should block till it has read all frames. * + * This function is not exposed to the core. + * Instead, the function exposed to the core just copies + * the most recent data that was read by this + * * @param[in] driver_context Opaque handle to the audio driver context * that was used to create the provided microphone. * @param[in] microphone_context Opaque handle to the microphone From 11bca8c789c0d76828d45ef3cf9f8805e43b4258 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 23 Dec 2022 18:15:47 -0500 Subject: [PATCH 052/309] Update audio_driver.c --- audio/audio_driver.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 635f96eda558..0dadcaca4c3a 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -422,6 +422,14 @@ bool audio_driver_find_driver( * and reads samples from the driver's input * (if mic support is enabled). * Will first perform DSP processing (if enabled) and resampling. + * + * @param audio_st The overall state of the audio driver. + * @param slowmotion_ratio TODO + * @param audio_fastforward_mute True if no audio should be output while the game is in fast-forward. + * @param data Audio output data that was most recently provided by the core. + * @param samples The size of data, in TODO + * @param is_slowmotion True if the player is currently running the game in slow motion. + * @param is_fastmotion True if the player is currently running the game in fast-forward. **/ static void audio_driver_flush( audio_driver_state_t *audio_st, @@ -617,18 +625,21 @@ static void audio_driver_flush( microphone->most_recent_read_length = samples_read; if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) - input_frames *= sizeof(float); - else { + input_frames *= sizeof(float); + input_data = audio_st->input_samples_conv_buf; convert_float_to_s16(audio_st->input_samples_conv_buf, (const float*)input_data, input_frames); - - input_data = audio_st->input_samples_conv_buf; + // TODO: Write a mono version of this + } + else + { input_frames *= sizeof(int16_t); } memcpy(microphone->sample_buffer, input_data, MIN(input_frames, sample_buffer_length)); + /* Copy the data we read from the mic into the buffer that the core reads from */ } } } From 0760bfb4573f91a216508fe4449d244d29993e79 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 10 Jan 2023 18:56:24 -0500 Subject: [PATCH 053/309] Fix some logging calls --- audio/drivers/sdl_audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 719dd95b9d9f..ac463bd1efc4 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -473,7 +473,7 @@ static void *sdl_audio_init_microphone(void *data, tmp = calloc(1, bufsize); microphone->sample_buffer = fifo_new(bufsize); - RARCH_DBG("[SDL audio]: Initialized microphone sample buffer with %u bytes\n", bufsize); + RARCH_DBG("[SDL audio]: Initialized microphone sample queue with %u bytes\n", bufsize); if (tmp) { @@ -551,7 +551,7 @@ static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, { /* If the entire audio driver isn't paused... */ SDL_PauseAudioDevice(microphone->device_id, microphone->is_paused); } - RARCH_LOG("[SDL audio]: Set state of microphone %u to %s\n", + RARCH_DBG("[SDL audio]: Set state of microphone %u to %s\n", microphone->device_id, enabled ? "enabled" : "disabled"); return true; } From 541668c8de5b4641b3de3fbfc21cd08756f98fd5 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 10 Jan 2023 18:57:06 -0500 Subject: [PATCH 054/309] Use the microphone's synchronization objects instead of the driver's - That probably explains the freezing I've been getting --- audio/drivers/sdl_audio.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index ac463bd1efc4..b9ca2acb615b 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -417,6 +417,15 @@ static void *sdl_audio_init_microphone(void *data, if (!microphone) return NULL; + { + int i; + int num_available_microphones = SDL_GetNumAudioDevices(true); + RARCH_DBG("[SDL audio]: %d audio capture devices found:\n", num_available_microphones); + for (i = 0; i < num_available_microphones; ++i) { + RARCH_DBG("[SDL audio]: - %s\n", SDL_GetAudioDeviceName(i, true)); + } + } + /* We have to buffer up some data ourselves, so we let SDL * carry approximately half of the latency. * @@ -591,9 +600,9 @@ static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, v { SDL_UnlockAudioDevice(microphone->device_id); #ifdef HAVE_THREADS - slock_lock(sdl->lock); - scond_wait(sdl->cond, sdl->lock); - slock_unlock(sdl->lock); + slock_lock(microphone->lock); + scond_wait(microphone->cond, microphone->lock); + slock_unlock(microphone->lock); #endif } else From 30e59b1262794c386290da60a918763b9ae54ff3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 10 Jan 2023 18:59:41 -0500 Subject: [PATCH 055/309] Only print SDL audio devices if verbose logging is enabled --- audio/drivers/sdl_audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index b9ca2acb615b..0613bdbadbe7 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -417,7 +417,8 @@ static void *sdl_audio_init_microphone(void *data, if (!microphone) return NULL; - { + if (verbosity_is_enabled()) + { /* Only print SDL audio devices if verbose logging is enabled */ int i; int num_available_microphones = SDL_GetNumAudioDevices(true); RARCH_DBG("[SDL audio]: %d audio capture devices found:\n", num_available_microphones); From c3a47da62b8d0082fed024f49e640bcd6538bdbc Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 12:27:51 -0500 Subject: [PATCH 056/309] Add comments to sdl_audio_write - They helped me understand how it works --- audio/drivers/sdl_audio.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 0613bdbadbe7..3cc75da3f01e 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -246,41 +246,49 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size) sdl_audio_t *sdl = (sdl_audio_t*)data; if (sdl->nonblock) - { + { /* If we shouldn't wait for space in a full outgoing sample queue... */ size_t avail, write_amt; - SDL_LockAudioDevice(sdl->speaker_device); + SDL_LockAudioDevice(sdl->speaker_device); /* Stop the SDL speaker thread from running */ avail = FIFO_WRITE_AVAIL(sdl->speaker_buffer); - write_amt = avail > size ? size : avail; + write_amt = avail > size ? size : avail; /* Enqueue as much data as we can */ fifo_write(sdl->speaker_buffer, buf, write_amt); - SDL_UnlockAudioDevice(sdl->speaker_device); - ret = write_amt; + SDL_UnlockAudioDevice(sdl->speaker_device); /* Let the speaker thread run again */ + ret = write_amt; /* If the queue was full...well, too bad. */ } else { size_t written = 0; while (written < size) - { + { /* Until we've written all the sample data we have available... */ size_t avail; - SDL_LockAudioDevice(sdl->speaker_device); + SDL_LockAudioDevice(sdl->speaker_device); /* Stop the SDL speaker thread from running */ avail = FIFO_WRITE_AVAIL(sdl->speaker_buffer); if (avail == 0) - { + { /* If the outgoing sample queue is full... */ SDL_UnlockAudioDevice(sdl->speaker_device); + /* Let the SDL speaker thread run so it can play the enqueued samples, + * which will free up space for us to write new ones. */ #ifdef HAVE_THREADS slock_lock(sdl->lock); + /* Let *only* the SDL speaker thread touch the outgoing sample queue */ + scond_wait(sdl->cond, sdl->lock); + /* Block until SDL tells us that it's made room for new samples */ + slock_unlock(sdl->lock); + /* Now let this thread use the outgoing sample queue (which we'll do next iteration) */ #endif } else { size_t write_amt = size - written > avail ? avail : size - written; fifo_write(sdl->speaker_buffer, (const char*)buf + written, write_amt); - SDL_UnlockAudioDevice(sdl->speaker_device); + /* Enqueue as many samples as we have available without overflowing the queue */ + SDL_UnlockAudioDevice(sdl->speaker_device); /* Let the SDL speaker thread run again */ written += write_amt; } } From 00390c6db0ceaac0a586308f162805c0641b8ed1 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 12:28:45 -0500 Subject: [PATCH 057/309] Log info about the opened speaker in sdl_audio_init --- audio/drivers/sdl_audio.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 3cc75da3f01e..06b221eaff64 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -211,6 +211,26 @@ static void *sdl_audio_init(const char *device, } *new_rate = out.freq; + RARCH_DBG("[SDL audio]: Opened SDL audio out device with ID %u\n", + sdl->speaker_device); + RARCH_DBG("[SDL audio]: Requested a speaker frequency of %u Hz, got %u Hz\n", + spec.freq, out.freq); + RARCH_DBG("[SDL audio]: Requested %u channels for speaker, got %u\n", + spec.channels, out.channels); + RARCH_DBG("[SDL audio]: Requested a %u-frame speaker buffer, got %u frames (%u bytes)\n", + frames, out.samples, out.size); + RARCH_DBG("[SDL audio]: Got a speaker silence value of %u\n", out.silence); + RARCH_DBG("[SDL audio]: Requested speaker audio format: %u-bit %s %s %s endian\n", + SDL_AUDIO_BITSIZE(spec.format), + SDL_AUDIO_ISSIGNED(spec.format) ? "signed" : "unsigned", + SDL_AUDIO_ISFLOAT(spec.format) ? "floating-point" : "integer", + SDL_AUDIO_ISBIGENDIAN(spec.format) ? "big" : "little"); + + RARCH_DBG("[SDL audio]: Received speaker audio format: %u-bit %s %s %s endian\n", + SDL_AUDIO_BITSIZE(spec.format), + SDL_AUDIO_ISSIGNED(spec.format) ? "signed" : "unsigned", + SDL_AUDIO_ISFLOAT(spec.format) ? "floating-point" : "integer", + SDL_AUDIO_ISBIGENDIAN(spec.format) ? "big" : "little"); #ifdef HAVE_THREADS sdl->lock = slock_new(); @@ -231,6 +251,8 @@ static void *sdl_audio_init(const char *device, free(tmp); } + RARCH_DBG("[SDL audio]: Initialized speaker sample queue with %u bytes\n", bufsize); + SDL_PauseAudioDevice(sdl->speaker_device, false); return sdl; From 54ff8615ab3cba38cecd0738ffae30b16ce17bc4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:42:27 -0500 Subject: [PATCH 058/309] Update some comments for clarity --- audio/audio_driver.c | 2 +- audio/drivers/sdl_audio.c | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 0dadcaca4c3a..5befbc6e90a0 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -427,7 +427,7 @@ bool audio_driver_find_driver( * @param slowmotion_ratio TODO * @param audio_fastforward_mute True if no audio should be output while the game is in fast-forward. * @param data Audio output data that was most recently provided by the core. - * @param samples The size of data, in TODO + * @param samples The size of data, in samples. * @param is_slowmotion True if the player is currently running the game in slow motion. * @param is_fastmotion True if the player is currently running the game in fast-forward. **/ diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 06b221eaff64..1cac51d3a41c 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -606,10 +606,10 @@ static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, v return -1; if (sdl->nonblock) - { + { /* If we shouldn't block on an empty queue... */ size_t avail, read_amt; - SDL_LockAudioDevice(microphone->device_id); + SDL_LockAudioDevice(microphone->device_id); /* Stop the SDL mic thread */ avail = FIFO_READ_AVAIL(microphone->sample_buffer); read_amt = avail > size ? size : avail; fifo_read(microphone->sample_buffer, buf, read_amt); @@ -628,19 +628,28 @@ static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, v avail = FIFO_READ_AVAIL(microphone->sample_buffer); if (avail == 0) - { + { /* If the incoming sample queue is empty... */ SDL_UnlockAudioDevice(microphone->device_id); + /* Let the SDL microphone thread run so it can push some incoming samples */ #ifdef HAVE_THREADS slock_lock(microphone->lock); + /* Let *only* the SDL microphone thread access the incoming sample queue. */ + scond_wait(microphone->cond, microphone->lock); + /* Wait until the SDL microphone thread tells us it's added some samples. */ + slock_unlock(microphone->lock); + /* Allow this thread to access the incoming sample queue, which we'll do next iteration */ #endif } else { size_t read_amt = size - read > avail ? avail : size - read; fifo_read(microphone->sample_buffer, buf + read, read_amt); + /* Read as many samples as we have available without underflowing the queue */ + SDL_UnlockAudioDevice(microphone->device_id); + /* Let the SDL microphone thread run again */ read += read_amt; } } From 61bc9df7b30a9ab5591c3e46f49afbbc0949c203 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:42:46 -0500 Subject: [PATCH 059/309] Update some more comments --- audio/drivers/sdl_audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 1cac51d3a41c..2b0110a5b786 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -621,10 +621,11 @@ static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, v size_t read = 0; while (read < size) - { + { /* Until we've given the caller as much data as they've asked for... */ size_t avail; SDL_LockAudioDevice(microphone->device_id); + /* Stop the SDL microphone thread from running */ avail = FIFO_READ_AVAIL(microphone->sample_buffer); if (avail == 0) From 152fd35ef552b7dc799f0e909bac0f5990eaa9e3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:43:11 -0500 Subject: [PATCH 060/309] Update more comments --- audio/audio_driver.h | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 95d29cc969c5..8dd465a04bcd 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -94,24 +94,26 @@ struct retro_microphone int16_t* sample_buffer; /** - * Length of sample_buffer in samples, \em not bytes. + * Length of sample_buffer in bytes, \em not samples. */ size_t sample_buffer_length; /** - * Length of the most recent read into sample_buffer, in samples. - */ - size_t most_recent_read_length; + * Number of bytes that were copied into the buffer in the most recent flush. + * Accounts for resampling + **/ + size_t most_recent_copy_length; /* May be enabled even before the driver is ready */ bool pending_enabled; - /* true if this object represents a valid or pending microphone */ + /** + * True if this object represents a valid or pending microphone. + * Mostly exists because retro_microphone is statically allocated, + * so there's no reason to check it against NULL. + */ bool active; - /** - * True if the most recent read attempt ended in an error. - * Mostly just used */ bool error; }; @@ -335,8 +337,9 @@ typedef struct audio_driver * whose input will be received. * @param[out] buf Buffer for received audio data. * Should be large enough to hold one frame's worth of audio samples. - * @param[in] size Size of audio buffer, in samples (\em not bytes). - * @return The number of samples that were read into \c buf, + * @param[in] size Size of audio buffer, in bytes (\em not samples). + * Don't ask for more than you need per frame. + * @return The number of bytes that were read into \c buf, * or -1 if there was an error. */ ssize_t (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); From da5fb1d0aee0f2cea7acfc1eec906451d20bb754 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:43:56 -0500 Subject: [PATCH 061/309] Add length fields for certain buffers --- audio/audio_driver.c | 14 ++++++++------ audio/audio_driver.h | 4 ++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 5befbc6e90a0..4c56f5d5b1dd 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -710,12 +710,14 @@ bool audio_driver_init_internal( memset(audio_buf, 0, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float)); - audio_driver_st.input_data = audio_buf; - audio_driver_st.output_samples_conv_buf = out_conv_buf; - audio_driver_st.input_samples_conv_buf = in_conv_buf; - audio_driver_st.chunk_block_size = AUDIO_CHUNK_SIZE_BLOCKING; - audio_driver_st.chunk_nonblock_size = AUDIO_CHUNK_SIZE_NONBLOCKING; - audio_driver_st.chunk_size = audio_driver_st.chunk_block_size; + audio_driver_st.input_data = audio_buf; + audio_driver_st.output_samples_conv_buf = out_conv_buf; + audio_driver_st.output_samples_conv_buf_length = outsamples_max * sizeof(int16_t); + audio_driver_st.input_samples_conv_buf = in_conv_buf; + audio_driver_st.input_samples_conv_buf_length = (in_conv_buf) ? insamples_max * sizeof(int16_t) : 0; + audio_driver_st.chunk_block_size = AUDIO_CHUNK_SIZE_BLOCKING; + audio_driver_st.chunk_nonblock_size = AUDIO_CHUNK_SIZE_NONBLOCKING; + audio_driver_st.chunk_size = audio_driver_st.chunk_block_size; #ifdef HAVE_REWIND /* Needs to be able to hold full content of a full max_bufsamples diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 8dd465a04bcd..128dd15720f7 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -371,7 +371,9 @@ typedef struct * to give to the driver. */ float *output_samples_buf; + size_t output_samples_buf_length; float *input_samples_buf; + size_t input_samples_buf_length; #ifdef HAVE_REWIND int16_t *rewind_buf; #endif @@ -381,7 +383,9 @@ typedef struct * so that it can be sent to the driver. */ int16_t *output_samples_conv_buf; + size_t output_samples_conv_buf_length; int16_t *input_samples_conv_buf; + size_t input_samples_conv_buf_length; #ifdef HAVE_DSP_FILTER retro_dsp_filter_t *dsp; #endif From d14f154936582cb788b0d1778ae8f3d6046823ce Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:44:18 -0500 Subject: [PATCH 062/309] Add a utility function --- audio/audio_driver.c | 6 ++++++ audio/audio_driver.h | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 4c56f5d5b1dd..bc8b5ee03d2f 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -194,6 +194,12 @@ const char *config_get_audio_driver_options(void) return char_list_new_special(STRING_LIST_AUDIO_DRIVERS, NULL); } +unsigned audio_driver_get_sample_size(void) +{ + audio_driver_state_t *audio_st = &audio_driver_st; + return (audio_st->flags & AUDIO_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t); +} + #ifdef HAVE_TRANSLATE /* TODO/FIXME - Doesn't currently work. Fix this. */ bool audio_driver_is_ai_service_speech_running(void) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 128dd15720f7..e0a6c8de7816 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -514,6 +514,17 @@ bool audio_driver_start(bool is_shutdown); bool audio_driver_stop(void); +/** + * If you need to query the size of audio samples, + * use this function instead of checking the flags directly. + * + * @return The size of a single audio sample in bytes, + * as determined by the presence of the \c AUDIO_FLAG_USE_FLOAT flag. + * Will currently return either 2 (for \c uint16_t) or 4 (for \c float), + * although this may change if we add support for more sample types. + */ +unsigned audio_driver_get_sample_size(void); + /** * * @param driver The audio driver whose microphone support you're querying From a99ae4810bc7d8d8952238820fe83f4f15c79ea7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:47:13 -0500 Subject: [PATCH 063/309] Move mic audio processing to a separate function --- audio/audio_driver.c | 247 +++++++++++++++++++++++++++---------------- audio/audio_driver.h | 2 +- 2 files changed, 157 insertions(+), 92 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index bc8b5ee03d2f..4857c2e6fcf5 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -420,13 +420,7 @@ bool audio_driver_find_driver( } /** - * audio_driver_flush: - * @data : pointer to audio buffer. - * @right : amount of samples to write. - * - * Writes audio samples to audio driver's output, - * and reads samples from the driver's input - * (if mic support is enabled). + * Writes audio samples to audio driver's output. * Will first perform DSP processing (if enabled) and resampling. * * @param audio_st The overall state of the audio driver. @@ -590,64 +584,6 @@ static void audio_driver_flush( audio_st->current_audio->write(audio_st->context_audio_data, output_data, output_frames * 2); } - - /* Now let's process the microphone input, if any. - * Only one mic is supported right now. If you add support for multiple mics, - * read through all of them here. */ - if ( (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && - audio_st->current_microphone.active && - audio_st->current_microphone.microphone_context && - audio_st->current_microphone.sample_buffer && - audio_st->current_microphone.sample_buffer_length && - audio_st->current_audio->read_microphone && - audio_st->current_audio->get_microphone_state && - audio_st->current_audio->get_microphone_state(audio_st->current_audio, - audio_st->current_microphone.microphone_context)) - { /* If mic support is enabled and available, and the mic is enabled... */ - void *input_data = audio_st->input_samples_buf; - unsigned input_frames = (unsigned)src_data.input_frames; - retro_microphone_t *microphone = &audio_st->current_microphone; - size_t sample_buffer_length = microphone->sample_buffer_length; - ssize_t samples_read = audio_st->current_audio->read_microphone( - audio_st->context_audio_data, - microphone->microphone_context, - input_data, - input_frames); - /* First, get the most recent mic data */ - - if (samples_read < 0) - { /* If there was an error... */ - microphone->error = true; - microphone->most_recent_read_length = 0; - } - else if (samples_read == 0) - { - microphone->error = false; - microphone->most_recent_read_length = 0; - } - else - { - microphone->error = false; - microphone->most_recent_read_length = samples_read; - - if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) - { - input_frames *= sizeof(float); - input_data = audio_st->input_samples_conv_buf; - convert_float_to_s16(audio_st->input_samples_conv_buf, - (const float*)input_data, input_frames); - // TODO: Write a mono version of this - } - else - { - input_frames *= sizeof(int16_t); - } - - memcpy(microphone->sample_buffer, input_data, - MIN(input_frames, sample_buffer_length)); - /* Copy the data we read from the mic into the buffer that the core reads from */ - } - } } static void audio_driver_init_microphone_internal(retro_microphone_t* microphone); @@ -884,9 +820,11 @@ bool audio_driver_init_internal( if (!out_samples_buf || ((audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) && !in_samples_buf)) goto error; - audio_driver_st.output_samples_buf = (float*)out_samples_buf; - audio_driver_st.input_samples_buf = (float*)in_samples_buf; - audio_driver_st.flags &= ~AUDIO_FLAG_CONTROL; + audio_driver_st.output_samples_buf = (float*)out_samples_buf; + audio_driver_st.output_samples_buf_length = outsamples_max * sizeof(float); + audio_driver_st.input_samples_buf = (float*)in_samples_buf; + audio_driver_st.input_samples_buf_length = (in_samples_buf) ? insamples_max * sizeof(float) : 0; + audio_driver_st.flags &= ~AUDIO_FLAG_CONTROL; if ( !audio_cb_inited @@ -1835,7 +1773,7 @@ static void audio_driver_microphone_handle_init(retro_microphone_t *microphone, microphone->active = true; microphone->sample_buffer = NULL; microphone->sample_buffer_length = 0; - microphone->most_recent_read_length = 0; + microphone->most_recent_copy_length = 0; microphone->error = false; } } @@ -1890,10 +1828,9 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone if (!microphone || !audio_driver) return; - microphone->sample_buffer_length = insamples_max * sizeof(int16_t); - microphone->error = false; - microphone->most_recent_read_length = 0; - microphone->sample_buffer = + microphone->sample_buffer_length = insamples_max * sizeof(int16_t); + microphone->error = false; + microphone->sample_buffer = (int16_t*)memalign_alloc(64, microphone->sample_buffer_length); if (!microphone->sample_buffer) @@ -2067,29 +2004,157 @@ bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) } } -int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length) +/** + * Pull queued microphone samples from the driver + * and copy them to the provided buffer(s). + * + * Only one mic is supported right now, + * but the API is designed to accommodate multiple. + * If multi-mic support is implemented, + * you'll want to update this function. + * + * Note that microphone samples are provided in mono, + * so a "sample" and a "frame" are equivalent here. + * + * @param audio_st The overall state of the audio driver. + * @param slowmotion_ratio TODO + * @param audio_fastforward_mute True if no audio should be input while the game is in fast-forward. + * @param[out] frames The buffer in which the core will receive microphone samples. + * @param num_frames The size of \c frames, in samples. + * @param is_slowmotion True if the player is running the core in slow-motion. + * @param is_fastmotion True if the player is running the core in fast-forward. + * + * @see audio_driver_flush() + */ +static void audio_driver_flush_microphone_input( + audio_driver_state_t *audio_st, + retro_microphone_t *microphone, + float slowmotion_ratio, + bool audio_fastforward_mute, + int16_t *frames, size_t num_frames, + bool is_slowmotion, bool is_fastmotion) +{ + if ( audio_st && /* If the audio driver state is valid... */ + (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && /* ...and mic support is on... */ + audio_st->current_audio && + audio_st->context_audio_data && /* ...and the audio driver is initialized... */ + audio_st->input_samples_buf && /* ...with scratch space... */ + microphone && + microphone->active && /* ...and the mic itself is initialized... */ + microphone->microphone_context && /* ...and ready... */ + microphone->sample_buffer && + microphone->sample_buffer_length && /* ...with a non-empty sample buffer... */ + + audio_st->current_audio->read_microphone && /* ...and valid function pointers... */ + audio_st->current_audio->get_microphone_state && + audio_st->current_audio->get_microphone_state( /* ...and it's enabled... */ + audio_st->current_audio, + microphone->microphone_context)) + { + struct resampler_data src_data; + unsigned sample_size = audio_driver_get_sample_size(); + size_t bytes_to_read = MIN(audio_st->input_samples_buf_length, num_frames * sample_size); + ssize_t bytes_read = audio_st->current_audio->read_microphone( + audio_st->context_audio_data, + microphone->microphone_context, + audio_st->input_samples_buf, + bytes_to_read); + /* First, get the most recent mic data */ + + if (bytes_read < 0) + { /* If there was an error... */ + microphone->error = true; + return; + } + else if (bytes_read == 0) + { + microphone->error = false; + return; + } + + src_data.data_in = NULL; /* Will be assigned later */ + src_data.input_frames = num_frames; + src_data.data_out = audio_st->output_samples_buf; + src_data.output_frames = 0; /* Will be assigned by the resampler */ + src_data.ratio = audio_st->source_ratio_current; + + if (is_slowmotion) + src_data.ratio *= slowmotion_ratio; + + float audio_volume_gain = (audio_st->mute_enable || (audio_fastforward_mute && is_fastmotion)) + ? 0.0f + : audio_st->volume_gain; + + if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) + { /* If the mic gave us floating-point input... */ + src_data.data_in = audio_st->input_samples_buf; + } + else + { + convert_s16_to_float(audio_st->input_data, audio_st->input_samples_buf, bytes_read / sample_size / 2, + audio_volume_gain); + src_data.data_in = audio_st->input_data; + } + + audio_st->resampler->process(audio_st->resampler_data, &src_data); + + convert_float_to_s16(microphone->sample_buffer, src_data.data_out, src_data.output_frames); + + memcpy(frames, microphone->sample_buffer, num_frames * sample_size); + } +} + + +int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* frames, size_t num_frames) { - /* TODO: Should mic use on a pending microphone driver be an error? */ - if ( !microphone || - microphone->error || - !microphone->active || - !microphone->microphone_context || - !microphone->sample_buffer || - !microphone->sample_buffer_length || - !audio_driver_alive() || - !audio_driver_get_microphone_state(microphone)) + uint32_t runloop_flags; + size_t frames_remaining = num_frames; + audio_driver_state_t *audio_st = &audio_driver_st; + + if (audio_st->flags & AUDIO_FLAG_SUSPENDED + || num_frames == 0 + || !frames + || !microphone + || !microphone->active + || !audio_driver_get_microphone_state(microphone) + ) + { /* If the driver is suspended, or the core didn't actually ask for frames, + * or the microphone is disabled... */ return -1; - /* If the provided mic is invalid, - * or the audio driver isn't active, - * or mic support isn't fully implemented... */ + } - if (microphone->most_recent_read_length == 0) - return 0; + if (!(audio_st->context_audio_data && microphone->microphone_context)) + { /* If the microphone isn't ready... */ + return 0; /* Not an error */ + } - int copy_length = MIN(data_length, microphone->most_recent_read_length); - memcpy(data, microphone->sample_buffer, copy_length * 2); + runloop_flags = runloop_get_flags(); + + do + { + size_t frames_to_read = + (frames_remaining > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)) ? + (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1) : frames_remaining; + + if (!( (runloop_flags & RUNLOOP_FLAG_PAUSED) + || !(audio_st->flags & (AUDIO_FLAG_ACTIVE | AUDIO_FLAG_MIC_ACTIVE)) + || !(audio_st->input_samples_buf))) + /* If the game is running, the audio driver and mic are running, + * and the input sample buffer is valid... */ + audio_driver_flush_microphone_input(audio_st, + microphone, + config_get_ptr()->floats.slowmotion_ratio, + config_get_ptr()->bools.audio_fastforward_mute, + frames, + frames_to_read, + runloop_flags & RUNLOOP_FLAG_SLOWMOTION, + runloop_flags & RUNLOOP_FLAG_FASTMOTION); + frames_remaining -= frames_to_read; + frames += frames_to_read << 1; + } + while (frames_remaining > 0); - return copy_length; + return num_frames; } #ifdef HAVE_REWIND diff --git a/audio/audio_driver.h b/audio/audio_driver.h index e0a6c8de7816..0dc5dd91a99d 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -540,7 +540,7 @@ bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool stat bool audio_driver_get_microphone_state(const retro_microphone_t *microphone); -int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* data, size_t data_length); +int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* frames, size_t num_frames); #ifdef HAVE_TRANSLATE /* TODO/FIXME - Doesn't currently work. Fix this. */ From 679dd1f1d373a341b8684986dfcdaeb5dae6a39f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:47:35 -0500 Subject: [PATCH 064/309] Don't read from the incoming sample queue if it's empty --- audio/drivers/sdl_audio.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 2b0110a5b786..7e2f645f0a46 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -612,8 +612,12 @@ static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, v SDL_LockAudioDevice(microphone->device_id); /* Stop the SDL mic thread */ avail = FIFO_READ_AVAIL(microphone->sample_buffer); read_amt = avail > size ? size : avail; - fifo_read(microphone->sample_buffer, buf, read_amt); - SDL_UnlockAudioDevice(microphone->device_id); + if (read_amt > 0) + { /* If the incoming queue isn't empty... */ + fifo_read(microphone->sample_buffer, buf, read_amt); + /* ...then read as much data as will fit in buf */ + } + SDL_UnlockAudioDevice(microphone->device_id); /* Let the mic thread run again */ ret = read_amt; } else From 6e219e1c7c434e958fbe47b50ea5d5cea4ae9db6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:47:52 -0500 Subject: [PATCH 065/309] Some slight fixes --- audio/drivers/sdl_audio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 7e2f645f0a46..49fcbdac2364 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -649,7 +649,7 @@ static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, v } else { - size_t read_amt = size - read > avail ? avail : size - read; + size_t read_amt = MIN(size - read, avail); fifo_read(microphone->sample_buffer, buf + read, read_amt); /* Read as many samples as we have available without underflowing the queue */ @@ -661,9 +661,7 @@ static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, v ret = read; } - return ret / sizeof(int16_t); - /* Because the function should return the number of *samples* processed, not bytes - * (which is what the FIFO queues operate on) */ + return ret; } #endif From 0581ec99d03110e512d67797c536d6a9eb613974 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:48:27 -0500 Subject: [PATCH 066/309] Update libretro.h --- libretro-common/include/libretro.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 02ff1c9ac30b..ddfdcfd98327 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -3875,24 +3875,29 @@ typedef bool (RETRO_CALLCONV *retro_set_microphone_state_t)(retro_microphone_t * typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(const retro_microphone_t *microphone); /** - * Retrieves the input processed by the microphone since the previous frame. - * If called while the microphone or the audio driver are disabled, - * then nothing will be copied into data. + * Retrieves the input processed by the microphone since the last call. + * \em Must be called every frame unless \c microphone is disabled, + * similar to how \c retro_audio_sample_batch_t works. * * @param microphone Opaque handle to the microphone * whose recent input will be retrieved. - * @param data The buffer that will be used to store the microphone's data. + * @param samples The buffer that will be used to store the microphone's data. * Microphone input is in mono (i.e. one number per sample). * Should be large enough to accommodate the expected number of samples per frame; * for example, a 44.1kHz sample rate at 60 FPS would require space for 735 samples. - * @param data_length The size of the data buffer, in samples (\em not bytes). + * @param num_samples The size of the data buffer, in samples (\em not bytes). + * Microphone input is in mono, so a "frame" and a "sample" are equivalent in length here. + * + * @return The number of samples that were copied into \c samples. + * Will return 0 if \c microphone is still pending + * because the driver hasn't finished initializing. + * This is not an error. * - * @return The number of samples that were collected this frame. * Will return -1 if the microphone is disabled, * the audio driver is paused, * or there was an error. */ -typedef int (RETRO_CALLCONV *retro_get_microphone_input_t)(retro_microphone_t *microphone, int16_t* data, size_t data_length); +typedef int (RETRO_CALLCONV *retro_get_microphone_input_t)(retro_microphone_t *microphone, int16_t* samples, size_t num_samples); /** * An interface for querying the microphone and accessing data read from it. From 18cc94ee283a26a3fd3d4482958cf3d3800b6e55 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Jan 2023 21:48:58 -0500 Subject: [PATCH 067/309] Log calls to RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE --- runloop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runloop.c b/runloop.c index 8068a1c8e1b6..a6864285d299 100644 --- a/runloop.c +++ b/runloop.c @@ -3324,6 +3324,8 @@ bool runloop_environment_cb(unsigned cmd, void *data) struct retro_microphone_interface* microphone = (struct retro_microphone_interface *)data; const audio_driver_t *audio_driver = audio_state_get_ptr()->current_audio; + RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE.\n"); + if (!audio_driver) { RARCH_DBG("[Environ]: Couldn't initialize microphone interface, driver is not initialized\n"); From a8fca5aaa7e74a78d680f1b1ed9080372514cec2 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 12 Jan 2023 20:49:08 -0500 Subject: [PATCH 068/309] Finish proof-of-concept for mic support - It works, but doesn't support floating-point audio yet - It may need to be resampled, too --- audio/audio_driver.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 4857c2e6fcf5..fc8ba95aebfd 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -2085,8 +2085,9 @@ static void audio_driver_flush_microphone_input( ? 0.0f : audio_st->volume_gain; + /* if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) - { /* If the mic gave us floating-point input... */ + { src_data.data_in = audio_st->input_samples_buf; } else @@ -2098,9 +2099,9 @@ static void audio_driver_flush_microphone_input( audio_st->resampler->process(audio_st->resampler_data, &src_data); - convert_float_to_s16(microphone->sample_buffer, src_data.data_out, src_data.output_frames); + convert_float_to_s16(microphone->sample_buffer, src_data.data_out, src_data.output_frames);*/ - memcpy(frames, microphone->sample_buffer, num_frames * sample_size); + memcpy(frames, audio_st->input_samples_buf, num_frames * sample_size); } } From fb4f3c0de373ae00308c08dc158fb4f0e7cd5960 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 12 Jan 2023 21:21:40 -0500 Subject: [PATCH 069/309] Add macros that aren't available in SDL 2 --- audio/drivers/sdl_audio.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 49fcbdac2364..f22d7ff8fd35 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -61,6 +61,19 @@ typedef Uint32 SDL_AudioDeviceID; /** Compatibility stub that defers to SDL_CloseAudio. */ #define SDL_CloseAudioDevice(dev) SDL_CloseAudio() + +/* Macros for checking audio format bits that were introduced in SDL 2 */ +#define SDL_AUDIO_MASK_BITSIZE (0xFF) +#define SDL_AUDIO_MASK_DATATYPE (1<<8) +#define SDL_AUDIO_MASK_ENDIAN (1<<12) +#define SDL_AUDIO_MASK_SIGNED (1<<15) +#define SDL_AUDIO_BITSIZE(x) (x & SDL_AUDIO_MASK_BITSIZE) +#define SDL_AUDIO_ISFLOAT(x) (x & SDL_AUDIO_MASK_DATATYPE) +#define SDL_AUDIO_ISBIGENDIAN(x) (x & SDL_AUDIO_MASK_ENDIAN) +#define SDL_AUDIO_ISSIGNED(x) (x & SDL_AUDIO_MASK_SIGNED) +#define SDL_AUDIO_ISINT(x) (!SDL_AUDIO_ISFLOAT(x)) +#define SDL_AUDIO_ISLITTLEENDIAN(x) (!SDL_AUDIO_ISBIGENDIAN(x)) +#define SDL_AUDIO_ISUNSIGNED(x) (!SDL_AUDIO_ISSIGNED(x)) #endif #ifdef SDL_DRIVER_MIC_SUPPORT From 7ad85d8779044df25cca1933442a91640f0784c6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 07:36:56 -0500 Subject: [PATCH 070/309] Comment out a variable definition for now - For C89 compliance --- audio/audio_driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index e15a8fe64fad..e3b8fe502e3c 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -2095,9 +2095,10 @@ static void audio_driver_flush_microphone_input( if (is_slowmotion) src_data.ratio *= slowmotion_ratio; + /* float audio_volume_gain = (audio_st->mute_enable || (audio_fastforward_mute && is_fastmotion)) ? 0.0f - : audio_st->volume_gain; + : audio_st->volume_gain;*/ /* if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) From abb968ddd01d4336f161138746534aa57f91bd43 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 08:33:27 -0500 Subject: [PATCH 071/309] Add some comments for clarity --- audio/audio_thread_wrapper.c | 47 +++++++++++++++++++++++------- libretro-common/include/libretro.h | 7 +++++ 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/audio/audio_thread_wrapper.c b/audio/audio_thread_wrapper.c index f83a4306980f..ef63dc07684c 100644 --- a/audio/audio_thread_wrapper.c +++ b/audio/audio_thread_wrapper.c @@ -51,6 +51,10 @@ typedef struct audio_thread } audio_thread_t; +/** + * The thread that manages the life of the audio driver. + * The wrapped audio driver lives and dies with this function. + */ static void audio_thread_loop(void *data) { audio_thread_t *thr = (audio_thread_t*)data; @@ -113,6 +117,10 @@ static void audio_thread_loop(void *data) thr->driver->free(thr->driver_data); } +/** + * Lets the audio thread finish what it's doing, + * then stops it from doing further work. + */ static void audio_thread_block(audio_thread_t *thr) { if (!thr) @@ -133,15 +141,19 @@ static void audio_thread_block(audio_thread_t *thr) slock_unlock(thr->lock); } +/** + * Resumes the audio thread. + * This function is called from the main thread. + */ static void audio_thread_unblock(audio_thread_t *thr) { if (!thr) return; - slock_lock(thr->lock); - thr->stopped = false; - scond_signal(thr->cond); - slock_unlock(thr->lock); + slock_lock(thr->lock); /* Prevent the audio thread from touching this flag... */ + thr->stopped = false; /* ...so that the main thread can do it. */ + scond_signal(thr->cond); /* Then let the audio thread know that it's okay to resume. */ + slock_unlock(thr->lock); /* "As you were." */ } static void audio_thread_free(void *data) @@ -153,13 +165,15 @@ static void audio_thread_free(void *data) if (thr->thread) { - slock_lock(thr->lock); - thr->stopped = false; + slock_lock(thr->lock); /* Let the audio thread finish what it's doing... */ + thr->stopped = false; /* Then stop it. "You're fired." */ thr->alive = false; - scond_signal(thr->cond); - slock_unlock(thr->lock); + scond_signal(thr->cond); /* Let the thread know it's okay to continue */ + slock_unlock(thr->lock); /* At this point, it will exit its loop. */ sthread_join(thr->thread); + /* Wait for the audio thread to exit, ensure that it's really dead. + * (It will call the wrapped driver's free() function.) */ } if (thr->lock) @@ -167,6 +181,7 @@ static void audio_thread_free(void *data) if (thr->cond) scond_free(thr->cond); free(thr); + /* The audio driver is done, clean up the thread itself. */ } static bool audio_thread_alive(void *data) @@ -191,6 +206,9 @@ static bool audio_thread_stop(void *data) if (!thr) return false; + /* Don't immediately call stop on the driver; + * let the audio thread finish its current loop iteration. + * It will call stop then. */ audio_thread_block(thr); thr->is_paused = true; @@ -219,6 +237,8 @@ static void audio_thread_set_nonblock_state(void *data, bool state) { (void)data; (void)state; + /* Ignored, because blocking state is irrelevant + * when audio is running on a separate thread. */ } static bool audio_thread_use_float(void *data) @@ -274,6 +294,9 @@ static void *audio_thread_init_microphone(void *data, const char *device, unsign return microphone; } +/** + * Should only be called on the main thread. + */ static void audio_thread_free_microphone(void *data, void *microphone_context) { // TODO: Implement properly @@ -289,9 +312,11 @@ static void audio_thread_free_microphone(void *data, void *microphone_context) thr->driver->free_microphone(thr->driver_data, microphone_context); } +/** + * Can be called from the main thread or the audio callback. + */ static bool audio_thread_get_microphone_state(const void *data, const void *microphone_context) { - // TODO: Implement properly audio_thread_t *thr = (audio_thread_t*)data; if (!thr) @@ -301,6 +326,7 @@ static bool audio_thread_get_microphone_state(const void *data, const void *micr thr->driver_data && thr->driver->get_microphone_state) return thr->driver->get_microphone_state(thr->driver_data, microphone_context); + /* Don't lock here, because this function could be called each frame */ return false; } @@ -323,6 +349,7 @@ static bool audio_thread_set_microphone_state(void *data, void *microphone_conte static ssize_t audio_thread_read_microphone(void *data, void *microphone_context, void *buf, size_t size) { + // TODO: Implement properly int ret; audio_thread_t *thr = (audio_thread_t*)data; @@ -343,7 +370,7 @@ static ssize_t audio_thread_read_microphone(void *data, void *microphone_context } static const audio_driver_t audio_thread = { - NULL, + NULL, /* No need to wrap init, it's called at the start of the thread loop */ audio_thread_write, audio_thread_stop, audio_thread_start, diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index ddfdcfd98327..5957bce068c2 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -3817,6 +3817,8 @@ typedef struct retro_microphone retro_microphone_t; * The returned handle will be valid until it's freed, * even if the audio driver is reinitialized. * + * This should only be called from the main thread. + * * @returns \c NULL if a microphone couldn't be initialized. * This likely means that no microphone is plugged in and recognized, * or the maximum number of supported microphones has been reached. @@ -3879,6 +3881,9 @@ typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(const retro_micropho * \em Must be called every frame unless \c microphone is disabled, * similar to how \c retro_audio_sample_batch_t works. * + * If using \c RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK, + * then this function should be called within the provided callback. + * * @param microphone Opaque handle to the microphone * whose recent input will be retrieved. * @param samples The buffer that will be used to store the microphone's data. @@ -3896,6 +3901,8 @@ typedef bool (RETRO_CALLCONV *retro_get_microphone_state_t)(const retro_micropho * Will return -1 if the microphone is disabled, * the audio driver is paused, * or there was an error. + * + * @see RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK */ typedef int (RETRO_CALLCONV *retro_get_microphone_input_t)(retro_microphone_t *microphone, int16_t* samples, size_t num_samples); From 8096acd416ed926005a73d767761ca4ec5a3d04d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 08:43:31 -0500 Subject: [PATCH 072/309] Let ALSA tolerate a null new_rate --- audio/drivers/alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 8a604324846b..3f875df74b87 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -107,7 +107,7 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, if (snd_pcm_hw_params_set_rate_near(alsa->pcm, params, &rate, 0) < 0) goto error; - if (rate != orig_rate) + if (new_rate && (rate != orig_rate)) *new_rate = rate; if (snd_pcm_hw_params_set_buffer_time_near( From 0097b80ec143ea711223d1fd8e7995ef9603e62d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 10:55:10 -0500 Subject: [PATCH 073/309] Partial ALSA microphone support - Not yet tested - Mic is created and destroyed - Mic can also be paused or unpaused - Mic is paused or unpaused with the rest of the driver - Microphone is not yet read --- audio/drivers/alsa.c | 254 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 3f875df74b87..91d3dd4a62c4 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -24,9 +24,23 @@ #include "../audio_driver.h" #include "../../verbosity.h" +typedef struct alsa_microphone +{ + snd_pcm_t *pcm; + size_t buffer_size; + unsigned int frame_bits; + bool has_float; + bool can_pause; + bool is_paused; +} alsa_microphone_t; + typedef struct alsa { snd_pcm_t *pcm; + /* Only one microphone is supported right now; + * the driver state should track multiple microphone handles, + * but the driver *context* should track multiple microphone contexts */ + alsa_microphone_t *microphone; size_t buffer_size; unsigned int frame_bits; bool nonblock; @@ -290,6 +304,21 @@ static bool alsa_stop(void *data) alsa->is_paused = true; } + if (alsa->microphone) + { + /* Stop the microphone independently of whether it's paused; + * note that upon alsa_start, the microphone won't resume + * if it was previously paused */ + int errnum = snd_pcm_pause(alsa->microphone->pcm, true); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to pause microphone %x: %s", + alsa->microphone->pcm, + snd_strerror(errnum)); + return false; + } + } + return true; } @@ -318,6 +347,18 @@ static bool alsa_start(void *data, bool is_shutdown) } alsa->is_paused = false; + + if (alsa->microphone && !alsa->microphone->is_paused) + { /* If the mic wasn't paused at the time the overall driver was paused... */ + int errnum = snd_pcm_pause(alsa->microphone->pcm, false); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to unpause microphone %x: %s", + alsa->microphone->pcm, + snd_strerror(errnum)); + return false; + } + } } return true; } @@ -414,6 +455,214 @@ static void alsa_device_list_free(void *data, void *array_list_data) string_list_free(s); } +static void *alsa_init_microphone(void *data, + const char *device, + unsigned rate, + unsigned latency, + unsigned block_frames, + unsigned *new_rate) +{ + snd_pcm_format_t format; + snd_pcm_uframes_t buffer_size; + alsa_t *alsa = (alsa_t*)data; + snd_pcm_hw_params_t *params = NULL; + snd_pcm_sw_params_t *sw_params = NULL; + unsigned latency_usec = latency * 1000; + unsigned periods = 4; + unsigned orig_rate = rate; + const char *alsa_mic_dev = "default"; + alsa_microphone_t *microphone = NULL; + int errnum = 0; + + if (!alsa) /* If we weren't given a valid ALSA context... */ + return NULL; + + microphone = calloc(1, sizeof(alsa_microphone_t)); + + if (!microphone) /* If the microphone context couldn't be allocated... */ + return NULL; + + if (device) + alsa_mic_dev = device; + + errnum = snd_pcm_open(µphone->pcm, alsa_mic_dev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to open microphone device: %s\n", snd_strerror(errnum)); + goto error; + } + + if (snd_pcm_hw_params_malloc(¶ms) < 0) + goto error; + + if (snd_pcm_hw_params_any(microphone->pcm, params) < 0) + goto error; + + microphone->has_float = find_float_format(microphone->pcm, params); + format = microphone->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; + + if (microphone->has_float != alsa->has_float) + { /* If the mic and speaker don't both use floating point or integer samples... */ + RARCH_WARN("[ALSA]: Microphone and speaker do not use the same PCM format\n"); + RARCH_WARN("[ALSA]: (%s and %s, respectively)", + microphone->has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16", + alsa->has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16"); + } + + if (snd_pcm_hw_params_set_access( + microphone->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) + goto error; + + /* channels hardcoded to 1, because we only support mono mic input */ + microphone->frame_bits = snd_pcm_format_physical_width(format); + + if (snd_pcm_hw_params_set_format(microphone->pcm, params, format) < 0) + goto error; + + if (snd_pcm_hw_params_set_channels(microphone->pcm, params, 1) < 0) + goto error; + + /* Don't allow rate resampling when probing for the default rate (but ignore if this call fails) */ + snd_pcm_hw_params_set_rate_resample(microphone->pcm, params, 0 ); + if (snd_pcm_hw_params_set_rate_near(microphone->pcm, params, &rate, 0) < 0) + goto error; + + if (new_rate && (rate != orig_rate)) + *new_rate = rate; + + if (snd_pcm_hw_params_set_buffer_time_near( + microphone->pcm, params, &latency_usec, NULL) < 0) + goto error; + + if (snd_pcm_hw_params_set_periods_near( + microphone->pcm, params, &periods, NULL) < 0) + goto error; + + if (snd_pcm_hw_params(microphone->pcm, params) < 0) + goto error; + + /* Shouldn't have to bother with this, + * but some drivers are apparently broken. */ + if (snd_pcm_hw_params_get_period_size(params, &buffer_size, NULL)) + snd_pcm_hw_params_get_period_size_min(params, &buffer_size, NULL); + + RARCH_LOG("[ALSA]: Microphone period size: %d frames\n", (int)buffer_size); + + if (snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) + snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size); + + RARCH_LOG("[ALSA]: Microphone buffer size: %d frames\n", (int)buffer_size); + + microphone->buffer_size = snd_pcm_frames_to_bytes(microphone->pcm, buffer_size); + microphone->can_pause = snd_pcm_hw_params_can_pause(params); + + RARCH_LOG("[ALSA]: Can pause microphone: %s.\n", microphone->can_pause ? "yes" : "no"); + + if (snd_pcm_sw_params_malloc(&sw_params) < 0) + goto error; + + if (snd_pcm_sw_params_current(microphone->pcm, sw_params) < 0) + goto error; + + snd_pcm_hw_params_free(params); + snd_pcm_sw_params_free(sw_params); + + alsa->microphone = microphone; + return microphone; + +error: + RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); + if (params) + snd_pcm_hw_params_free(params); + + if (sw_params) + snd_pcm_sw_params_free(sw_params); + + if (microphone) + { + if (microphone->pcm) + { + snd_pcm_close(microphone->pcm); + } + + free(microphone); + } + return NULL; +} + +static void alsa_free_microphone(void *data, void *microphone_context) +{ + alsa_t *alsa = (alsa_t*)data; + alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; + + if (alsa && microphone) + { + if (microphone->pcm) + { + snd_pcm_drop(microphone->pcm); + snd_pcm_close(microphone->pcm); + } + + alsa->microphone = NULL; + free(microphone); + } +} + +static bool alsa_get_microphone_state(const void *data, const void *microphone_context) +{ + alsa_t *alsa = (alsa_t*)data; + alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; + + if (!alsa || !microphone) + return false; + /* Both params must be non-null */ + + return !microphone->is_paused; + /* The mic might be paused due to app requirements, + * or it might be stopped because the entire audio driver is stopped. */ +} + +static bool alsa_set_microphone_state(void *data, void *microphone_context, bool enabled) +{ + alsa_t *alsa = (alsa_t*)data; + alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; + + if (!alsa || !microphone) + return false; + /* Both params must be non-null */ + + if (!microphone->can_pause) + { + RARCH_WARN("[ALSA]: Microphone %x cannot be paused\n", microphone->pcm); + return true; + } + + if (!alsa->is_paused) + { /* If the entire audio driver isn't paused... */ + int errnum = snd_pcm_pause(microphone->pcm, !enabled); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to %s microphone %x: %s", + enabled ? "unpause" : "pause", + microphone->pcm, + snd_strerror(errnum)); + return false; + } + + microphone->is_paused = !enabled; + RARCH_DBG("[ALSA]: Set state of microphone %x to %s\n", + microphone->pcm, enabled ? "enabled" : "disabled"); + } + + return true; +} + +static ssize_t alsa_read_microphone(void *driver_context, void *microphone_context, void *buf, size_t size) +{ + + alsa_t *alsa = (alsa_t*)driver_context; +} + audio_driver_t audio_alsa = { alsa_init, alsa_write, @@ -428,4 +677,9 @@ audio_driver_t audio_alsa = { alsa_device_list_free, alsa_write_avail, alsa_buffer_size, + alsa_init_microphone, + alsa_free_microphone, + alsa_get_microphone_state, + alsa_set_microphone_state, + alsa_read_microphone, }; From 722243d920e803a13a6face476d1f24096c9a452 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 11:44:07 -0500 Subject: [PATCH 074/309] Install error logging in the ALSA driver - It defers to RARCH_ERR --- audio/drivers/alsa.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 91d3dd4a62c4..6058499ee674 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -55,6 +55,14 @@ static bool alsa_use_float(void *data) return alsa->has_float; } +static void alsa_log_error(const char *file, int line, const char *function, int err, const char *fmt,...) +{ + va_list va; + va_start(va, fmt); + RARCH_ERR(fmt, va); + va_end(va); +} + static bool find_float_format(snd_pcm_t *pcm, void *data) { snd_pcm_hw_params_t *params = (snd_pcm_hw_params_t*)data; @@ -168,6 +176,9 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, snd_pcm_hw_params_free(params); snd_pcm_sw_params_free(sw_params); + snd_lib_error_set_handler(alsa_log_error); + /* Should we store the current error handler, or is it fine to assume it's the default? */ + return alsa; error: @@ -188,6 +199,7 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, free(alsa); } + snd_lib_error_set_handler(NULL); return NULL; } @@ -378,6 +390,8 @@ static void alsa_free(void *data) free(alsa); } + + snd_lib_error_set_handler(NULL); } static size_t alsa_write_avail(void *data) From d1cc869bd3257dfa477387ca31da46908a48544b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 11:44:26 -0500 Subject: [PATCH 075/309] Free the ALSA microphone in alsa_free --- audio/drivers/alsa.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 6058499ee674..8c6b126094bc 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -375,6 +375,7 @@ static bool alsa_start(void *data, bool is_shutdown) return true; } +static void alsa_free_microphone(void *data, void *microphone_context); static void alsa_free(void *data) { alsa_t *alsa = (alsa_t*)data; @@ -385,9 +386,14 @@ static void alsa_free(void *data) { snd_pcm_drop(alsa->pcm); snd_pcm_close(alsa->pcm); - snd_config_update_free_global(); } + if (alsa->microphone) + { + alsa_free_microphone(alsa, alsa->microphone); + } + + snd_config_update_free_global(); free(alsa); } From e6712fdad063e09473ee620c36d00cf5c427a2f9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 11:44:32 -0500 Subject: [PATCH 076/309] Fix an indent --- audio/drivers/alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 8c6b126094bc..ef186c4fb76f 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -644,7 +644,7 @@ static bool alsa_get_microphone_state(const void *data, const void *microphone_c static bool alsa_set_microphone_state(void *data, void *microphone_context, bool enabled) { - alsa_t *alsa = (alsa_t*)data; + alsa_t *alsa = (alsa_t*)data; alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; if (!alsa || !microphone) From b864aabb3f6f02a84dd6230301929e5bd5cabe78 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 11:44:47 -0500 Subject: [PATCH 077/309] First draft of alsa_read_microphone --- audio/drivers/alsa.c | 95 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index ef186c4fb76f..d24e2392d914 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -677,10 +677,101 @@ static bool alsa_set_microphone_state(void *data, void *microphone_context, bool return true; } -static ssize_t alsa_read_microphone(void *driver_context, void *microphone_context, void *buf, size_t size) +static ssize_t alsa_read_microphone(void *driver_context, void *microphone_context, void *buf_, size_t size_) { + alsa_t *alsa = (alsa_t*)driver_context; + alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; + uint8_t *buf = (uint8_t*)buf_; + snd_pcm_sframes_t read = 0; + int errnum = 0; + snd_pcm_sframes_t size; + size_t frames_size; - alsa_t *alsa = (alsa_t*)driver_context; + if (!alsa || !microphone || !buf) + return -1; + + size = BYTES_TO_FRAMES(size_, microphone->frame_bits); + frames_size = microphone->has_float ? sizeof(float) : sizeof(int16_t); + + /* Workaround buggy menu code. + * If a read happens while we're paused, we might never progress. */ + if (microphone->is_paused) + if (!alsa_set_microphone_state(alsa, microphone, true)) + return -1; + + if (alsa->nonblock) + { + while (size) + { + snd_pcm_sframes_t frames = snd_pcm_readi(microphone->pcm, buf, size); + + if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) + { + errnum = snd_pcm_recover(microphone->pcm, frames, 0); + if (errnum < 0) + { + RARCH_ERR("[ALSA] Failed to read from microphone: %s\n", snd_strerror(frames)); + RARCH_ERR("[ALSA] Additionally, recovery failed with: %s\n", snd_strerror(errnum)); + return -1; + } + + break; + } + else if (frames == -EAGAIN) + break; + else if (frames < 0) + return -1; + + read += frames; + buf += frames_size; + size -= frames; + } + } + else + { + bool eagain_retry = true; + + while (size) + { + snd_pcm_sframes_t frames; + int rc = snd_pcm_wait(microphone->pcm, -1); + + if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR) + { + if (snd_pcm_recover(microphone->pcm, rc, 1) < 0) + return -1; + continue; + } + + frames = snd_pcm_readi(microphone->pcm, buf, size); + + if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) + { + if (snd_pcm_recover(microphone->pcm, frames, 1) < 0) + return -1; + + break; + } + else if (frames == -EAGAIN) + { + /* Definitely not supposed to happen. */ + if (eagain_retry) + { + eagain_retry = false; + continue; + } + break; + } + else if (frames < 0) + return -1; + + read += frames; + buf += frames_size; + size -= frames; + } + } + + return read * frames_size; } audio_driver_t audio_alsa = { From a16a8922b7d061f3478c4752937eeb8f3b9ab5bd Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 13 Jan 2023 11:45:01 -0500 Subject: [PATCH 078/309] Deinitialize SDL Audio in sdl_audio_free --- audio/drivers/sdl_audio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index f22d7ff8fd35..142772deed05 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -411,6 +411,8 @@ static void sdl_audio_free(void *data) slock_free(sdl->lock); scond_free(sdl->cond); #endif + + SDL_QuitSubSystem(SDL_INIT_AUDIO); } free(sdl); } From 5d00726a2b0760a7893bc64aaee8b35d5b870435 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 14 Jan 2023 11:45:25 -0500 Subject: [PATCH 079/309] Save and restore the ALSA error logger - You should always practice safe global state --- audio/drivers/alsa.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index d24e2392d914..80e512c9d93c 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -41,6 +41,10 @@ typedef struct alsa * the driver state should track multiple microphone handles, * but the driver *context* should track multiple microphone contexts */ alsa_microphone_t *microphone; + + /* The error handler that was set before this driver was initialized. + * Likely to be equal to the default, but kept and restored just in case. */ + snd_lib_error_handler_t prev_error_handler; size_t buffer_size; unsigned int frame_bits; bool nonblock; @@ -95,6 +99,9 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, if (!alsa) return NULL; + alsa->prev_error_handler = snd_lib_error; + snd_lib_error_set_handler(alsa_log_error); + if (device) alsa_dev = device; @@ -176,9 +183,6 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, snd_pcm_hw_params_free(params); snd_pcm_sw_params_free(sw_params); - snd_lib_error_set_handler(alsa_log_error); - /* Should we store the current error handler, or is it fine to assume it's the default? */ - return alsa; error: @@ -197,9 +201,13 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, snd_config_update_free_global(); } + if (alsa->prev_error_handler) + { /* If we ever changed the error handler, put it back. */ + snd_lib_error_set_handler(alsa->prev_error_handler); + } + free(alsa); } - snd_lib_error_set_handler(NULL); return NULL; } @@ -393,11 +401,15 @@ static void alsa_free(void *data) alsa_free_microphone(alsa, alsa->microphone); } + + if (alsa->prev_error_handler) + { /* If we ever changed the error handler, put it back. */ + snd_lib_error_set_handler(alsa->prev_error_handler); + } + snd_config_update_free_global(); free(alsa); } - - snd_lib_error_set_handler(NULL); } static size_t alsa_write_avail(void *data) From 3fb83047ffdd891206ec0808f7cd5876143bff19 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 14 Jan 2023 15:18:39 -0500 Subject: [PATCH 080/309] Add newlines to some RARCH_ERRs --- audio/drivers/alsa.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 80e512c9d93c..8fbc1ef6c775 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -332,7 +332,7 @@ static bool alsa_stop(void *data) int errnum = snd_pcm_pause(alsa->microphone->pcm, true); if (errnum < 0) { - RARCH_ERR("[ALSA]: Failed to pause microphone %x: %s", + RARCH_ERR("[ALSA]: Failed to pause microphone %x: %s\n", alsa->microphone->pcm, snd_strerror(errnum)); return false; @@ -373,7 +373,7 @@ static bool alsa_start(void *data, bool is_shutdown) int errnum = snd_pcm_pause(alsa->microphone->pcm, false); if (errnum < 0) { - RARCH_ERR("[ALSA]: Failed to unpause microphone %x: %s", + RARCH_ERR("[ALSA]: Failed to unpause microphone %x: %s\n", alsa->microphone->pcm, snd_strerror(errnum)); return false; @@ -536,7 +536,7 @@ static void *alsa_init_microphone(void *data, if (microphone->has_float != alsa->has_float) { /* If the mic and speaker don't both use floating point or integer samples... */ RARCH_WARN("[ALSA]: Microphone and speaker do not use the same PCM format\n"); - RARCH_WARN("[ALSA]: (%s and %s, respectively)", + RARCH_WARN("[ALSA]: (%s and %s, respectively)\n", microphone->has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16", alsa->has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16"); } @@ -674,7 +674,7 @@ static bool alsa_set_microphone_state(void *data, void *microphone_context, bool int errnum = snd_pcm_pause(microphone->pcm, !enabled); if (errnum < 0) { - RARCH_ERR("[ALSA]: Failed to %s microphone %x: %s", + RARCH_ERR("[ALSA]: Failed to %s microphone %x: %s\n", enabled ? "unpause" : "pause", microphone->pcm, snd_strerror(errnum)); From 9034eb1f8f2a28120be03efc9e3c04fc3c071a6e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 14 Jan 2023 17:38:46 -0500 Subject: [PATCH 081/309] Add some logging --- audio/drivers/alsa.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 8fbc1ef6c775..86e0bfed6a3b 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -61,10 +61,21 @@ static bool alsa_use_float(void *data) static void alsa_log_error(const char *file, int line, const char *function, int err, const char *fmt,...) { - va_list va; - va_start(va, fmt); - RARCH_ERR(fmt, va); - va_end(va); + va_list args; + char temp[256]; + + memset(temp, 0, sizeof(temp)); + + if (err) + { + RARCH_ERR("[ALSA] [System] Error in %s:%s:%d: %s\n", file, function, line, snd_strerror(err)); + } + + va_start(args, fmt); + vsnprintf(temp, 255, fmt, args); + /* Write up to 255 characters. (The 256th will be \0.) */ + va_end(args); + RARCH_ERR("[ALSA] [%s:%s:%d] %s\n", file, function, line, temp); /* To ensure that there's a newline at the end */ } static bool find_float_format(snd_pcm_t *pcm, void *data) @@ -105,6 +116,8 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, if (device) alsa_dev = device; + RARCH_DBG("[ALSA] Requesting device \"%s\" for output\n", alsa_dev); + if (snd_pcm_open( &alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) goto error; @@ -517,6 +530,8 @@ static void *alsa_init_microphone(void *data, if (device) alsa_mic_dev = device; + RARCH_DBG("[ALSA] Requesting device \"%s\" for input\n", alsa_mic_dev); + errnum = snd_pcm_open(µphone->pcm, alsa_mic_dev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (errnum < 0) { From 716ec00693128907e2024a96373d7ae7705ef29b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 14 Jan 2023 18:50:24 -0500 Subject: [PATCH 082/309] Check for the mic being active via settings instead of via flags --- audio/audio_driver.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index e3b8fe502e3c..02714ec39b8b 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1896,9 +1896,11 @@ retro_microphone_t *audio_driver_init_microphone(void) return NULL; } - if (!(audio_st->flags & AUDIO_FLAG_MIC_ACTIVE)) - { - RARCH_WARN("[Audio]: Refused to initialize microphone because it's disabled\n"); + if (!settings->bools.audio_enable_microphone) + { /* Not checking audio_st->flags because they might not be set yet; + * don't forget, the user can ask for a mic + * before the audio driver is ready to create one. */ + RARCH_WARN("[Audio]: Refused to initialize microphone because it's disabled in the settings\n"); return NULL; } From 4476a77331a20d74dd27861f8c0d8aebc9f08a37 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 11:21:23 -0500 Subject: [PATCH 083/309] Adjusted a log entry to be less misleading - A frequency of 0Hz looks weird to the uninformed - In reality, it means the driver used the requested frequency --- audio/audio_driver.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 02714ec39b8b..e304e3fd11df 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1863,10 +1863,15 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone audio_driver_set_microphone_state(microphone, microphone->pending_enabled); if (actual_sample_rate != 0) + { + RARCH_LOG("[Audio]: Requested microphone sample rate of %uHz, got %Hz. Updating settings with this value.\n", + settings->uints.audio_input_sample_rate, + actual_sample_rate + ); configuration_set_uint(settings, settings->uints.audio_input_sample_rate, actual_sample_rate); + } - RARCH_LOG("[Audio]: Initialized microphone (sample rate: %dHz)\n", actual_sample_rate); - + RARCH_LOG("[Audio]: Initialized microphone\n", actual_sample_rate); return; error: audio_driver_microphone_handle_free(microphone); From 6272b99aa866a2f65b7cf1817d6ec01bb49d77bf Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 14:16:23 -0500 Subject: [PATCH 084/309] Fix an incorrect format string --- audio/audio_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index e304e3fd11df..6801931da264 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1864,7 +1864,7 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone if (actual_sample_rate != 0) { - RARCH_LOG("[Audio]: Requested microphone sample rate of %uHz, got %Hz. Updating settings with this value.\n", + RARCH_LOG("[Audio]: Requested microphone sample rate of %uHz, got %uHz. Updating settings with this value.\n", settings->uints.audio_input_sample_rate, actual_sample_rate ); From c63fd756bb008e2ba1ba5c1659eb8b04a8e2df5d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 14:16:41 -0500 Subject: [PATCH 085/309] Tidy up logging in alsa.c --- audio/drivers/alsa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 86e0bfed6a3b..774f92734cfd 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -72,10 +72,10 @@ static void alsa_log_error(const char *file, int line, const char *function, int } va_start(args, fmt); - vsnprintf(temp, 255, fmt, args); + vsnprintf(temp, sizeof(temp), fmt, args); /* Write up to 255 characters. (The 256th will be \0.) */ va_end(args); - RARCH_ERR("[ALSA] [%s:%s:%d] %s\n", file, function, line, temp); /* To ensure that there's a newline at the end */ + RARCH_ERR("[ALSA] [%s:%s:%d]: %s\n", file, function, line, temp); /* To ensure that there's a newline at the end */ } static bool find_float_format(snd_pcm_t *pcm, void *data) From f1f19cc097aada1eaaf6bea72a2e7c8f5dbc02c6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 14:18:33 -0500 Subject: [PATCH 086/309] Rename audio_enable_microphone to audio_enable_input --- audio/audio_driver.c | 4 ++-- config.def.h | 2 +- configuration.c | 2 +- configuration.h | 2 +- retroarch.cfg | 2 +- runloop.c | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 6801931da264..27462636eade 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -616,7 +616,7 @@ bool audio_driver_init_internal( settings_t *settings = (settings_t*)settings_data; size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2; bool audio_enable = settings->bools.audio_enable; - bool audio_enable_microphone = settings->bools.audio_enable_microphone; + bool audio_enable_microphone = settings->bools.audio_enable_input; bool audio_sync = settings->bools.audio_sync; bool audio_rate_control = settings->bools.audio_rate_control; float slowmotion_ratio = settings->floats.slowmotion_ratio; @@ -1901,7 +1901,7 @@ retro_microphone_t *audio_driver_init_microphone(void) return NULL; } - if (!settings->bools.audio_enable_microphone) + if (!settings->bools.audio_enable_input) { /* Not checking audio_st->flags because they might not be set yet; * don't forget, the user can ask for a mic * before the audio driver is ready to create one. */ diff --git a/config.def.h b/config.def.h index ba126dd48ba8..75653e059c9a 100644 --- a/config.def.h +++ b/config.def.h @@ -978,7 +978,7 @@ /* Will enable audio or not. */ #define DEFAULT_AUDIO_ENABLE true -#define DEFAULT_AUDIO_ENABLE_MICROPHONE true +#define DEFAULT_AUDIO_ENABLE_INPUT true /* Enable menu audio sounds. */ #define DEFAULT_AUDIO_ENABLE_MENU false diff --git a/configuration.c b/configuration.c index f289c94988b0..043a02b5b9a1 100644 --- a/configuration.c +++ b/configuration.c @@ -1732,7 +1732,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("keyboard_gamepad_enable", &settings->bools.input_keyboard_gamepad_enable, true, true, false); SETTING_BOOL("core_set_supports_no_game_enable", &settings->bools.set_supports_no_game_enable, true, true, false); SETTING_BOOL("audio_enable", &settings->bools.audio_enable, true, DEFAULT_AUDIO_ENABLE, false); - SETTING_BOOL("audio_enable_microphone", &settings->bools.audio_enable_microphone, true, DEFAULT_AUDIO_ENABLE_MICROPHONE, false); + SETTING_BOOL("audio_enable_input", &settings->bools.audio_enable_input, true, DEFAULT_AUDIO_ENABLE_INPUT, false); SETTING_BOOL("menu_enable_widgets", &settings->bools.menu_enable_widgets, true, DEFAULT_MENU_ENABLE_WIDGETS, false); SETTING_BOOL("menu_show_load_content_animation", &settings->bools.menu_show_load_content_animation, true, DEFAULT_MENU_SHOW_LOAD_CONTENT_ANIMATION, false); SETTING_BOOL("notification_show_autoconfig", &settings->bools.notification_show_autoconfig, true, DEFAULT_NOTIFICATION_SHOW_AUTOCONFIG, false); diff --git a/configuration.h b/configuration.h index a0a416f45265..114e60ebd48b 100644 --- a/configuration.h +++ b/configuration.h @@ -598,7 +598,7 @@ typedef struct settings /* Audio */ bool audio_enable; - bool audio_enable_microphone; + bool audio_enable_input; bool audio_enable_menu; bool audio_enable_menu_ok; bool audio_enable_menu_cancel; diff --git a/retroarch.cfg b/retroarch.cfg index 9927b939b57a..7ad3d186967f 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -296,7 +296,7 @@ # audio_enable = true # Enable the microphone. -# audio_enable_microphone = true +# audio_enable_input = true # Enable menu audio sounds. # audio_enable_menu = false diff --git a/runloop.c b/runloop.c index ed0eb985bf32..873ec0c428d9 100644 --- a/runloop.c +++ b/runloop.c @@ -3346,7 +3346,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) return false; /* User didn't provide a pointer for a response, what can we do? */ - if (!settings->bools.audio_enable_microphone) + if (!settings->bools.audio_enable_input) return false; if (audio_driver_supports_microphone(audio_driver)) @@ -3375,7 +3375,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) return false; /* User didn't provide a pointer for a response, what can we do? */ - *microphone_enabled = settings->bools.audio_enable_microphone; + *microphone_enabled = settings->bools.audio_enable_input; return true; } default: From 3a4d7bfffd58780a2a41eaef3f42ab5a3dc1fe94 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 14:27:01 -0500 Subject: [PATCH 087/309] Rename microphone_device to audio_input_device --- audio/audio_driver.c | 2 +- config.def.h | 2 +- configuration.c | 8 ++++---- configuration.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 27462636eade..f5756f5ee684 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1851,7 +1851,7 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone goto error; microphone->microphone_context = audio_driver->init_microphone(context, - *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, + *settings->arrays.audio_input_device ? settings->arrays.audio_input_device : NULL, settings->uints.audio_input_sample_rate, audio_latency, settings->uints.audio_block_frames, diff --git a/config.def.h b/config.def.h index 75653e059c9a..7c3748ef0645 100644 --- a/config.def.h +++ b/config.def.h @@ -1076,7 +1076,7 @@ /* Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults. */ #define DEFAULT_AUDIO_DEVICE NULL -#define DEFAULT_MICROPHONE_DEVICE NULL +#define DEFAULT_AUDIO_INPUT_DEVICE NULL /* Desired audio latency in milliseconds. Might not be honored * if driver can't provide given latency. */ diff --git a/configuration.c b/configuration.c index 043a02b5b9a1..62c67415653b 100644 --- a/configuration.c +++ b/configuration.c @@ -1416,7 +1416,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings SETTING_ARRAY("menu_driver", settings->arrays.menu_driver, false, NULL, true); #endif SETTING_ARRAY("audio_device", settings->arrays.audio_device, false, NULL, true); - SETTING_ARRAY("microphone_device", settings->arrays.microphone_device, false, NULL, true); + SETTING_ARRAY("audio_input_device", settings->arrays.audio_input_device, false, NULL, true); SETTING_ARRAY("camera_device", settings->arrays.camera_device, false, NULL, true); #ifdef HAVE_CHEEVOS SETTING_ARRAY("cheevos_custom_host", settings->arrays.cheevos_custom_host, false, NULL, true); @@ -2684,10 +2684,10 @@ void config_set_defaults(void *data) settings->arrays.audio_device, DEFAULT_AUDIO_DEVICE); - if (DEFAULT_MICROPHONE_DEVICE) + if (DEFAULT_AUDIO_INPUT_DEVICE) configuration_set_string(settings, - settings->arrays.microphone_device, - DEFAULT_MICROPHONE_DEVICE); + settings->arrays.audio_input_device, + DEFAULT_AUDIO_INPUT_DEVICE); if (!g_defaults.settings_out_latency) g_defaults.settings_out_latency = DEFAULT_OUT_LATENCY; diff --git a/configuration.h b/configuration.h index 114e60ebd48b..14a1d989527f 100644 --- a/configuration.h +++ b/configuration.h @@ -441,7 +441,7 @@ typedef struct settings #endif char audio_device[255]; - char microphone_device[255]; + char audio_input_device[255]; char camera_device[255]; char netplay_mitm_server[255]; From 4b2bfd5aeb0afd5b9cd16c85530ed5c868b54723 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 14:28:51 -0500 Subject: [PATCH 088/309] Add audio_input_latency and audio_input_block_frames settings --- audio/audio_driver.c | 4 ++-- config.def.h | 2 ++ configuration.c | 7 +++++++ configuration.h | 2 ++ defaults.h | 1 + 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index f5756f5ee684..e0f59026acca 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1832,7 +1832,7 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone const audio_driver_t *audio_driver = audio_st->current_audio; void *context = audio_st->context_audio_data; unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; - unsigned setting_audio_latency = settings->uints.audio_latency; + unsigned setting_audio_latency = settings->uints.audio_input_latency; unsigned actual_sample_rate = 0; unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? runloop_audio_latency : setting_audio_latency; @@ -1854,7 +1854,7 @@ static void audio_driver_init_microphone_internal(retro_microphone_t* microphone *settings->arrays.audio_input_device ? settings->arrays.audio_input_device : NULL, settings->uints.audio_input_sample_rate, audio_latency, - settings->uints.audio_block_frames, + settings->uints.audio_input_block_frames, &actual_sample_rate); if (!microphone->microphone_context) diff --git a/config.def.h b/config.def.h index 7c3748ef0645..e62fec68ccf6 100644 --- a/config.def.h +++ b/config.def.h @@ -1083,8 +1083,10 @@ #if defined(ANDROID) || defined(EMSCRIPTEN) || defined(RETROFW) || defined(MIYOO) /* For most Android devices, 64ms is way too low. */ #define DEFAULT_OUT_LATENCY 128 +#define DEFAULT_IN_LATENCY 128 #else #define DEFAULT_OUT_LATENCY 64 +#define DEFAULT_IN_LATENCY 64 #endif /* Will sync audio. (recommended) */ diff --git a/configuration.c b/configuration.c index 62c67415653b..2bf49a88a4b4 100644 --- a/configuration.c +++ b/configuration.c @@ -2191,8 +2191,10 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("input_rumble_gain", &settings->uints.input_rumble_gain, true, DEFAULT_RUMBLE_GAIN, false); SETTING_UINT("input_auto_game_focus", &settings->uints.input_auto_game_focus, true, DEFAULT_INPUT_AUTO_GAME_FOCUS, false); SETTING_UINT("audio_latency", &settings->uints.audio_latency, false, 0 /* TODO */, false); + SETTING_UINT("audio_input_latency", &settings->uints.audio_input_latency, false, 0 /* TODO */, false); SETTING_UINT("audio_resampler_quality", &settings->uints.audio_resampler_quality, true, DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL, false); SETTING_UINT("audio_block_frames", &settings->uints.audio_block_frames, true, 0, false); + SETTING_UINT("audio_input_block_frames", &settings->uints.audio_input_block_frames, true, 0, false); #ifdef ANDROID SETTING_UINT("input_block_timeout", &settings->uints.input_block_timeout, true, 0, false); #endif @@ -2694,6 +2696,11 @@ void config_set_defaults(void *data) settings->uints.audio_latency = g_defaults.settings_out_latency; + if (!g_defaults.settings_in_latency) + g_defaults.settings_in_latency = DEFAULT_IN_LATENCY; + + settings->uints.audio_input_latency = g_defaults.settings_in_latency; + audio_set_float(AUDIO_ACTION_VOLUME_GAIN, settings->floats.audio_volume); #ifdef HAVE_AUDIOMIXER audio_set_float(AUDIO_ACTION_MIXER_VOLUME_GAIN, settings->floats.audio_mixer_volume); diff --git a/configuration.h b/configuration.h index 14a1d989527f..e46cd0060284 100644 --- a/configuration.h +++ b/configuration.h @@ -158,7 +158,9 @@ typedef struct settings unsigned audio_output_sample_rate; unsigned audio_input_sample_rate; unsigned audio_block_frames; + unsigned audio_input_block_frames; unsigned audio_latency; + unsigned audio_input_latency; unsigned fps_update_interval; unsigned memory_update_interval; diff --git a/defaults.h b/defaults.h index ff11ff72c873..48b68d86ab8d 100644 --- a/defaults.h +++ b/defaults.h @@ -83,6 +83,7 @@ struct defaults #endif #endif int settings_out_latency; + int settings_in_latency; #ifdef HAVE_MENU unsigned menu_materialui_menu_color_theme; #endif From 41e850bbc31f43cb602b30f769d3c7570adc0b5e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 16:25:44 -0500 Subject: [PATCH 089/309] Add all mic-related settings to the options menu --- intl/msg_hash_lbl.h | 20 +++++++ intl/msg_hash_us.c | 1 + intl/msg_hash_us.h | 56 ++++++++++++++++--- menu/cbs/menu_cbs_deferred_push.c | 5 ++ menu/cbs/menu_cbs_ok.c | 5 ++ menu/cbs/menu_cbs_sublabel.c | 12 ++++ menu/cbs/menu_cbs_title.c | 2 + menu/drivers/materialui.c | 1 + menu/menu_cbs.h | 1 + menu/menu_displaylist.c | 33 +++++++++-- menu/menu_displaylist.h | 1 + menu/menu_setting.c | 93 +++++++++++++++++++++++++------ msg_hash.h | 5 ++ 13 files changed, 206 insertions(+), 29 deletions(-) diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 2501bed97982..65773ee98923 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -106,10 +106,18 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_BLOCK_FRAMES, "audio_block_frames" ) +MSG_HASH( + MENU_ENUM_LABEL_AUDIO_INPUT_BLOCK_FRAMES, + "audio_input_block_frames" + ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_DEVICE, "audio_device" ) +MSG_HASH( + MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE, + "audio_input_device" + ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_DRIVER, "audio_driver" @@ -142,6 +150,10 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_LATENCY, "audio_latency" ) +MSG_HASH( + MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, + "audio_input_latency" + ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_MAX_TIMING_SKEW, "audio_max_timing_skew" @@ -186,6 +198,10 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS, "audio_output_settings" ) +MSG_HASH( + MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, + "audio_input_settings" + ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_SYNC, "audio_sync" @@ -5845,6 +5861,10 @@ MSG_HASH( MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST, "deferred_audio_output_settings_list" ) +MSG_HASH( + MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, + "deferred_audio_input_settings_list" + ) MSG_HASH( MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, "deferred_audio_synchronization_settings_list" diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 6c7cfa06c81b..837b518fb1f0 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -336,6 +336,7 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) #endif break; case MENU_ENUM_LABEL_AUDIO_DEVICE: + case MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE: { strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_HELP_AUDIO_DEVICE), len); #ifdef HAVE_ALSA diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index fd1379618319..76d9e1a12cbd 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1158,7 +1158,7 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_SETTINGS, - "Change audio output settings." + "Change audio input/output settings." ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_SETTINGS, @@ -2365,6 +2365,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_SETTINGS, "Change audio output settings." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_SETTINGS, + "Input" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUDIO_INPUT_SETTINGS, + "Change audio input settings." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_SETTINGS, "Resampler" @@ -2500,14 +2508,6 @@ MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_ENABLE, "Enable audio output." ) -MSG_HASH( - MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, - "Microphone" -) -MSG_HASH( - MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MICROPHONE, - "Enable audio input." -) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUDIO_DEVICE, "Device" @@ -2545,6 +2545,36 @@ MSG_HASH( "Desired audio latency in milliseconds. Might not be honored if the audio driver can't provide given latency." ) +/* Settings > Audio > Input */ +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, + "Microphone Input" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MICROPHONE, + "Enable audio input. Requires audio output to be enabled as well." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_DEVICE, + "Device" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUDIO_INPUT_DEVICE, + "Override the default input device the audio driver uses. This is driver dependent." + ) +MSG_HASH( + MENU_ENUM_LABEL_HELP_AUDIO_INPUT_DEVICE, + "Override the default input device the audio driver uses. This is driver dependent." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_LATENCY, + "Audio Input Latency (ms)" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUDIO_INPUT_LATENCY, + "Desired audio input latency in milliseconds. Might not be honored if the audio driver can't provide given latency." + ) + /* Settings > Audio > Resampler */ MSG_HASH( @@ -2563,6 +2593,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE, "Audio output sample rate." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_RATE, + "Input Rate (Hz)" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_AUDIO_INPUT_RATE, + "Audio input sample rate." + ) /* Settings > Audio > Synchronization */ diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 8f6cca20ffb2..4a0b5d721533 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -223,6 +223,7 @@ GENERIC_DEFERRED_PUSH(deferred_push_privacy_settings_list, DISPLAYLIST_ GENERIC_DEFERRED_PUSH(deferred_push_midi_settings_list, DISPLAYLIST_MIDI_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_settings_list, DISPLAYLIST_AUDIO_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_output_settings_list, DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST) +GENERIC_DEFERRED_PUSH(deferred_push_audio_input_settings_list, DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_resampler_settings_list, DISPLAYLIST_AUDIO_RESAMPLER_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_synchronization_settings_list, DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_mixer_settings_list, DISPLAYLIST_AUDIO_MIXER_SETTINGS_LIST) @@ -781,6 +782,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label( {MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, deferred_push_audio_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, deferred_push_audio_synchronization_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST, deferred_push_audio_output_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, deferred_push_audio_input_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST, deferred_push_audio_resampler_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_MIXER_SETTINGS_LIST, deferred_push_audio_mixer_settings_list}, {MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST, deferred_push_latency_settings_list}, @@ -1294,6 +1296,9 @@ static int menu_cbs_init_bind_deferred_push_compare_label( case MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_output_settings_list); break; + case MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST: + BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_input_settings_list); + break; case MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_resampler_settings_list); break; diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 0e3d97b821fa..8b01deabaf69 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -456,6 +456,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST; case ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST; + case ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST: + return MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST; case ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST; case ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: @@ -1635,6 +1637,7 @@ int generic_action_ok_displaylist_push(const char *path, case ACTION_OK_DL_AUDIO_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST: + case ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_MIXER_SETTINGS_LIST: case ACTION_OK_DL_INPUT_HOTKEY_BINDS_LIST: @@ -5919,6 +5922,7 @@ DEFAULT_ACTION_OK_FUNC(action_ok_push_core_restore_backup_list, ACTION_OK_DL_COR DEFAULT_ACTION_OK_FUNC(action_ok_push_core_delete_backup_list, ACTION_OK_DL_CORE_DELETE_BACKUP_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_settings_list, ACTION_OK_DL_AUDIO_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_output_settings_list, ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST) +DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_input_settings_list, ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_resampler_settings_list, ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_synchronization_settings_list, ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST) #ifdef HAVE_AUDIOMIXER @@ -8306,6 +8310,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL, action_ok_close_submenu }, {MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST, action_ok_push_manual_content_scan_list}, {MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS, action_ok_push_audio_output_settings_list}, + {MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, action_ok_push_audio_input_settings_list}, {MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS, action_ok_push_audio_resampler_settings_list}, {MENU_ENUM_LABEL_LATENCY_SETTINGS, action_ok_push_latency_settings_list}, {MENU_ENUM_LABEL_CORE_SETTINGS, action_ok_push_core_settings_list}, diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 24e88ed4f903..5dcd178577a6 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -341,6 +341,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_window_show_menubar, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_settings_list, MENU_ENUM_SUBLABEL_AUDIO_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_resampler_settings_list, MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_output_settings_list, MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_SETTINGS) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_settings_list, MENU_ENUM_SUBLABEL_AUDIO_INPUT_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_synchronization_settings_list, MENU_ENUM_SUBLABEL_AUDIO_SYNCHRONIZATION_SETTINGS) #ifdef HAVE_AUDIOMIXER DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_mixer_settings_list, MENU_ENUM_SUBLABEL_AUDIO_MIXER_SETTINGS) @@ -488,6 +489,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_configurations_list_list, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_shared_context, MENU_ENUM_SUBLABEL_VIDEO_SHARED_CONTEXT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_driver_switch_enable, MENU_ENUM_SUBLABEL_DRIVER_SWITCH_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_latency, MENU_ENUM_SUBLABEL_AUDIO_LATENCY) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_latency, MENU_ENUM_SUBLABEL_AUDIO_INPUT_LATENCY) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_rate_control_delta, MENU_ENUM_SUBLABEL_AUDIO_RATE_CONTROL_DELTA) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_mute, MENU_ENUM_SUBLABEL_AUDIO_MUTE) #ifdef HAVE_AUDIOMIXER @@ -769,6 +771,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_filter_supported_extensions, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_wallpaper, MENU_ENUM_SUBLABEL_MENU_WALLPAPER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_dynamic_wallpaper, MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_device, MENU_ENUM_SUBLABEL_AUDIO_DEVICE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_device, MENU_ENUM_SUBLABEL_AUDIO_INPUT_DEVICE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_output_rate, MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_rate, MENU_ENUM_SUBLABEL_AUDIO_INPUT_RATE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_dsp_plugin, MENU_ENUM_SUBLABEL_AUDIO_DSP_PLUGIN) @@ -3544,6 +3547,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_DEVICE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_device); break; + case MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_device); + break; case MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_wasapi_exclusive_mode); break; @@ -4285,6 +4291,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_LATENCY: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_latency); break; + case MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_latency); + break; case MENU_ENUM_LABEL_DRIVER_SWITCH_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_driver_switch_enable); break; @@ -4607,6 +4616,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_output_settings_list); break; + case MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_settings_list); + break; case MENU_ENUM_LABEL_AUDIO_MIXER_SETTINGS: #ifdef HAVE_AUDIOMIXER BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_mixer_settings_list); diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index a47b57d7f89c..91fff237ca18 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -681,6 +681,7 @@ DEFAULT_TITLE_MACRO(action_get_updater_settings_list, MENU_ENUM_LABEL_ DEFAULT_TITLE_MACRO(action_get_audio_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_SETTINGS) DEFAULT_TITLE_MACRO(action_get_audio_resampler_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_SETTINGS) DEFAULT_TITLE_MACRO(action_get_audio_output_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_OUTPUT_SETTINGS) +DEFAULT_TITLE_MACRO(action_get_audio_input_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_SETTINGS) DEFAULT_TITLE_MACRO(action_get_audio_synchronization_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_SYNCHRONIZATION_SETTINGS) #ifdef HAVE_AUDIOMIXER DEFAULT_TITLE_MACRO(action_get_audio_mixer_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_SETTINGS) @@ -1052,6 +1053,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, action_get_audio_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST, action_get_audio_resampler_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST, action_get_audio_output_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, action_get_audio_input_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, action_get_audio_synchronization_settings_list}, #ifdef HAVE_AUDIOMIXER {MENU_ENUM_LABEL_DEFERRED_AUDIO_MIXER_SETTINGS_LIST, action_get_audio_mixer_settings_list}, diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 353b3c6ca67e..83f46d9f3a50 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -10756,6 +10756,7 @@ static void materialui_list_insert( string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_SYNCHRONIZATION_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_MIXER_SETTINGS)) || diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 0f2ea3bc8d86..1f6a57f5b2bf 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -110,6 +110,7 @@ enum ACTION_OK_DL_CRT_SWITCHRES_SETTINGS_LIST, ACTION_OK_DL_AUDIO_SETTINGS_LIST, ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST, + ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST, ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST, ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, ACTION_OK_DL_AUDIO_MIXER_SETTINGS_LIST, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index dde6b47cdaa6..094602afbe4f 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6644,10 +6644,6 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_AUDIO_ENABLE, PARSE_ONLY_BOOL, false) == 0) count++; - if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, - PARSE_ONLY_BOOL, false) == 0) - count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_AUDIO_DRIVER, PARSE_ONLY_STRING_OPTIONS, false) == 0) @@ -6656,6 +6652,10 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_AUDIO_DEVICE, PARSE_ONLY_STRING, false) == 0) count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE, + PARSE_ONLY_UINT, false) == 0) + count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_AUDIO_LATENCY, PARSE_ONLY_UINT, false) == 0) @@ -6677,6 +6677,28 @@ unsigned menu_displaylist_build_list( PARSE_ONLY_UINT, false) == 0) count++; break; + case DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST: + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE, + PARSE_ONLY_STRING, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_INPUT_RATE, + PARSE_ONLY_UINT, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, + PARSE_ONLY_UINT, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_AUDIO_INPUT_BLOCK_FRAMES, + PARSE_ONLY_UINT, false) == 0) + count++; + break; case DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_AUDIO_SYNC, @@ -6701,6 +6723,7 @@ unsigned menu_displaylist_build_list( #endif menu_displaylist_build_info_selective_t build_list[] = { {MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS, PARSE_ACTION, true }, + {MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_AUDIO_SYNCHRONIZATION_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_MIDI_SETTINGS, PARSE_ACTION, true }, @@ -9279,6 +9302,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO, PARSE_ONLY_BOOL, true }, {MENU_ENUM_LABEL_AUDIO_LATENCY, PARSE_ONLY_UINT, true }, + {MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_INPUT_BLOCK_TIMEOUT, PARSE_ONLY_UINT, true }, #ifdef HAVE_RUNAHEAD @@ -13268,6 +13292,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, case DISPLAYLIST_AUDIO_SETTINGS_LIST: case DISPLAYLIST_AUDIO_RESAMPLER_SETTINGS_LIST: case DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST: + case DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST: case DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: case DISPLAYLIST_HELP_SCREEN_LIST: case DISPLAYLIST_INFORMATION_LIST: diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index d6e52fd31264..27376d07e89a 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -185,6 +185,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_AUDIO_SETTINGS_LIST, DISPLAYLIST_AUDIO_RESAMPLER_SETTINGS_LIST, DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST, + DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST, DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, DISPLAYLIST_AUDIO_MIXER_SETTINGS_LIST, DISPLAYLIST_CORE_SETTINGS_LIST, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 9583f47f3703..c8bd064b686b 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -8332,6 +8332,7 @@ static void general_write_handler(rarch_setting_t *setting) #endif break; case MENU_ENUM_LABEL_AUDIO_LATENCY: + case MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY: case MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE: case MENU_ENUM_LABEL_AUDIO_INPUT_RATE: case MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE: @@ -10384,6 +10385,14 @@ static bool setting_append_list( &subgroup_info, parent_group); + CONFIG_ACTION( + list, list_info, + MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_SETTINGS, + &group_info, + &subgroup_info, + parent_group); + CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_AUDIO_SYNCHRONIZATION_SETTINGS, @@ -13433,22 +13442,6 @@ static bool setting_append_list( SD_FLAG_NONE ); - CONFIG_BOOL( - list, list_info, - &settings->bools.audio_enable_microphone, - MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, - MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, - DEFAULT_AUDIO_ENABLE_MICROPHONE, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler, - SD_FLAG_NONE - ); - CONFIG_BOOL( list, list_info, audio_get_bool_ptr(AUDIO_ACTION_MUTE_ENABLE), @@ -13576,6 +13569,22 @@ static bool setting_append_list( menu_settings_list_current_add_range(list, list_info, 0, 512, 1.0, true, true); SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED); + CONFIG_UINT( + list, list_info, + &settings->uints.audio_input_latency, + MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_LATENCY, + g_defaults.settings_in_latency ? + g_defaults.settings_in_latency : DEFAULT_IN_LATENCY, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 0, 512, 1.0, true, true); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED); + CONFIG_UINT( list, list_info, &settings->uints.audio_resampler_quality, @@ -13651,12 +13660,46 @@ static bool setting_append_list( general_write_handler, general_read_handler); SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); + + CONFIG_UINT( + list, list_info, + &settings->uints.audio_input_block_frames, + MENU_ENUM_LABEL_AUDIO_INPUT_BLOCK_FRAMES, + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_BLOCK_FRAMES, + 0, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); #endif END_SUB_GROUP(list, list_info, parent_group); parent_group = msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS); + START_SUB_GROUP(list, list_info, "State", &group_info, &subgroup_info, parent_group); + + CONFIG_BOOL( + list, list_info, + &settings->bools.audio_enable_input, + MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, + MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, + DEFAULT_AUDIO_ENABLE_INPUT, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + + END_SUB_GROUP(list, list_info, parent_group); + + parent_group = msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS); + START_SUB_GROUP( list, list_info, @@ -13683,6 +13726,24 @@ static bool setting_append_list( (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; (*list)[list_info->index - 1].action_left = &setting_string_action_left_audio_device; (*list)[list_info->index - 1].action_right = &setting_string_action_right_audio_device; + + CONFIG_STRING( + list, list_info, + settings->arrays.audio_input_device, + sizeof(settings->arrays.audio_input_device), + MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE, + MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_DEVICE, + "", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT; + (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; + (*list)[list_info->index - 1].action_left = &setting_string_action_left_audio_device; + (*list)[list_info->index - 1].action_right = &setting_string_action_right_audio_device; #endif CONFIG_UINT( diff --git a/msg_hash.h b/msg_hash.h index 00f04b1a8fc1..5c2d0a423e46 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1911,6 +1911,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST, + MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_MIXER_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_CORE_SETTINGS_LIST, @@ -2056,7 +2057,9 @@ enum msg_hash_enums MENU_ENUM_LABEL_HELP_AUDIO_DEVICE_OSS, MENU_ENUM_LABEL_HELP_AUDIO_DEVICE_JACK, MENU_ENUM_LABEL_HELP_AUDIO_DEVICE_RSOUND, + MENU_LBL_H(AUDIO_INPUT_DEVICE), MENU_LABEL(AUDIO_BLOCK_FRAMES), + MENU_LABEL(AUDIO_INPUT_BLOCK_FRAMES), MENU_LABEL(AUDIO_DSP_PLUGIN), MENU_LABEL(AUDIO_DSP_PLUGIN_REMOVE), MENU_LABEL(AUDIO_MUTE), @@ -2067,6 +2070,7 @@ enum msg_hash_enums MENU_LABEL(AUDIO_MIXER_VOLUME), MENU_LBL_H(AUDIO_RATE_CONTROL_DELTA), MENU_LABEL(AUDIO_LATENCY), + MENU_LABEL(AUDIO_INPUT_LATENCY), MENU_LABEL(AUDIO_RESAMPLER_QUALITY), MENU_LABEL(AUDIO_WASAPI_EXCLUSIVE_MODE), MENU_LABEL(AUDIO_WASAPI_FLOAT_FORMAT), @@ -2899,6 +2903,7 @@ enum msg_hash_enums MENU_LABEL(AUDIO_SETTINGS), MENU_LABEL(AUDIO_RESAMPLER_SETTINGS), MENU_LABEL(AUDIO_OUTPUT_SETTINGS), + MENU_LABEL(AUDIO_INPUT_SETTINGS), MENU_LABEL(AUDIO_SYNCHRONIZATION_SETTINGS), MENU_LABEL(AUDIO_MIXER_SETTINGS), MENU_LABEL(LATENCY_SETTINGS), From 68254a96089dd806ee9e31274f28faa71658d600 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 17:51:40 -0500 Subject: [PATCH 090/309] Adjust logging for alsa.c - Log the ALSA library version - Add errno details --- audio/drivers/alsa.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 774f92734cfd..0b13f85f9862 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -63,19 +63,19 @@ static void alsa_log_error(const char *file, int line, const char *function, int { va_list args; char temp[256]; + char errno_temp[256]; memset(temp, 0, sizeof(temp)); - - if (err) - { - RARCH_ERR("[ALSA] [System] Error in %s:%s:%d: %s\n", file, function, line, snd_strerror(err)); - } + memset(errno_temp, 0, sizeof(temp)); va_start(args, fmt); vsnprintf(temp, sizeof(temp), fmt, args); + if (err) + snprintf(errno_temp, sizeof(errno_temp), " (%s)", snd_strerror(err)); + /* Write up to 255 characters. (The 256th will be \0.) */ va_end(args); - RARCH_ERR("[ALSA] [%s:%s:%d]: %s\n", file, function, line, temp); /* To ensure that there's a newline at the end */ + RARCH_ERR("[ALSA] [%s:%s:%d]: %s%s\n", file, function, line, temp, errno_temp); /* To ensure that there's a newline at the end */ } static bool find_float_format(snd_pcm_t *pcm, void *data) @@ -116,6 +116,7 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, if (device) alsa_dev = device; + RARCH_LOG("[ALSA] Using ALSA version %s\n", snd_asoundlib_version()); RARCH_DBG("[ALSA] Requesting device \"%s\" for output\n", alsa_dev); if (snd_pcm_open( From a5d1cf7347f89d4299b19ae0c6429826d56eed3b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 18:26:57 -0500 Subject: [PATCH 091/309] Refer to the microphone in logs by name --- audio/drivers/alsa.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 0b13f85f9862..ac250467997f 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -346,8 +346,8 @@ static bool alsa_stop(void *data) int errnum = snd_pcm_pause(alsa->microphone->pcm, true); if (errnum < 0) { - RARCH_ERR("[ALSA]: Failed to pause microphone %x: %s\n", - alsa->microphone->pcm, + RARCH_ERR("[ALSA]: Failed to pause microphone \"%s\": %s\n", + snd_pcm_name(alsa->microphone->pcm), snd_strerror(errnum)); return false; } @@ -681,7 +681,7 @@ static bool alsa_set_microphone_state(void *data, void *microphone_context, bool if (!microphone->can_pause) { - RARCH_WARN("[ALSA]: Microphone %x cannot be paused\n", microphone->pcm); + RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->pcm)); return true; } @@ -690,16 +690,16 @@ static bool alsa_set_microphone_state(void *data, void *microphone_context, bool int errnum = snd_pcm_pause(microphone->pcm, !enabled); if (errnum < 0) { - RARCH_ERR("[ALSA]: Failed to %s microphone %x: %s\n", + RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\": %s\n", enabled ? "unpause" : "pause", - microphone->pcm, + snd_pcm_name(microphone->pcm), snd_strerror(errnum)); return false; } microphone->is_paused = !enabled; - RARCH_DBG("[ALSA]: Set state of microphone %x to %s\n", - microphone->pcm, enabled ? "enabled" : "disabled"); + RARCH_DBG("[ALSA]: Set state of microphone \"%s\" to %s\n", + snd_pcm_name(microphone->pcm), enabled ? "enabled" : "disabled"); } return true; From 7042eff75333641865847d832a4f99fb67462553 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 19:36:42 -0500 Subject: [PATCH 092/309] Use %u instead of %d for some log items --- audio/drivers/alsa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index ac250467997f..9dee6fe8da48 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -594,12 +594,12 @@ static void *alsa_init_microphone(void *data, if (snd_pcm_hw_params_get_period_size(params, &buffer_size, NULL)) snd_pcm_hw_params_get_period_size_min(params, &buffer_size, NULL); - RARCH_LOG("[ALSA]: Microphone period size: %d frames\n", (int)buffer_size); + RARCH_LOG("[ALSA]: Microphone period size: %u frames\n", buffer_size); if (snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size); - RARCH_LOG("[ALSA]: Microphone buffer size: %d frames\n", (int)buffer_size); + RARCH_LOG("[ALSA]: Microphone buffer size: %u frames\n", buffer_size); microphone->buffer_size = snd_pcm_frames_to_bytes(microphone->pcm, buffer_size); microphone->can_pause = snd_pcm_hw_params_can_pause(params); From 811cb1440e117e3b6a2a852a30d13125b6ac3fd3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 21:16:40 -0500 Subject: [PATCH 093/309] Add input_samples_buf --- audio/audio_driver.c | 4 +++- audio/audio_driver.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index e0f59026acca..cd205323fa34 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -633,7 +633,8 @@ bool audio_driver_init_internal( int16_t *out_conv_buf = (int16_t*)memalign_alloc(64, outsamples_max * sizeof(int16_t)); int16_t *in_conv_buf = audio_enable_microphone ? (int16_t*)memalign_alloc(64, insamples_max * sizeof(int16_t)) : NULL; - float *audio_buf = (float*)memalign_alloc(64, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float)); + size_t audio_buf_length = AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float); + float *audio_buf = (float*)memalign_alloc(64, audio_buf_length); bool verbosity_enabled = verbosity_is_enabled(); convert_s16_to_float_init_simd(); @@ -646,6 +647,7 @@ bool audio_driver_init_internal( memset(audio_buf, 0, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float)); audio_driver_st.input_data = audio_buf; + audio_driver_st.input_data_length = audio_buf_length; audio_driver_st.output_samples_conv_buf = out_conv_buf; audio_driver_st.output_samples_conv_buf_length = outsamples_max * sizeof(int16_t); audio_driver_st.input_samples_conv_buf = in_conv_buf; diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 2a0f95e03dce..b7e01e3f6896 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -411,6 +411,7 @@ typedef struct * Scratch buffer for preparing data for the resampler */ float *input_data; + size_t input_data_length; #ifdef HAVE_AUDIOMIXER struct audio_mixer_stream mixer_streams[AUDIO_MIXER_MAX_SYSTEM_STREAMS]; From 77f5330852c8f9b321c0c491aa64b07a601c48e4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 21:16:49 -0500 Subject: [PATCH 094/309] Remove an inaccurate comment --- audio/audio_driver.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index b7e01e3f6896..5b4168f39577 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -327,10 +327,6 @@ typedef struct audio_driver * Unless said otherwise with set_nonblock_state(), all reads * are blocking, and it should block till it has read all frames. * - * This function is not exposed to the core. - * Instead, the function exposed to the core just copies - * the most recent data that was read by this - * * @param[in] driver_context Opaque handle to the audio driver context * that was used to create the provided microphone. * @param[in] microphone_context Opaque handle to the microphone From 560eddfbe48d053e4c8652cbe5aea676acd68f4b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 21:17:06 -0500 Subject: [PATCH 095/309] Change type of input_samples_buf --- audio/audio_driver.c | 2 +- audio/audio_driver.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index cd205323fa34..b51bbe4d5ab3 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -798,7 +798,7 @@ bool audio_driver_init_internal( out_samples_buf = (float*)memalign_alloc(64, outsamples_max * sizeof(float)); in_samples_buf = (audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) ? - (float*)memalign_alloc(64, insamples_max * sizeof(float)) : NULL; + memalign_alloc(64, insamples_max * sizeof(float)) : NULL; /* It's not an error for in_samples_buf to be NULL if we don't want the mic */ if (!out_samples_buf || ((audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) && !in_samples_buf)) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 5b4168f39577..97989d49e6b5 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -368,7 +368,7 @@ typedef struct */ float *output_samples_buf; size_t output_samples_buf_length; - float *input_samples_buf; + void *input_samples_buf; size_t input_samples_buf_length; #ifdef HAVE_REWIND int16_t *rewind_buf; From 11577090965951262501db42ecb1a4ac3c9a1b23 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 21:17:44 -0500 Subject: [PATCH 096/309] Clean up audio_driver_flush_microphone_input --- audio/audio_driver.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index b51bbe4d5ab3..c091882d0638 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -2075,9 +2075,12 @@ static void audio_driver_flush_microphone_input( microphone->microphone_context)) { struct resampler_data src_data; - unsigned sample_size = audio_driver_get_sample_size(); - size_t bytes_to_read = MIN(audio_st->input_samples_buf_length, num_frames * sample_size); - ssize_t bytes_read = audio_st->current_audio->read_microphone( + void *buffer_source = NULL; + unsigned sample_size = audio_driver_get_sample_size(); + float audio_volume_gain = (audio_st->mute_enable || (audio_fastforward_mute && is_fastmotion)) + ? 0.0f : audio_st->volume_gain; + size_t bytes_to_read = MIN(audio_st->input_samples_buf_length, num_frames * sample_size); + ssize_t bytes_read = audio_st->current_audio->read_microphone( audio_st->context_audio_data, microphone->microphone_context, audio_st->input_samples_buf, @@ -2104,28 +2107,20 @@ static void audio_driver_flush_microphone_input( if (is_slowmotion) src_data.ratio *= slowmotion_ratio; - /* - float audio_volume_gain = (audio_st->mute_enable || (audio_fastforward_mute && is_fastmotion)) - ? 0.0f - : audio_st->volume_gain;*/ - - /* if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) { - src_data.data_in = audio_st->input_samples_buf; + convert_float_to_s16(audio_st->input_samples_conv_buf, audio_st->input_samples_buf, bytes_read / sample_size); + buffer_source = audio_st->input_samples_conv_buf; } else { - convert_s16_to_float(audio_st->input_data, audio_st->input_samples_buf, bytes_read / sample_size / 2, - audio_volume_gain); - src_data.data_in = audio_st->input_data; + buffer_source = audio_st->input_samples_buf; } - audio_st->resampler->process(audio_st->resampler_data, &src_data); - - convert_float_to_s16(microphone->sample_buffer, src_data.data_out, src_data.output_frames);*/ + // TODO: Run converted (if necessary) samples through the resampler) + // TODO: Convert resampled data to int16_t's - memcpy(frames, audio_st->input_samples_buf, num_frames * sample_size); + memcpy(frames, buffer_source, num_frames * sample_size); } } From 2589ddd2b31d17c83d85d63397aeeecefb83fc94 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 21:18:40 -0500 Subject: [PATCH 097/309] Comment convert_float_to_s16 - It helped me understand what it's doing - Turns out it'll work just fine on mono audio --- .../audio/conversion/float_to_s16.c | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/libretro-common/audio/conversion/float_to_s16.c b/libretro-common/audio/conversion/float_to_s16.c index 770981d9c1cb..6f65fe1588d8 100644 --- a/libretro-common/audio/conversion/float_to_s16.c +++ b/libretro-common/audio/conversion/float_to_s16.c @@ -97,22 +97,25 @@ void convert_float_to_s16(int16_t *out, size_t i = 0; #if defined(__SSE2__) __m128 factor = _mm_set1_ps((float)0x8000); + /* Initialize a 4D vector with 32768.0 for its elements */ for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8) - { - __m128 input_l = _mm_loadu_ps(in + 0); - __m128 input_r = _mm_loadu_ps(in + 4); - __m128 res_l = _mm_mul_ps(input_l, factor); - __m128 res_r = _mm_mul_ps(input_r, factor); - __m128i ints_l = _mm_cvtps_epi32(res_l); - __m128i ints_r = _mm_cvtps_epi32(res_r); - __m128i packed = _mm_packs_epi32(ints_l, ints_r); - - _mm_storeu_si128((__m128i *)out, packed); + { /* Skip forward 8 samples at a time... */ + __m128 input_a = _mm_loadu_ps(in + 0); /* Create a 4-float vector from the next four samples... */ + __m128 input_b = _mm_loadu_ps(in + 4); /* ...and another from the *next* next four. */ + __m128 res_a = _mm_mul_ps(input_a, factor); + __m128 res_b = _mm_mul_ps(input_b, factor); /* Multiply these samples by 32768 */ + __m128i ints_a = _mm_cvtps_epi32(res_a); + __m128i ints_b = _mm_cvtps_epi32(res_b); /* Convert the samples to 32-bit integers */ + __m128i packed = _mm_packs_epi32(ints_a, ints_b); /* Then convert them to 16-bit ints, clamping to [-32768, 32767] */ + + _mm_storeu_si128((__m128i *)out, packed); /* Then put the result in the output array */ } samples = samples - i; i = 0; + /* If there are any stray samples at the end, we need to convert them + * (maybe the original array didn't contain a multiple of 8 samples) */ #elif defined(__ALTIVEC__) int samples_in = samples; @@ -165,6 +168,8 @@ void convert_float_to_s16(int16_t *out, } #endif + /* This loop converts stray samples to the right format, + * but it's also a fallback in case no SIMD instructions are available. */ for (; i < samples; i++) { int32_t val = (int32_t)(in[i] * 0x8000); From 13e3be71e3b29f0560f878b34fd2b9c991148e09 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 22:14:26 -0500 Subject: [PATCH 098/309] Don't use the resampler for mic input --- audio/audio_driver.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index c091882d0638..174e9a9c99a1 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -2074,13 +2074,10 @@ static void audio_driver_flush_microphone_input( audio_st->current_audio, microphone->microphone_context)) { - struct resampler_data src_data; - void *buffer_source = NULL; - unsigned sample_size = audio_driver_get_sample_size(); - float audio_volume_gain = (audio_st->mute_enable || (audio_fastforward_mute && is_fastmotion)) - ? 0.0f : audio_st->volume_gain; - size_t bytes_to_read = MIN(audio_st->input_samples_buf_length, num_frames * sample_size); - ssize_t bytes_read = audio_st->current_audio->read_microphone( + void *buffer_source = NULL; + unsigned sample_size = audio_driver_get_sample_size(); + size_t bytes_to_read = MIN(audio_st->input_samples_buf_length, num_frames * sample_size); + ssize_t bytes_read = audio_st->current_audio->read_microphone( audio_st->context_audio_data, microphone->microphone_context, audio_st->input_samples_buf, @@ -2098,15 +2095,6 @@ static void audio_driver_flush_microphone_input( return; } - src_data.data_in = NULL; /* Will be assigned later */ - src_data.input_frames = num_frames; - src_data.data_out = audio_st->output_samples_buf; - src_data.output_frames = 0; /* Will be assigned by the resampler */ - src_data.ratio = audio_st->source_ratio_current; - - if (is_slowmotion) - src_data.ratio *= slowmotion_ratio; - if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) { convert_float_to_s16(audio_st->input_samples_conv_buf, audio_st->input_samples_buf, bytes_read / sample_size); @@ -2117,9 +2105,6 @@ static void audio_driver_flush_microphone_input( buffer_source = audio_st->input_samples_buf; } - // TODO: Run converted (if necessary) samples through the resampler) - // TODO: Convert resampled data to int16_t's - memcpy(frames, buffer_source, num_frames * sample_size); } } From 5f7565f59047905f44fee9ac4a80e817aeb40d86 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 23:08:49 -0500 Subject: [PATCH 099/309] Fix crash in the ALSA driver when reading from a mic --- audio/audio_driver.c | 2 +- audio/drivers/alsa.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 174e9a9c99a1..063de9ee47f5 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -2105,7 +2105,7 @@ static void audio_driver_flush_microphone_input( buffer_source = audio_st->input_samples_buf; } - memcpy(frames, buffer_source, num_frames * sample_size); + memcpy(frames, buffer_source, num_frames * sizeof(int16_t)); } } diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 9dee6fe8da48..d448e3cc19c1 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -799,7 +799,7 @@ static ssize_t alsa_read_microphone(void *driver_context, void *microphone_conte } } - return read * frames_size; + return FRAMES_TO_BYTES(read, microphone->frame_bits); } audio_driver_t audio_alsa = { From 4e05fbda2e4cb90f5aa06513826932b08fe7e140 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 23:09:39 -0500 Subject: [PATCH 100/309] Update some logging messages --- audio/drivers/alsa.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index d448e3cc19c1..960a7f576135 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -536,9 +536,13 @@ static void *alsa_init_microphone(void *data, errnum = snd_pcm_open(µphone->pcm, alsa_mic_dev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (errnum < 0) { - RARCH_ERR("[ALSA]: Failed to open microphone device: %s\n", snd_strerror(errnum)); + RARCH_ERR("[ALSA]: Failed to open input device: %s\n", snd_strerror(errnum)); goto error; } + else + { + RARCH_DBG("[ALSA]: Opened input device with name \"%s\"\n", snd_pcm_name(microphone->pcm)); + } if (snd_pcm_hw_params_malloc(¶ms) < 0) goto error; From 2b337d047ab7af18d1910b036e46b99d9aeb8e28 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 15 Jan 2023 23:23:19 -0500 Subject: [PATCH 101/309] ALSA support now works for mics --- audio/drivers/alsa.c | 69 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 960a7f576135..6d92c837b08d 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -110,8 +110,8 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, if (!alsa) return NULL; - alsa->prev_error_handler = snd_lib_error; - snd_lib_error_set_handler(alsa_log_error); + //alsa->prev_error_handler = snd_lib_error; + //snd_lib_error_set_handler(alsa_log_error); if (device) alsa_dev = device; @@ -384,13 +384,47 @@ static bool alsa_start(void *data, bool is_shutdown) if (alsa->microphone && !alsa->microphone->is_paused) { /* If the mic wasn't paused at the time the overall driver was paused... */ - int errnum = snd_pcm_pause(alsa->microphone->pcm, false); - if (errnum < 0) + snd_pcm_state_t mic_state = snd_pcm_state(alsa->microphone->pcm); + + /* If we're calling this function with a pending microphone, + * (as happens when a core requests a microphone at the start), + * the mic will be in the PREPARED state rather than the PAUSED state. + **/ + switch (mic_state) { - RARCH_ERR("[ALSA]: Failed to unpause microphone %x: %s\n", - alsa->microphone->pcm, - snd_strerror(errnum)); - return false; + case SND_PCM_STATE_PREPARED: + { + int errnum = snd_pcm_start(alsa->microphone->pcm); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", + snd_pcm_name(alsa->microphone->pcm), + snd_strerror(errnum)); + + return false; + } + break; + } + case SND_PCM_STATE_PAUSED: + { + int errnum = snd_pcm_pause(alsa->microphone->pcm, false); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to unpause microphone \"%s\": %s\n", + snd_pcm_name(alsa->microphone->pcm), + snd_strerror(errnum)); + + return false; + } + break; + } + default: + { + RARCH_ERR("[ALSA]: Expected microphone \"%s\" to be in state PREPARED or PAUSED, it was in %s\n", + snd_pcm_name(alsa->microphone->pcm), + snd_pcm_state_name(mic_state)); + return false; + } } } } @@ -718,6 +752,7 @@ static ssize_t alsa_read_microphone(void *driver_context, void *microphone_conte int errnum = 0; snd_pcm_sframes_t size; size_t frames_size; + snd_pcm_state_t state; if (!alsa || !microphone || !buf) return -1; @@ -731,6 +766,24 @@ static ssize_t alsa_read_microphone(void *driver_context, void *microphone_conte if (!alsa_set_microphone_state(alsa, microphone, true)) return -1; + state = snd_pcm_state(microphone->pcm); + if (state != SND_PCM_STATE_RUNNING) + { + RARCH_WARN("[ALSA] Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", + snd_pcm_name(microphone->pcm), + snd_pcm_state_name(state)); + + errnum = snd_pcm_start(microphone->pcm); + if (errnum < 0) + { + RARCH_ERR("[ALSA] Failed to start microphone \"%s\": %s\n", + snd_pcm_name(microphone->pcm), + snd_strerror(errnum)); + + return -1; + } + } + if (alsa->nonblock) { while (size) From 67b760330009a86cac2a1863468f0163e7e87c4d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 07:20:46 -0500 Subject: [PATCH 102/309] Reuse some common functions in alsa.c --- audio/drivers/alsa.c | 13 ++++++------- audio/drivers/alsa.h | 26 ++++++++++++++++++++++++++ audio/drivers/alsathread.c | 19 ++++--------------- 3 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 audio/drivers/alsa.h diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 6d92c837b08d..2d7134c213f1 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -22,6 +22,7 @@ #include #include "../audio_driver.h" +#include "alsa.h" #include "../../verbosity.h" typedef struct alsa_microphone @@ -78,10 +79,8 @@ static void alsa_log_error(const char *file, int line, const char *function, int RARCH_ERR("[ALSA] [%s:%s:%d]: %s%s\n", file, function, line, temp, errno_temp); /* To ensure that there's a newline at the end */ } -static bool find_float_format(snd_pcm_t *pcm, void *data) +bool alsa_find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { - snd_pcm_hw_params_t *params = (snd_pcm_hw_params_t*)data; - if (snd_pcm_hw_params_test_format(pcm, params, SND_PCM_FORMAT_FLOAT) == 0) { RARCH_LOG("[ALSA]: Using floating point format.\n"); @@ -129,7 +128,7 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, if (snd_pcm_hw_params_any(alsa->pcm, params) < 0) goto error; - alsa->has_float = find_float_format(alsa->pcm, params); + alsa->has_float = alsa_find_float_format(alsa->pcm, params); format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; if (snd_pcm_hw_params_set_access( @@ -477,7 +476,7 @@ static size_t alsa_buffer_size(void *data) return alsa->buffer_size; } -static void *alsa_device_list_new(void *data) +void *alsa_device_list_new(void *data) { void **hints, **n; union string_list_elem_attr attr; @@ -525,7 +524,7 @@ static void *alsa_device_list_new(void *data) return NULL; } -static void alsa_device_list_free(void *data, void *array_list_data) +void alsa_device_list_free(void *data, void *array_list_data) { struct string_list *s = (struct string_list*)array_list_data; @@ -584,7 +583,7 @@ static void *alsa_init_microphone(void *data, if (snd_pcm_hw_params_any(microphone->pcm, params) < 0) goto error; - microphone->has_float = find_float_format(microphone->pcm, params); + microphone->has_float = alsa_find_float_format(microphone->pcm, params); format = microphone->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; if (microphone->has_float != alsa->has_float) diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h new file mode 100644 index 000000000000..caf82dbb1337 --- /dev/null +++ b/audio/drivers/alsa.h @@ -0,0 +1,26 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 The RetroArch team + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + + +#ifndef _RETROARCH_ALSA +#define _RETROARCH_ALSA + +/* Header file for common functions that are used by alsa and alsathread. */ + +bool alsa_find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +void *alsa_device_list_new(void *data); +void alsa_device_list_free(void *data, void *array_list_data); + +#endif /* _RETROARCH_ALSA */ diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 1e239ef119fa..3b55f3a9bc9e 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -25,6 +25,7 @@ #include #include "../audio_driver.h" +#include "alsa.h" #include "../../verbosity.h" #define TRY_ALSA(x) if (x < 0) \ @@ -109,18 +110,6 @@ static bool alsa_thread_use_float(void *data) return alsa->has_float; } -static bool alsathread_find_float_format(snd_pcm_t *pcm, - snd_pcm_hw_params_t *params) -{ - if (snd_pcm_hw_params_test_format(pcm, params, SND_PCM_FORMAT_FLOAT) == 0) - { - RARCH_LOG("ALSA: Using floating point format.\n"); - return true; - } - RARCH_LOG("ALSA: Using signed 16-bit format.\n"); - return false; -} - static void alsa_thread_free(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; @@ -175,7 +164,7 @@ static void *alsa_thread_init(const char *device, TRY_ALSA(snd_pcm_hw_params_malloc(¶ms)); TRY_ALSA(snd_pcm_hw_params_any(alsa->pcm, params)); - alsa->has_float = alsathread_find_float_format(alsa->pcm, params); + alsa->has_float = alsa_find_float_format(alsa->pcm, params); format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; TRY_ALSA(snd_pcm_hw_params_set_access( @@ -412,8 +401,8 @@ audio_driver_t audio_alsathread = { alsa_thread_free, alsa_thread_use_float, "alsathread", - alsa_thread_device_list_new, - alsa_thread_device_list_free, + alsa_device_list_new, + alsa_device_list_free, alsa_thread_write_avail, alsa_thread_buffer_size, }; From c5cc5944fa9a141132824418a3705cc7458c8ff7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 08:44:20 -0500 Subject: [PATCH 103/309] Add alsa_thread_microphone_t --- audio/drivers/alsathread.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 3b55f3a9bc9e..10810684a6c9 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -31,9 +31,29 @@ #define TRY_ALSA(x) if (x < 0) \ goto error; +typedef struct alsa_thread_microphone +{ + snd_pcm_t *pcm; + fifo_buffer_t *buffer; + size_t buffer_size; + sthread_t *worker_thread; + slock_t *fifo_lock; + scond_t *cond; + slock_t *cond_lock; + unsigned int frame_bits; + bool has_float; + bool can_pause; + bool is_paused; +} alsa_thread_microphone_t; + typedef struct alsa_thread { snd_pcm_t *pcm; + /* Only one microphone is supported right now; + * the driver state should track multiple microphone handles, + * but the driver *context* should track multiple microphone contexts */ + alsa_thread_microphone_t *microphone; + fifo_buffer_t *buffer; sthread_t *worker_thread; slock_t *fifo_lock; From a93858751868cb89b4e840dedb9604a11df36b03 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 10:57:46 -0500 Subject: [PATCH 104/309] Refactor alsa.c - Introduce alsa_init_pcm to init any PCM that we're using - Vastly simplifies the implementation of alsa_init and alsa_init_microphone - Will be used for the read-based versions next --- audio/drivers/alsa.c | 419 +++++++++++++++++++++++++------------------ audio/drivers/alsa.h | 20 +++ 2 files changed, 266 insertions(+), 173 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 2d7134c213f1..d49cd9255ba9 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -27,37 +27,31 @@ typedef struct alsa_microphone { - snd_pcm_t *pcm; - size_t buffer_size; - unsigned int frame_bits; - bool has_float; - bool can_pause; - bool is_paused; + snd_pcm_t *pcm; + alsa_stream_info_t stream_info; + bool is_paused; } alsa_microphone_t; typedef struct alsa { snd_pcm_t *pcm; - /* Only one microphone is supported right now; - * the driver state should track multiple microphone handles, - * but the driver *context* should track multiple microphone contexts */ + /* Only one microphone is supported right now; + * the driver state should track multiple microphone handles, + * but the driver *context* should track multiple microphone contexts */ alsa_microphone_t *microphone; /* The error handler that was set before this driver was initialized. * Likely to be equal to the default, but kept and restored just in case. */ snd_lib_error_handler_t prev_error_handler; - size_t buffer_size; - unsigned int frame_bits; + alsa_stream_info_t stream_info; bool nonblock; - bool has_float; - bool can_pause; bool is_paused; } alsa_t; static bool alsa_use_float(void *data) { alsa_t *alsa = (alsa_t*)data; - return alsa->has_float; + return alsa->stream_info.has_float; } static void alsa_log_error(const char *file, int line, const char *function, int err, const char *fmt,...) @@ -91,121 +85,291 @@ bool alsa_find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) return false; } -static void *alsa_init(const char *device, unsigned rate, unsigned latency, - unsigned block_frames, - unsigned *new_rate) +int alsa_init_pcm(snd_pcm_t **pcm, + const char* device, + snd_pcm_stream_t stream, + unsigned rate, + unsigned latency, + unsigned channels, + alsa_stream_info_t *stream_info, + unsigned *new_rate) { snd_pcm_format_t format; snd_pcm_uframes_t buffer_size; snd_pcm_hw_params_t *params = NULL; snd_pcm_sw_params_t *sw_params = NULL; unsigned latency_usec = latency * 1000; - unsigned channels = 2; unsigned periods = 4; unsigned orig_rate = rate; - const char *alsa_dev = "default"; - alsa_t *alsa = (alsa_t*)calloc(1, sizeof(alsa_t)); + const char *alsa_dev = device ? device : "default"; + int errnum = 0; - if (!alsa) - return NULL; + RARCH_DBG("[ALSA] Requesting device \"%s\" for %s stream\n", alsa_dev, snd_pcm_stream_name(stream)); - //alsa->prev_error_handler = snd_lib_error; - //snd_lib_error_set_handler(alsa_log_error); + if ((errnum = snd_pcm_open(pcm, alsa_dev, stream, SND_PCM_NONBLOCK)) < 0) + { + RARCH_ERR("[ALSA]: Failed to open %s stream on device \"%s\": %s\n", + snd_pcm_stream_name(stream), + alsa_dev, + snd_strerror(errnum)); - if (device) - alsa_dev = device; + goto error; + } - RARCH_LOG("[ALSA] Using ALSA version %s\n", snd_asoundlib_version()); - RARCH_DBG("[ALSA] Requesting device \"%s\" for output\n", alsa_dev); + if ((errnum = snd_pcm_hw_params_malloc(¶ms)) < 0) + { + RARCH_ERR("[ALSA]: Failed to allocate hardware parameters: %s\n", + snd_strerror(errnum)); - if (snd_pcm_open( - &alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) goto error; + } - if (snd_pcm_hw_params_malloc(¶ms) < 0) - goto error; + if ((errnum = snd_pcm_hw_params_any(*pcm, params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to query hardware parameters from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_hw_params_any(alsa->pcm, params) < 0) goto error; + } - alsa->has_float = alsa_find_float_format(alsa->pcm, params); - format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; + format = (snd_pcm_hw_params_test_format(*pcm, params, SND_PCM_FORMAT_FLOAT) == 0) + ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; + stream_info->has_float = (format == SND_PCM_FORMAT_FLOAT); + + RARCH_LOG("[ALSA]: Using %s sample format for %s device \"%s\"\n", + snd_pcm_format_name(format), + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm) + ); + + if ((errnum = snd_pcm_hw_params_set_access(*pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + { + RARCH_ERR("[ALSA]: Failed to set %s access for %s device \"%s\": %s\n", + snd_pcm_access_name(SND_PCM_ACCESS_RW_INTERLEAVED), + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_hw_params_set_access( - alsa->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) goto error; + } + stream_info->frame_bits = snd_pcm_format_physical_width(format) * channels; - /* channels hardcoded to 2 for now */ - alsa->frame_bits = snd_pcm_format_physical_width(format) * 2; + if ((errnum = snd_pcm_hw_params_set_format(*pcm, params, format)) < 0) + { + RARCH_ERR("[ALSA]: Failed to set %s format for %s device \"%s\": %s\n", + snd_pcm_format_name(format), + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_hw_params_set_format(alsa->pcm, params, format) < 0) goto error; + } + + if ((errnum = snd_pcm_hw_params_set_channels(*pcm, params, channels)) < 0) + { + RARCH_ERR("[ALSA]: Failed to set %u-channel audio for %s device \"%s\": %s\n", + channels, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_hw_params_set_channels(alsa->pcm, params, channels) < 0) goto error; + } /* Don't allow rate resampling when probing for the default rate (but ignore if this call fails) */ - snd_pcm_hw_params_set_rate_resample(alsa->pcm, params, 0 ); - if (snd_pcm_hw_params_set_rate_near(alsa->pcm, params, &rate, 0) < 0) + if ((errnum = snd_pcm_hw_params_set_rate_resample(*pcm, params, false)) < 0) + { + RARCH_WARN("[ALSA]: Failed to request a default unsampled rate for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + } + + if ((errnum = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0)) < 0) + { + RARCH_ERR("[ALSA]: Failed to request a rate near %uHz for %s device \"%s\": %s\n", + rate, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + goto error; + } if (new_rate && (rate != orig_rate)) *new_rate = rate; - if (snd_pcm_hw_params_set_buffer_time_near( - alsa->pcm, params, &latency_usec, NULL) < 0) + if ((snd_pcm_hw_params_set_buffer_time_near(*pcm, params, &latency_usec, NULL)) < 0) + { + RARCH_ERR("[ALSA]: Failed to request a buffer time near %uus for %s device \"%s\": %s\n", + latency_usec, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + goto error; - if (snd_pcm_hw_params_set_periods_near( - alsa->pcm, params, &periods, NULL) < 0) + } + + if ((errnum = snd_pcm_hw_params_set_periods_near(*pcm, params, &periods, NULL)) < 0) + { + RARCH_ERR("[ALSA]: Failed to request %u periods per buffer for %s device \"%s\": %s\n", + periods, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + goto error; + } + + if ((errnum = snd_pcm_hw_params(*pcm, params)) < 0) + { /* This calls snd_pcm_prepare() under the hood */ + RARCH_ERR("[ALSA]: Failed to install hardware parameters for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_hw_params(alsa->pcm, params) < 0) goto error; + } /* Shouldn't have to bother with this, * but some drivers are apparently broken. */ - if (snd_pcm_hw_params_get_period_size(params, &buffer_size, NULL)) - snd_pcm_hw_params_get_period_size_min(params, &buffer_size, NULL); + if ((errnum = snd_pcm_hw_params_get_period_size(params, &buffer_size, NULL)) < 0) + { + RARCH_WARN("[ALSA]: Failed to get an exact period size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + RARCH_WARN("[ALSA]: Trying the minimum period size instead\n"); + + if ((errnum = snd_pcm_hw_params_get_period_size_min(params, &buffer_size, NULL)) < 0) + { + RARCH_ERR("[ALSA]: Failed to get min period size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + goto error; + } + } + + RARCH_LOG("[ALSA]: Period size: %lu frames\n", buffer_size); + + if ((errnum = snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) < 0) + { + RARCH_WARN("[ALSA]: Failed to get exact buffer size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + RARCH_WARN("[ALSA]: Trying the maximum buffer size instead\n"); - RARCH_LOG("[ALSA]: Period size: %d frames\n", (int)buffer_size); + if ((errnum = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size)) < 0) + { + RARCH_ERR("[ALSA]: Failed to get max buffer size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + goto error; + } + } - if (snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) - snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size); + RARCH_LOG("[ALSA]: Buffer size: %lu frames\n", buffer_size); - RARCH_LOG("[ALSA]: Buffer size: %d frames\n", (int)buffer_size); + stream_info->buffer_size = snd_pcm_frames_to_bytes(*pcm, buffer_size); + stream_info->can_pause = snd_pcm_hw_params_can_pause(params); - alsa->buffer_size = snd_pcm_frames_to_bytes(alsa->pcm, buffer_size); - alsa->can_pause = snd_pcm_hw_params_can_pause(params); + RARCH_LOG("[ALSA]: Can pause: %s.\n", stream_info->can_pause ? "yes" : "no"); - RARCH_LOG("[ALSA]: Can pause: %s.\n", alsa->can_pause ? "yes" : "no"); + if ((errnum = snd_pcm_sw_params_malloc(&sw_params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to allocate software parameters: %s\n", + snd_strerror(errnum)); - if (snd_pcm_sw_params_malloc(&sw_params) < 0) goto error; + } + + if ((errnum = snd_pcm_sw_params_current(*pcm, sw_params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to query current software parameters for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_sw_params_current(alsa->pcm, sw_params) < 0) goto error; + } + + if ((errnum = snd_pcm_sw_params_set_start_threshold(*pcm, sw_params, buffer_size / 2)) < 0) + { + // TODO: Should "2" be a separate parameter, or equal to channels? + + RARCH_ERR("[ALSA]: Failed to set start %lu-frame threshold for %s device \"%s\": %s\n", + buffer_size / 2, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_sw_params_set_start_threshold( - alsa->pcm, sw_params, buffer_size / 2) < 0) goto error; + } + + if ((errnum = snd_pcm_sw_params(*pcm, sw_params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to install software parameters for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); - if (snd_pcm_sw_params(alsa->pcm, sw_params) < 0) goto error; + } snd_pcm_hw_params_free(params); snd_pcm_sw_params_free(sw_params); - return alsa; + RARCH_LOG("[ALSA] Initialized %s device \"%s\"\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm)); + return 0; error: - RARCH_ERR("[ALSA]: Failed to initialize...\n"); if (params) snd_pcm_hw_params_free(params); if (sw_params) snd_pcm_sw_params_free(sw_params); + if (*pcm) + { + snd_pcm_close(*pcm); + *pcm = NULL; + } + + return errnum; +} + +static void *alsa_init(const char *device, unsigned rate, unsigned latency, + unsigned block_frames, + unsigned *new_rate) +{ + alsa_t *alsa = (alsa_t*)calloc(1, sizeof(alsa_t)); + + if (!alsa) + return NULL; + + //alsa->prev_error_handler = snd_lib_error; + //snd_lib_error_set_handler(alsa_log_error); + + RARCH_LOG("[ALSA] Using ALSA version %s\n", snd_asoundlib_version()); + + if (alsa_init_pcm(&alsa->pcm, device, SND_PCM_STREAM_PLAYBACK, rate, latency, 2, &alsa->stream_info, new_rate) < 0) + { + goto error; + } + + return alsa; + +error: + RARCH_ERR("[ALSA]: Failed to initialize...\n"); + if (alsa) { if (alsa->pcm) @@ -233,8 +397,8 @@ static ssize_t alsa_write(void *data, const void *buf_, size_t size_) alsa_t *alsa = (alsa_t*)data; const uint8_t *buf = (const uint8_t*)buf_; snd_pcm_sframes_t written = 0; - snd_pcm_sframes_t size = BYTES_TO_FRAMES(size_, alsa->frame_bits); - size_t frames_size = alsa->has_float ? sizeof(float) : sizeof(int16_t); + snd_pcm_sframes_t size = BYTES_TO_FRAMES(size_, alsa->stream_info.frame_bits); + size_t frames_size = alsa->stream_info.has_float ? sizeof(float) : sizeof(int16_t); /* Workaround buggy menu code. * If a write happens while we're paused, we might never progress. */ @@ -326,7 +490,7 @@ static bool alsa_stop(void *data) if (alsa->is_paused) return true; - if (alsa->can_pause + if (alsa->stream_info.can_pause && !alsa->is_paused) { int ret = snd_pcm_pause(alsa->pcm, 1); @@ -367,7 +531,7 @@ static bool alsa_start(void *data, bool is_shutdown) if (!alsa->is_paused) return true; - if (alsa->can_pause + if (alsa->stream_info.can_pause && alsa->is_paused) { int ret = snd_pcm_pause(alsa->pcm, 0); @@ -465,15 +629,15 @@ static size_t alsa_write_avail(void *data) snd_pcm_sframes_t avail = snd_pcm_avail(alsa->pcm); if (avail < 0) - return alsa->buffer_size; + return alsa->stream_info.buffer_size; - return FRAMES_TO_BYTES(avail, alsa->frame_bits); + return FRAMES_TO_BYTES(avail, alsa->stream_info.frame_bits); } static size_t alsa_buffer_size(void *data) { alsa_t *alsa = (alsa_t*)data; - return alsa->buffer_size; + return alsa->stream_info.buffer_size; } void *alsa_device_list_new(void *data) @@ -541,17 +705,8 @@ static void *alsa_init_microphone(void *data, unsigned block_frames, unsigned *new_rate) { - snd_pcm_format_t format; - snd_pcm_uframes_t buffer_size; alsa_t *alsa = (alsa_t*)data; - snd_pcm_hw_params_t *params = NULL; - snd_pcm_sw_params_t *sw_params = NULL; - unsigned latency_usec = latency * 1000; - unsigned periods = 4; - unsigned orig_rate = rate; - const char *alsa_mic_dev = "default"; alsa_microphone_t *microphone = NULL; - int errnum = 0; if (!alsa) /* If we weren't given a valid ALSA context... */ return NULL; @@ -561,107 +716,25 @@ static void *alsa_init_microphone(void *data, if (!microphone) /* If the microphone context couldn't be allocated... */ return NULL; - if (device) - alsa_mic_dev = device; - - RARCH_DBG("[ALSA] Requesting device \"%s\" for input\n", alsa_mic_dev); - - errnum = snd_pcm_open(µphone->pcm, alsa_mic_dev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); - if (errnum < 0) + /* channels hardcoded to 1, because we only support mono mic input */ + if (alsa_init_pcm(µphone->pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->stream_info, new_rate) < 0) { - RARCH_ERR("[ALSA]: Failed to open input device: %s\n", snd_strerror(errnum)); goto error; } - else - { - RARCH_DBG("[ALSA]: Opened input device with name \"%s\"\n", snd_pcm_name(microphone->pcm)); - } - if (snd_pcm_hw_params_malloc(¶ms) < 0) - goto error; - - if (snd_pcm_hw_params_any(microphone->pcm, params) < 0) - goto error; - - microphone->has_float = alsa_find_float_format(microphone->pcm, params); - format = microphone->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; - - if (microphone->has_float != alsa->has_float) + if (microphone->stream_info.has_float != alsa->stream_info.has_float) { /* If the mic and speaker don't both use floating point or integer samples... */ - RARCH_WARN("[ALSA]: Microphone and speaker do not use the same PCM format\n"); + RARCH_WARN("[ALSA]: Microphone and speaker use different sample formats\n"); RARCH_WARN("[ALSA]: (%s and %s, respectively)\n", - microphone->has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16", - alsa->has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16"); - } - - if (snd_pcm_hw_params_set_access( - microphone->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) - goto error; - - /* channels hardcoded to 1, because we only support mono mic input */ - microphone->frame_bits = snd_pcm_format_physical_width(format); - - if (snd_pcm_hw_params_set_format(microphone->pcm, params, format) < 0) - goto error; - - if (snd_pcm_hw_params_set_channels(microphone->pcm, params, 1) < 0) - goto error; - - /* Don't allow rate resampling when probing for the default rate (but ignore if this call fails) */ - snd_pcm_hw_params_set_rate_resample(microphone->pcm, params, 0 ); - if (snd_pcm_hw_params_set_rate_near(microphone->pcm, params, &rate, 0) < 0) - goto error; - - if (new_rate && (rate != orig_rate)) - *new_rate = rate; - - if (snd_pcm_hw_params_set_buffer_time_near( - microphone->pcm, params, &latency_usec, NULL) < 0) - goto error; - - if (snd_pcm_hw_params_set_periods_near( - microphone->pcm, params, &periods, NULL) < 0) - goto error; - - if (snd_pcm_hw_params(microphone->pcm, params) < 0) - goto error; - - /* Shouldn't have to bother with this, - * but some drivers are apparently broken. */ - if (snd_pcm_hw_params_get_period_size(params, &buffer_size, NULL)) - snd_pcm_hw_params_get_period_size_min(params, &buffer_size, NULL); - - RARCH_LOG("[ALSA]: Microphone period size: %u frames\n", buffer_size); - - if (snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) - snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size); - - RARCH_LOG("[ALSA]: Microphone buffer size: %u frames\n", buffer_size); - - microphone->buffer_size = snd_pcm_frames_to_bytes(microphone->pcm, buffer_size); - microphone->can_pause = snd_pcm_hw_params_can_pause(params); - - RARCH_LOG("[ALSA]: Can pause microphone: %s.\n", microphone->can_pause ? "yes" : "no"); - - if (snd_pcm_sw_params_malloc(&sw_params) < 0) - goto error; - - if (snd_pcm_sw_params_current(microphone->pcm, sw_params) < 0) - goto error; - - snd_pcm_hw_params_free(params); - snd_pcm_sw_params_free(sw_params); + microphone->stream_info.has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16", + alsa->stream_info.has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16"); + } alsa->microphone = microphone; return microphone; error: RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); - if (params) - snd_pcm_hw_params_free(params); - - if (sw_params) - snd_pcm_sw_params_free(sw_params); if (microphone) { @@ -716,7 +789,7 @@ static bool alsa_set_microphone_state(void *data, void *microphone_context, bool return false; /* Both params must be non-null */ - if (!microphone->can_pause) + if (!microphone->stream_info.can_pause) { RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->pcm)); return true; @@ -756,8 +829,8 @@ static ssize_t alsa_read_microphone(void *driver_context, void *microphone_conte if (!alsa || !microphone || !buf) return -1; - size = BYTES_TO_FRAMES(size_, microphone->frame_bits); - frames_size = microphone->has_float ? sizeof(float) : sizeof(int16_t); + size = BYTES_TO_FRAMES(size_, microphone->stream_info.frame_bits); + frames_size = microphone->stream_info.has_float ? sizeof(float) : sizeof(int16_t); /* Workaround buggy menu code. * If a read happens while we're paused, we might never progress. */ @@ -855,7 +928,7 @@ static ssize_t alsa_read_microphone(void *driver_context, void *microphone_conte } } - return FRAMES_TO_BYTES(read, microphone->frame_bits); + return FRAMES_TO_BYTES(read, microphone->stream_info.frame_bits); } audio_driver_t audio_alsa = { diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h index caf82dbb1337..e60fd21911cb 100644 --- a/audio/drivers/alsa.h +++ b/audio/drivers/alsa.h @@ -19,6 +19,26 @@ /* Header file for common functions that are used by alsa and alsathread. */ +/** + * Used for info that's common to all pcm devices + * that's relevant for our purposes. + */ +typedef struct alsa_stream_info { + size_t buffer_size; + unsigned int frame_bits; + bool has_float; + bool can_pause; +} alsa_stream_info_t; + +int alsa_init_pcm(snd_pcm_t **pcm, + const char* device, + snd_pcm_stream_t stream, + unsigned rate, + unsigned latency, + unsigned channels, + alsa_stream_info_t *stream_info, + unsigned *new_rate); + bool alsa_find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); void *alsa_device_list_new(void *data); void alsa_device_list_free(void *data, void *array_list_data); From 269f3d6ee87621fc054451e1b3b513f093f08207 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 12:13:50 -0500 Subject: [PATCH 105/309] Make ALSA logging a little more consistent --- audio/drivers/alsa.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index d49cd9255ba9..4cad69f7c72b 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -70,7 +70,7 @@ static void alsa_log_error(const char *file, int line, const char *function, int /* Write up to 255 characters. (The 256th will be \0.) */ va_end(args); - RARCH_ERR("[ALSA] [%s:%s:%d]: %s%s\n", file, function, line, temp, errno_temp); /* To ensure that there's a newline at the end */ + RARCH_ERR("[ALSA]: [%s:%s:%d]: %s%s\n", file, function, line, temp, errno_temp); /* To ensure that there's a newline at the end */ } bool alsa_find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) @@ -104,7 +104,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, const char *alsa_dev = device ? device : "default"; int errnum = 0; - RARCH_DBG("[ALSA] Requesting device \"%s\" for %s stream\n", alsa_dev, snd_pcm_stream_name(stream)); + RARCH_DBG("[ALSA]: Requesting device \"%s\" for %s stream\n", alsa_dev, snd_pcm_stream_name(stream)); if ((errnum = snd_pcm_open(pcm, alsa_dev, stream, SND_PCM_NONBLOCK)) < 0) { @@ -325,7 +325,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, snd_pcm_hw_params_free(params); snd_pcm_sw_params_free(sw_params); - RARCH_LOG("[ALSA] Initialized %s device \"%s\"\n", + RARCH_LOG("[ALSA]: Initialized %s device \"%s\"\n", snd_pcm_stream_name(stream), snd_pcm_name(*pcm)); @@ -353,12 +353,15 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, alsa_t *alsa = (alsa_t*)calloc(1, sizeof(alsa_t)); if (!alsa) + { + RARCH_ERR("[ALSA]: Failed to allocate driver context\n"); return NULL; + } //alsa->prev_error_handler = snd_lib_error; //snd_lib_error_set_handler(alsa_log_error); - RARCH_LOG("[ALSA] Using ALSA version %s\n", snd_asoundlib_version()); + RARCH_LOG("[ALSA]: Using ALSA version %s\n", snd_asoundlib_version()); if (alsa_init_pcm(&alsa->pcm, device, SND_PCM_STREAM_PLAYBACK, rate, latency, 2, &alsa->stream_info, new_rate) < 0) { @@ -757,8 +760,20 @@ static void alsa_free_microphone(void *data, void *microphone_context) { if (microphone->pcm) { - snd_pcm_drop(microphone->pcm); - snd_pcm_close(microphone->pcm); + int errnum = 0; + if ((errnum = snd_pcm_drop(microphone->pcm)) < 0) + { + RARCH_WARN("[ALSA] Failed to drop remaining samples in capture device \"%s\": %s\n", + snd_pcm_name(microphone->pcm), + snd_strerror(errnum)); + } + + if ((errnum = snd_pcm_close(microphone->pcm)) < 0) + { + RARCH_WARN("[ALSA] Failed to close capture device \"%s\": %s\n", + snd_pcm_name(microphone->pcm), + snd_strerror(errnum)); + } } alsa->microphone = NULL; @@ -841,14 +856,14 @@ static ssize_t alsa_read_microphone(void *driver_context, void *microphone_conte state = snd_pcm_state(microphone->pcm); if (state != SND_PCM_STATE_RUNNING) { - RARCH_WARN("[ALSA] Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", + RARCH_WARN("[ALSA]: Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", snd_pcm_name(microphone->pcm), snd_pcm_state_name(state)); errnum = snd_pcm_start(microphone->pcm); if (errnum < 0) { - RARCH_ERR("[ALSA] Failed to start microphone \"%s\": %s\n", + RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", snd_pcm_name(microphone->pcm), snd_strerror(errnum)); @@ -867,8 +882,8 @@ static ssize_t alsa_read_microphone(void *driver_context, void *microphone_conte errnum = snd_pcm_recover(microphone->pcm, frames, 0); if (errnum < 0) { - RARCH_ERR("[ALSA] Failed to read from microphone: %s\n", snd_strerror(frames)); - RARCH_ERR("[ALSA] Additionally, recovery failed with: %s\n", snd_strerror(errnum)); + RARCH_ERR("[ALSA]: Failed to read from microphone: %s\n", snd_strerror(frames)); + RARCH_ERR("[ALSA]: Additionally, recovery failed with: %s\n", snd_strerror(errnum)); return -1; } From 500d41283ac53b906ae761791784a3cf201539c3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 12:14:09 -0500 Subject: [PATCH 106/309] Clean up the mic with alsa_free_microphone if alsa_init_microphone fails --- audio/drivers/alsa.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 4cad69f7c72b..be1249c35dd6 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -739,15 +739,8 @@ static void *alsa_init_microphone(void *data, error: RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); - if (microphone) - { - if (microphone->pcm) - { - snd_pcm_close(microphone->pcm); - } + alsa_free_microphone(alsa, microphone); - free(microphone); - } return NULL; } From 49e17224484ada1e3b65997909a1ef426ec22118 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 12:15:09 -0500 Subject: [PATCH 107/309] Remove an unused function --- audio/drivers/alsa.c | 12 ------------ audio/drivers/alsa.h | 1 - 2 files changed, 13 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index be1249c35dd6..ecefa344e812 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -73,18 +73,6 @@ static void alsa_log_error(const char *file, int line, const char *function, int RARCH_ERR("[ALSA]: [%s:%s:%d]: %s%s\n", file, function, line, temp, errno_temp); /* To ensure that there's a newline at the end */ } -bool alsa_find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) -{ - if (snd_pcm_hw_params_test_format(pcm, params, SND_PCM_FORMAT_FLOAT) == 0) - { - RARCH_LOG("[ALSA]: Using floating point format.\n"); - return true; - } - - RARCH_LOG("[ALSA]: Using signed 16-bit format.\n"); - return false; -} - int alsa_init_pcm(snd_pcm_t **pcm, const char* device, snd_pcm_stream_t stream, diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h index e60fd21911cb..67e1735d32f2 100644 --- a/audio/drivers/alsa.h +++ b/audio/drivers/alsa.h @@ -39,7 +39,6 @@ int alsa_init_pcm(snd_pcm_t **pcm, alsa_stream_info_t *stream_info, unsigned *new_rate); -bool alsa_find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); void *alsa_device_list_new(void *data); void alsa_device_list_free(void *data, void *array_list_data); From fd7670e542f7497b964e2f648ff00f853faa69bc Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 12:52:52 -0500 Subject: [PATCH 108/309] Move some cleanup in alsa.c to a common function --- audio/drivers/alsa.c | 74 +++++++++++++++++++------------------------- audio/drivers/alsa.h | 2 +- 2 files changed, 32 insertions(+), 44 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index ecefa344e812..99e335451ab8 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -20,6 +20,7 @@ #include #include +#include #include "../audio_driver.h" #include "alsa.h" @@ -327,13 +328,38 @@ int alsa_init_pcm(snd_pcm_t **pcm, if (*pcm) { - snd_pcm_close(*pcm); + alsa_free_pcm(*pcm); *pcm = NULL; } return errnum; } +void alsa_free_pcm(snd_pcm_t *pcm) +{ + if (pcm) + { + int errnum = 0; + + if ((errnum = snd_pcm_drop(pcm)) < 0) + { + RARCH_WARN("[ALSA]: Failed to drop remaining samples in %s device \"%s\": %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_strerror(errnum)); + } + + if ((errnum = snd_pcm_close(pcm)) < 0) + { + RARCH_WARN("[ALSA]: Failed to close %s device \"%s\": %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_strerror(errnum)); + } + } +} + +static void alsa_free(void *data); static void *alsa_init(const char *device, unsigned rate, unsigned latency, unsigned block_frames, unsigned *new_rate) @@ -361,21 +387,8 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, error: RARCH_ERR("[ALSA]: Failed to initialize...\n"); - if (alsa) - { - if (alsa->pcm) - { - snd_pcm_close(alsa->pcm); - snd_config_update_free_global(); - } + alsa_free(alsa); - if (alsa->prev_error_handler) - { /* If we ever changed the error handler, put it back. */ - snd_lib_error_set_handler(alsa->prev_error_handler); - } - - free(alsa); - } return NULL; } @@ -592,17 +605,8 @@ static void alsa_free(void *data) if (alsa) { - if (alsa->pcm) - { - snd_pcm_drop(alsa->pcm); - snd_pcm_close(alsa->pcm); - } - - if (alsa->microphone) - { - alsa_free_microphone(alsa, alsa->microphone); - } - + alsa_free_pcm(alsa->pcm); + alsa_free_microphone(alsa, alsa->microphone); if (alsa->prev_error_handler) { /* If we ever changed the error handler, put it back. */ @@ -739,23 +743,7 @@ static void alsa_free_microphone(void *data, void *microphone_context) if (alsa && microphone) { - if (microphone->pcm) - { - int errnum = 0; - if ((errnum = snd_pcm_drop(microphone->pcm)) < 0) - { - RARCH_WARN("[ALSA] Failed to drop remaining samples in capture device \"%s\": %s\n", - snd_pcm_name(microphone->pcm), - snd_strerror(errnum)); - } - - if ((errnum = snd_pcm_close(microphone->pcm)) < 0) - { - RARCH_WARN("[ALSA] Failed to close capture device \"%s\": %s\n", - snd_pcm_name(microphone->pcm), - snd_strerror(errnum)); - } - } + alsa_free_pcm(microphone->pcm); alsa->microphone = NULL; free(microphone); diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h index 67e1735d32f2..02dfe32ab4b5 100644 --- a/audio/drivers/alsa.h +++ b/audio/drivers/alsa.h @@ -38,7 +38,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, unsigned channels, alsa_stream_info_t *stream_info, unsigned *new_rate); - +void alsa_free_pcm(snd_pcm_t *pcm); void *alsa_device_list_new(void *data); void alsa_device_list_free(void *data, void *array_list_data); From 111d3c1943e806e688702fac60e4db595164f7b0 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 13:12:12 -0500 Subject: [PATCH 109/309] First crack at mic support for alsathread - Refactor some duplicate code into functions - Use functions introduced in alsa.c - Create and destroy the mic --- audio/drivers/alsathread.c | 407 +++++++++++++++++++++---------------- 1 file changed, 230 insertions(+), 177 deletions(-) diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 10810684a6c9..90a5ebae6122 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -23,86 +23,140 @@ #include #include #include +#include #include "../audio_driver.h" #include "alsa.h" #include "../../verbosity.h" -#define TRY_ALSA(x) if (x < 0) \ - goto error; - -typedef struct alsa_thread_microphone +typedef struct alsa_thread_info { snd_pcm_t *pcm; fifo_buffer_t *buffer; - size_t buffer_size; sthread_t *worker_thread; slock_t *fifo_lock; scond_t *cond; slock_t *cond_lock; - unsigned int frame_bits; - bool has_float; - bool can_pause; + alsa_stream_info_t stream_info; + volatile bool thread_dead; +} alsa_thread_info_t; + +typedef struct alsa_thread_microphone +{ + alsa_thread_info_t info; bool is_paused; } alsa_thread_microphone_t; typedef struct alsa_thread { - snd_pcm_t *pcm; + alsa_thread_info_t info; /* Only one microphone is supported right now; * the driver state should track multiple microphone handles, * but the driver *context* should track multiple microphone contexts */ alsa_thread_microphone_t *microphone; - - fifo_buffer_t *buffer; - sthread_t *worker_thread; - slock_t *fifo_lock; - scond_t *cond; - slock_t *cond_lock; - size_t buffer_size; - size_t period_size; snd_pcm_uframes_t period_frames; + size_t period_size; bool nonblock; bool is_paused; - bool has_float; - volatile bool thread_dead; } alsa_thread_t; static void alsa_worker_thread(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; uint8_t *buf = (uint8_t *)calloc(1, alsa->period_size); + uintptr_t id = sthread_get_current_thread_id(); if (!buf) { - RARCH_ERR("failed to allocate audio buffer"); + RARCH_ERR("[ALSA] [playback thread %u]: Failed to allocate audio buffer\n", id); goto end; } - while (!alsa->thread_dead) + while (!alsa->info.thread_dead) { size_t avail; size_t fifo_size; snd_pcm_sframes_t frames; - slock_lock(alsa->fifo_lock); - avail = FIFO_READ_AVAIL(alsa->buffer); + slock_lock(alsa->info.fifo_lock); + avail = FIFO_READ_AVAIL(alsa->info.buffer); fifo_size = MIN(alsa->period_size, avail); - fifo_read(alsa->buffer, buf, fifo_size); - scond_signal(alsa->cond); - slock_unlock(alsa->fifo_lock); + fifo_read(alsa->info.buffer, buf, fifo_size); + scond_signal(alsa->info.cond); + slock_unlock(alsa->info.fifo_lock); /* If underrun, fill rest with silence. */ memset(buf + fifo_size, 0, alsa->period_size - fifo_size); - frames = snd_pcm_writei(alsa->pcm, buf, alsa->period_frames); + frames = snd_pcm_writei(alsa->info.pcm, buf, alsa->period_frames); if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) { - if (snd_pcm_recover(alsa->pcm, frames, 1) < 0) + if (snd_pcm_recover(alsa->info.pcm, frames, 1) < 0) + { + RARCH_ERR("[ALSA] [playback thread %u]: Failed to recover from error: %s\n", + id, + snd_strerror(frames)); + break; + } + + continue; + } + else if (frames < 0) + { + RARCH_ERR("[ALSA] [playback thread %u]: Error writing audio to device: %s\n", + id, + snd_strerror(frames)); + break; + } + } + +end: + slock_lock(alsa->info.cond_lock); + alsa->info.thread_dead = true; + scond_signal(alsa->info.cond); + slock_unlock(alsa->info.cond_lock); + free(buf); +} + +static void alsa_microphone_worker_thread(void *data) +{ + // TODO: Implement + alsa_thread_t *microphone = (alsa_thread_t*)data; + uint8_t *buf = (uint8_t *)calloc(1, microphone->period_size); + uintptr_t id = sthread_get_current_thread_id(); + + if (!buf) + { + RARCH_ERR("[ALSA] [capture thread %u]: Failed to allocate audio buffer\n", id); + goto end; + } + + while (!microphone->info.thread_dead) + { + size_t avail; + size_t fifo_size; + snd_pcm_sframes_t frames; + + slock_lock(microphone->info.fifo_lock); + avail = FIFO_READ_AVAIL(microphone->info.buffer); + fifo_size = MIN(microphone->period_size, avail); + fifo_read(microphone->info.buffer, buf, fifo_size); + scond_signal(microphone->info.cond); + slock_unlock(microphone->info.fifo_lock); + + /* If underrun, fill rest with silence. */ + memset(buf + fifo_size, 0, microphone->period_size - fifo_size); + + frames = snd_pcm_writei(microphone->info.pcm, buf, microphone->period_frames); + + if (frames == -EPIPE || frames == -EINTR || + frames == -ESTRPIPE) + { + if (snd_pcm_recover(microphone->info.pcm, frames, 1) < 0) { RARCH_ERR("[ALSA]: (#2) Failed to recover from error (%s)\n", - snd_strerror(frames)); + snd_strerror(frames)); break; } @@ -110,52 +164,64 @@ static void alsa_worker_thread(void *data) } else if (frames < 0) { - RARCH_ERR("[ALSA]: Unknown error occurred (%s).\n", - snd_strerror(frames)); + RARCH_ERR("[ALSA]: Unknown error occurred (%s).\n", snd_strerror(frames)); break; } } end: - slock_lock(alsa->cond_lock); - alsa->thread_dead = true; - scond_signal(alsa->cond); - slock_unlock(alsa->cond_lock); + slock_lock(microphone->info.cond_lock); + microphone->info.thread_dead = true; + scond_signal(microphone->info.cond); + slock_unlock(microphone->info.cond_lock); free(buf); } +static void alsa_thread_free_info(alsa_thread_info_t *info) +{ + if (info) + { + if (info->worker_thread) + { + slock_lock(info->cond_lock); + info->thread_dead = true; + slock_unlock(info->cond_lock); + sthread_join(info->worker_thread); + } + if (info->buffer) + fifo_free(info->buffer); + if (info->cond) + scond_free(info->cond); + if (info->fifo_lock) + slock_free(info->fifo_lock); + if (info->cond_lock) + slock_free(info->cond_lock); + if (info->pcm) + { + alsa_free_pcm(info->pcm); + } + } + /* Do NOT call free() here; the info object is embedded within + * something else */ +} + static bool alsa_thread_use_float(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; - return alsa->has_float; + return alsa->info.stream_info.has_float; } +static void alsa_thread_free_microphone(void *data, void *microphone_context); static void alsa_thread_free(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; if (alsa) { - if (alsa->worker_thread) - { - slock_lock(alsa->cond_lock); - alsa->thread_dead = true; - slock_unlock(alsa->cond_lock); - sthread_join(alsa->worker_thread); - } - if (alsa->buffer) - fifo_free(alsa->buffer); - if (alsa->cond) - scond_free(alsa->cond); - if (alsa->fifo_lock) - slock_free(alsa->fifo_lock); - if (alsa->cond_lock) - slock_free(alsa->cond_lock); - if (alsa->pcm) - { - snd_pcm_drop(alsa->pcm); - snd_pcm_close(alsa->pcm); - } + if (alsa->microphone) + alsa_thread_free_microphone(alsa, alsa->microphone); + + alsa_thread_free_info(&alsa->info); free(alsa); } } @@ -165,86 +231,39 @@ static void *alsa_thread_init(const char *device, unsigned block_frames, unsigned *new_rate) { - snd_pcm_uframes_t buffer_size; - snd_pcm_format_t format; - snd_pcm_hw_params_t *params = NULL; - snd_pcm_sw_params_t *sw_params = NULL; - const char *alsa_dev = device ? device : "default"; - unsigned latency_usec = latency * 1000 / 2; - unsigned channels = 2; - unsigned periods = 4; - alsa_thread_t *alsa = (alsa_thread_t*) - calloc(1, sizeof(alsa_thread_t)); + alsa_thread_t *alsa = (alsa_thread_t*)calloc(1, sizeof(alsa_thread_t)); if (!alsa) + { + RARCH_ERR("[ALSA] Failed to allocate driver context\n"); return NULL; + } + + RARCH_LOG("[ALSA] Using ALSA version %s\n", snd_asoundlib_version()); - TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, 0)); - - TRY_ALSA(snd_pcm_hw_params_malloc(¶ms)); - TRY_ALSA(snd_pcm_hw_params_any(alsa->pcm, params)); - - alsa->has_float = alsa_find_float_format(alsa->pcm, params); - format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; - - TRY_ALSA(snd_pcm_hw_params_set_access( - alsa->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED)); - TRY_ALSA(snd_pcm_hw_params_set_format(alsa->pcm, params, format)); - TRY_ALSA(snd_pcm_hw_params_set_channels(alsa->pcm, params, channels)); - TRY_ALSA(snd_pcm_hw_params_set_rate(alsa->pcm, params, rate, 0)); - - TRY_ALSA(snd_pcm_hw_params_set_buffer_time_near( - alsa->pcm, params, &latency_usec, NULL)); - TRY_ALSA(snd_pcm_hw_params_set_periods_near( - alsa->pcm, params, &periods, NULL)); - - TRY_ALSA(snd_pcm_hw_params(alsa->pcm, params)); - - /* Shouldn't have to bother with this, - * but some drivers are apparently broken. */ - if (snd_pcm_hw_params_get_period_size(params, &alsa->period_frames, NULL)) - snd_pcm_hw_params_get_period_size_min( - params, &alsa->period_frames, NULL); - RARCH_LOG("ALSA: Period size: %d frames\n", (int)alsa->period_frames); - if (snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) - snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size); - RARCH_LOG("ALSA: Buffer size: %d frames\n", (int)buffer_size); - - alsa->buffer_size = snd_pcm_frames_to_bytes(alsa->pcm, buffer_size); - alsa->period_size = snd_pcm_frames_to_bytes(alsa->pcm, alsa->period_frames); - - TRY_ALSA(snd_pcm_sw_params_malloc(&sw_params)); - TRY_ALSA(snd_pcm_sw_params_current(alsa->pcm, sw_params)); - TRY_ALSA(snd_pcm_sw_params_set_start_threshold( - alsa->pcm, sw_params, buffer_size / 2)); - TRY_ALSA(snd_pcm_sw_params(alsa->pcm, sw_params)); - - snd_pcm_hw_params_free(params); - snd_pcm_sw_params_free(sw_params); - - alsa->fifo_lock = slock_new(); - alsa->cond_lock = slock_new(); - alsa->cond = scond_new(); - alsa->buffer = fifo_new(alsa->buffer_size); - if (!alsa->fifo_lock || !alsa->cond_lock || !alsa->cond || !alsa->buffer) + if (alsa_init_pcm(&alsa->info.pcm, device, SND_PCM_STREAM_PLAYBACK, rate, latency, 2, &alsa->info.stream_info, new_rate) < 0) + { + goto error; + } + + alsa->info.fifo_lock = slock_new(); + alsa->info.cond_lock = slock_new(); + alsa->info.cond = scond_new(); + alsa->info.buffer = fifo_new(alsa->info.stream_info.buffer_size); + if (!alsa->info.fifo_lock || !alsa->info.cond_lock || !alsa->info.cond || !alsa->info.buffer) goto error; - alsa->worker_thread = sthread_create(alsa_worker_thread, alsa); - if (!alsa->worker_thread) + alsa->info.worker_thread = sthread_create(alsa_worker_thread, alsa); + if (!alsa->info.worker_thread) { - RARCH_ERR("error initializing worker thread"); + RARCH_ERR("[ALSA]: Failed to initialize worker thread\n"); goto error; } return alsa; error: - RARCH_ERR("ALSA: Failed to initialize...\n"); - if (params) - snd_pcm_hw_params_free(params); - - if (sw_params) - snd_pcm_sw_params_free(sw_params); + RARCH_ERR("[ALSA]: Failed to initialize...\n"); alsa_thread_free(alsa); @@ -255,7 +274,7 @@ static ssize_t alsa_thread_write(void *data, const void *buf, size_t size) { alsa_thread_t *alsa = (alsa_thread_t*)data; - if (alsa->thread_dead) + if (alsa->info.thread_dead) return -1; if (alsa->nonblock) @@ -263,38 +282,38 @@ static ssize_t alsa_thread_write(void *data, const void *buf, size_t size) size_t avail; size_t write_amt; - slock_lock(alsa->fifo_lock); - avail = FIFO_WRITE_AVAIL(alsa->buffer); + slock_lock(alsa->info.fifo_lock); + avail = FIFO_WRITE_AVAIL(alsa->info.buffer); write_amt = MIN(avail, size); - fifo_write(alsa->buffer, buf, write_amt); - slock_unlock(alsa->fifo_lock); + fifo_write(alsa->info.buffer, buf, write_amt); + slock_unlock(alsa->info.fifo_lock); return write_amt; } else { size_t written = 0; - while (written < size && !alsa->thread_dead) + while (written < size && !alsa->info.thread_dead) { size_t avail; - slock_lock(alsa->fifo_lock); - avail = FIFO_WRITE_AVAIL(alsa->buffer); + slock_lock(alsa->info.fifo_lock); + avail = FIFO_WRITE_AVAIL(alsa->info.buffer); if (avail == 0) { - slock_unlock(alsa->fifo_lock); - slock_lock(alsa->cond_lock); - if (!alsa->thread_dead) - scond_wait(alsa->cond, alsa->cond_lock); - slock_unlock(alsa->cond_lock); + slock_unlock(alsa->info.fifo_lock); + slock_lock(alsa->info.cond_lock); + if (!alsa->info.thread_dead) + scond_wait(alsa->info.cond, alsa->info.cond_lock); + slock_unlock(alsa->info.cond_lock); } else { size_t write_amt = MIN(size - written, avail); - fifo_write(alsa->buffer, + fifo_write(alsa->info.buffer, (const char*)buf + written, write_amt); - slock_unlock(alsa->fifo_lock); + slock_unlock(alsa->info.fifo_lock); written += write_amt; } } @@ -339,76 +358,105 @@ static size_t alsa_thread_write_avail(void *data) alsa_thread_t *alsa = (alsa_thread_t*)data; size_t val; - if (alsa->thread_dead) + if (alsa->info.thread_dead) return 0; - slock_lock(alsa->fifo_lock); - val = FIFO_WRITE_AVAIL(alsa->buffer); - slock_unlock(alsa->fifo_lock); + slock_lock(alsa->info.fifo_lock); + val = FIFO_WRITE_AVAIL(alsa->info.buffer); + slock_unlock(alsa->info.fifo_lock); return val; } static size_t alsa_thread_buffer_size(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; - return alsa->buffer_size; + return alsa->info.stream_info.buffer_size; } -static void *alsa_thread_device_list_new(void *data) +static void *alsa_thread_init_microphone(void *data, + const char *device, + unsigned rate, + unsigned latency, + unsigned block_frames, + unsigned *new_rate) { - void **hints, **n; - union string_list_elem_attr attr; - struct string_list *s = string_list_new(); + alsa_thread_t *alsa = (alsa_thread_t*)data; + alsa_thread_microphone_t *microphone = NULL; - if (!s) + if (!alsa) /* If we weren't given a valid ALSA context... */ return NULL; - attr.i = 0; + microphone = calloc(1, sizeof(alsa_thread_microphone_t)); - if (snd_device_name_hint(-1, "pcm", &hints) != 0) + if (!microphone) + { /* If the microphone context couldn't be allocated... */ + RARCH_ERR("[ALSA] Failed to allocate microphone context\n"); + return NULL; + } + + if (alsa_init_pcm(&alsa->info.pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, &alsa->info.stream_info, new_rate) < 0) + { goto error; + } - n = hints; + microphone->info.fifo_lock = slock_new(); + microphone->info.cond_lock = slock_new(); + microphone->info.cond = scond_new(); + microphone->info.buffer = fifo_new(microphone->info.stream_info.buffer_size); + if (!microphone->info.fifo_lock || !microphone->info.cond_lock || !microphone->info.cond || !microphone->info.buffer) + goto error; - while (*n) + microphone->info.worker_thread = sthread_create(alsa_microphone_worker_thread, microphone); + if (!microphone->info.worker_thread) { - char *name = snd_device_name_get_hint(*n, "NAME"); - char *io = snd_device_name_get_hint(*n, "IOID"); - char *desc = snd_device_name_get_hint(*n, "DESC"); + RARCH_ERR("[ALSA]: Failed to initialize microphone worker thread\n"); + goto error; + } - /* description of device IOID - input / output identifcation - * ("Input" or "Output"), NULL means both) */ + alsa->microphone = microphone; + return microphone; - if (!io || string_is_equal(io,"Output")) - string_list_append(s, name, attr); +error: + RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); - if (name) - free(name); - if (io) - free(io); - if (desc) - free(desc); + if (microphone) + { + if (microphone->info.pcm) + { + snd_pcm_close(microphone->info.pcm); + } - n++; + alsa_thread_free_microphone(alsa, microphone); } + alsa->microphone = NULL; + return NULL; +} - /* free hint buffer too */ - snd_device_name_free_hint(hints); +static void alsa_thread_free_microphone(void *data, void *microphone_context) +{ + alsa_thread_t *alsa = (alsa_thread_t*)data; + alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)microphone_context; - return s; + if (alsa && microphone) + { + alsa_thread_free_info(µphone->info); -error: - string_list_free(s); - return NULL; + alsa->microphone = NULL; + free(microphone); + } } -static void alsa_thread_device_list_free(void *data, void *array_list_data) +static bool alsa_thread_get_microphone_state(const void *data, const void *microphone_context) { - struct string_list *s = (struct string_list*)array_list_data; - if (!s) - return; +} +static bool alsa_thread_set_microphone_state(void *data, void *microphone_context, bool enabled) +{ + +} + +static ssize_t alsa_thread_read_microphone(void *driver_context, void *microphone_context, void *buf_, size_t size_) +{ - string_list_free(s); } audio_driver_t audio_alsathread = { @@ -425,4 +473,9 @@ audio_driver_t audio_alsathread = { alsa_device_list_free, alsa_thread_write_avail, alsa_thread_buffer_size, + alsa_thread_init_microphone, + alsa_thread_free_microphone, + alsa_thread_get_microphone_state, + alsa_thread_set_microphone_state, + alsa_thread_read_microphone, }; From f2a421ce15c1fd6685bf785cee964d9eca329c9e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 14:14:16 -0500 Subject: [PATCH 110/309] Slight cleanups for clarity --- audio/drivers/alsa.c | 9 +++++---- audio/drivers/alsa.h | 3 ++- audio/drivers/alsathread.c | 32 +++++++++++++++++--------------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 99e335451ab8..ca7ca7f44a84 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -81,7 +81,8 @@ int alsa_init_pcm(snd_pcm_t **pcm, unsigned latency, unsigned channels, alsa_stream_info_t *stream_info, - unsigned *new_rate) + unsigned *new_rate, + int mode) { snd_pcm_format_t format; snd_pcm_uframes_t buffer_size; @@ -95,7 +96,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, RARCH_DBG("[ALSA]: Requesting device \"%s\" for %s stream\n", alsa_dev, snd_pcm_stream_name(stream)); - if ((errnum = snd_pcm_open(pcm, alsa_dev, stream, SND_PCM_NONBLOCK)) < 0) + if ((errnum = snd_pcm_open(pcm, alsa_dev, stream, mode)) < 0) { RARCH_ERR("[ALSA]: Failed to open %s stream on device \"%s\": %s\n", snd_pcm_stream_name(stream), @@ -377,7 +378,7 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency, RARCH_LOG("[ALSA]: Using ALSA version %s\n", snd_asoundlib_version()); - if (alsa_init_pcm(&alsa->pcm, device, SND_PCM_STREAM_PLAYBACK, rate, latency, 2, &alsa->stream_info, new_rate) < 0) + if (alsa_init_pcm(&alsa->pcm, device, SND_PCM_STREAM_PLAYBACK, rate, latency, 2, &alsa->stream_info, new_rate, SND_PCM_NONBLOCK) < 0) { goto error; } @@ -712,7 +713,7 @@ static void *alsa_init_microphone(void *data, return NULL; /* channels hardcoded to 1, because we only support mono mic input */ - if (alsa_init_pcm(µphone->pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->stream_info, new_rate) < 0) + if (alsa_init_pcm(µphone->pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->stream_info, new_rate, SND_PCM_NONBLOCK) < 0) { goto error; } diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h index 02dfe32ab4b5..a3b36fa11cea 100644 --- a/audio/drivers/alsa.h +++ b/audio/drivers/alsa.h @@ -37,7 +37,8 @@ int alsa_init_pcm(snd_pcm_t **pcm, unsigned latency, unsigned channels, alsa_stream_info_t *stream_info, - unsigned *new_rate); + unsigned *new_rate, + int mode); void alsa_free_pcm(snd_pcm_t *pcm); void *alsa_device_list_new(void *data); void alsa_device_list_free(void *data, void *array_list_data); diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 90a5ebae6122..cec019dfcbfa 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -124,11 +124,11 @@ static void alsa_microphone_worker_thread(void *data) // TODO: Implement alsa_thread_t *microphone = (alsa_thread_t*)data; uint8_t *buf = (uint8_t *)calloc(1, microphone->period_size); - uintptr_t id = sthread_get_current_thread_id(); + uintptr_t thread_id = sthread_get_current_thread_id(); if (!buf) { - RARCH_ERR("[ALSA] [capture thread %u]: Failed to allocate audio buffer\n", id); + RARCH_ERR("[ALSA] [capture thread %p]: Failed to allocate audio buffer\n", thread_id); goto end; } @@ -150,12 +150,12 @@ static void alsa_microphone_worker_thread(void *data) frames = snd_pcm_writei(microphone->info.pcm, buf, microphone->period_frames); - if (frames == -EPIPE || frames == -EINTR || - frames == -ESTRPIPE) + if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) { if (snd_pcm_recover(microphone->info.pcm, frames, 1) < 0) { - RARCH_ERR("[ALSA]: (#2) Failed to recover from error (%s)\n", + RARCH_ERR("[ALSA] [capture thread %p]: (#2) Failed to recover from read error: %s\n", + thread_id, snd_strerror(frames)); break; } @@ -164,7 +164,9 @@ static void alsa_microphone_worker_thread(void *data) } else if (frames < 0) { - RARCH_ERR("[ALSA]: Unknown error occurred (%s).\n", snd_strerror(frames)); + RARCH_ERR("[ALSA] [capture thread %p]: Read error: %s.\n", + thread_id, + snd_strerror(frames)); break; } } @@ -177,7 +179,7 @@ static void alsa_microphone_worker_thread(void *data) free(buf); } -static void alsa_thread_free_info(alsa_thread_info_t *info) +static void alsa_thread_free_info_members(alsa_thread_info_t *info) { if (info) { @@ -201,8 +203,8 @@ static void alsa_thread_free_info(alsa_thread_info_t *info) alsa_free_pcm(info->pcm); } } - /* Do NOT call free() here; the info object is embedded within - * something else */ + /* Do NOT free() info itself; it's embedded within another struct + * that will be freed. */ } static bool alsa_thread_use_float(void *data) @@ -221,7 +223,7 @@ static void alsa_thread_free(void *data) if (alsa->microphone) alsa_thread_free_microphone(alsa, alsa->microphone); - alsa_thread_free_info(&alsa->info); + alsa_thread_free_info_members(&alsa->info); free(alsa); } } @@ -241,7 +243,7 @@ static void *alsa_thread_init(const char *device, RARCH_LOG("[ALSA] Using ALSA version %s\n", snd_asoundlib_version()); - if (alsa_init_pcm(&alsa->info.pcm, device, SND_PCM_STREAM_PLAYBACK, rate, latency, 2, &alsa->info.stream_info, new_rate) < 0) + if (alsa_init_pcm(&alsa->info.pcm, device, SND_PCM_STREAM_PLAYBACK, rate, latency, 2, &alsa->info.stream_info, new_rate, 0) < 0) { goto error; } @@ -393,7 +395,7 @@ static void *alsa_thread_init_microphone(void *data, return NULL; } - if (alsa_init_pcm(&alsa->info.pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, &alsa->info.stream_info, new_rate) < 0) + if (alsa_init_pcm(&alsa->info.pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, &alsa->info.stream_info, new_rate, 0) < 0) { goto error; } @@ -438,7 +440,7 @@ static void alsa_thread_free_microphone(void *data, void *microphone_context) if (alsa && microphone) { - alsa_thread_free_info(µphone->info); + alsa_thread_free_info_members(µphone->info); alsa->microphone = NULL; free(microphone); @@ -469,8 +471,8 @@ audio_driver_t audio_alsathread = { alsa_thread_free, alsa_thread_use_float, "alsathread", - alsa_device_list_new, - alsa_device_list_free, + alsa_device_list_new, /* Reusing these functions from alsa.c */ + alsa_device_list_free, /* because they don't use the driver context */ alsa_thread_write_avail, alsa_thread_buffer_size, alsa_thread_init_microphone, From 40b908006de14052743cb481269e21703a31af10 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 16:05:01 -0500 Subject: [PATCH 111/309] Implement alsa_thread_set/get_microphone_state --- audio/drivers/alsathread.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index cec019dfcbfa..13716400392c 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -449,11 +449,28 @@ static void alsa_thread_free_microphone(void *data, void *microphone_context) static bool alsa_thread_get_microphone_state(const void *data, const void *microphone_context) { + alsa_thread_t *alsa = (alsa_thread_t*)data; + alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t *)microphone_context; + + if (!alsa || !microphone) + return false; + return !microphone->is_paused; } + static bool alsa_thread_set_microphone_state(void *data, void *microphone_context, bool enabled) { + alsa_thread_t *alsa = (alsa_thread_t*)data; + alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)microphone_context; + if (!alsa || !microphone) + return false; + /* Both params must be non-null */ + + microphone->is_paused = !enabled; + + // TODO: Do I need to synchronize this? + return true; } static ssize_t alsa_thread_read_microphone(void *driver_context, void *microphone_context, void *buf_, size_t size_) From c3d9bc4b6e7962d34c21877e57d439c46135f07e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 17 Jan 2023 18:39:52 -0500 Subject: [PATCH 112/309] More work on alsathread - No more crashing, but the mic just returns silence --- audio/drivers/alsa.c | 23 ++++++- audio/drivers/alsa.h | 2 + audio/drivers/alsathread.c | 123 +++++++++++++++++++++++++++++++------ 3 files changed, 125 insertions(+), 23 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index ca7ca7f44a84..2dacd4f790a7 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -226,7 +226,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, /* Shouldn't have to bother with this, * but some drivers are apparently broken. */ - if ((errnum = snd_pcm_hw_params_get_period_size(params, &buffer_size, NULL)) < 0) + if ((errnum = snd_pcm_hw_params_get_period_size(params, &stream_info->period_frames, NULL)) < 0) { RARCH_WARN("[ALSA]: Failed to get an exact period size from %s device \"%s\": %s\n", snd_pcm_stream_name(stream), @@ -234,7 +234,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, snd_strerror(errnum)); RARCH_WARN("[ALSA]: Trying the minimum period size instead\n"); - if ((errnum = snd_pcm_hw_params_get_period_size_min(params, &buffer_size, NULL)) < 0) + if ((errnum = snd_pcm_hw_params_get_period_size_min(params, &stream_info->period_frames, NULL)) < 0) { RARCH_ERR("[ALSA]: Failed to get min period size from %s device \"%s\": %s\n", snd_pcm_stream_name(stream), @@ -244,7 +244,16 @@ int alsa_init_pcm(snd_pcm_t **pcm, } } - RARCH_LOG("[ALSA]: Period size: %lu frames\n", buffer_size); + stream_info->period_size = snd_pcm_frames_to_bytes(*pcm, stream_info->period_frames); + if (stream_info->period_size < 0) + { + RARCH_ERR("[ALSA]: Failed to convert a period size of %lu frames to bytes: %s\n", + stream_info->period_frames, + snd_strerror(stream_info->period_frames)); + goto error; + } + + RARCH_LOG("[ALSA]: Period size: %lu frames\n", stream_info->period_frames); if ((errnum = snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) < 0) { @@ -267,6 +276,14 @@ int alsa_init_pcm(snd_pcm_t **pcm, RARCH_LOG("[ALSA]: Buffer size: %lu frames\n", buffer_size); stream_info->buffer_size = snd_pcm_frames_to_bytes(*pcm, buffer_size); + if (stream_info->buffer_size < 0) + { + RARCH_ERR("[ALSA]: Failed to convert a buffer size of %lu frames to bytes: %s\n", + buffer_size, + snd_strerror(buffer_size)); + goto error; + } + stream_info->can_pause = snd_pcm_hw_params_can_pause(params); RARCH_LOG("[ALSA]: Can pause: %s.\n", stream_info->can_pause ? "yes" : "no"); diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h index a3b36fa11cea..3ebc7af85e99 100644 --- a/audio/drivers/alsa.h +++ b/audio/drivers/alsa.h @@ -25,6 +25,8 @@ */ typedef struct alsa_stream_info { size_t buffer_size; + size_t period_size; + snd_pcm_uframes_t period_frames; unsigned int frame_bits; bool has_float; bool can_pause; diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 13716400392c..e3e2b792bcfc 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "../audio_driver.h" @@ -54,8 +55,6 @@ typedef struct alsa_thread * the driver state should track multiple microphone handles, * but the driver *context* should track multiple microphone contexts */ alsa_thread_microphone_t *microphone; - snd_pcm_uframes_t period_frames; - size_t period_size; bool nonblock; bool is_paused; } alsa_thread_t; @@ -63,7 +62,7 @@ typedef struct alsa_thread static void alsa_worker_thread(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; - uint8_t *buf = (uint8_t *)calloc(1, alsa->period_size); + uint8_t *buf = (uint8_t *)calloc(1, alsa->info.stream_info.period_size); uintptr_t id = sthread_get_current_thread_id(); if (!buf) @@ -79,15 +78,15 @@ static void alsa_worker_thread(void *data) snd_pcm_sframes_t frames; slock_lock(alsa->info.fifo_lock); avail = FIFO_READ_AVAIL(alsa->info.buffer); - fifo_size = MIN(alsa->period_size, avail); + fifo_size = MIN(alsa->info.stream_info.period_size, avail); fifo_read(alsa->info.buffer, buf, fifo_size); scond_signal(alsa->info.cond); slock_unlock(alsa->info.fifo_lock); /* If underrun, fill rest with silence. */ - memset(buf + fifo_size, 0, alsa->period_size - fifo_size); + memset(buf + fifo_size, 0, alsa->info.stream_info.period_size - fifo_size); - frames = snd_pcm_writei(alsa->info.pcm, buf, alsa->period_frames); + frames = snd_pcm_writei(alsa->info.pcm, buf, alsa->info.stream_info.period_frames); if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) @@ -119,13 +118,15 @@ static void alsa_worker_thread(void *data) free(buf); } +/** @see alsa_thread_read_microphone() */ static void alsa_microphone_worker_thread(void *data) { - // TODO: Implement - alsa_thread_t *microphone = (alsa_thread_t*)data; - uint8_t *buf = (uint8_t *)calloc(1, microphone->period_size); - uintptr_t thread_id = sthread_get_current_thread_id(); + alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)data; + uint8_t *buf = NULL; + uintptr_t thread_id = sthread_get_current_thread_id(); + retro_assert(microphone != NULL); + buf = (uint8_t *)calloc(1, microphone->info.stream_info.period_size); if (!buf) { RARCH_ERR("[ALSA] [capture thread %p]: Failed to allocate audio buffer\n", thread_id); @@ -133,28 +134,35 @@ static void alsa_microphone_worker_thread(void *data) } while (!microphone->info.thread_dead) - { + { /* Until we're told to stop... */ size_t avail; size_t fifo_size; snd_pcm_sframes_t frames; + /* Lock the incoming sample queue (the main thread may block) */ slock_lock(microphone->info.fifo_lock); - avail = FIFO_READ_AVAIL(microphone->info.buffer); - fifo_size = MIN(microphone->period_size, avail); - fifo_read(microphone->info.buffer, buf, fifo_size); + + /* Fill the incoming sample queue with whatever we recently read */ + avail = FIFO_WRITE_AVAIL(microphone->info.buffer); + fifo_size = MIN(microphone->info.stream_info.period_size, avail); + fifo_write(microphone->info.buffer, buf, fifo_size); + + /* Tell the main thread that it's okay to query the mic again */ scond_signal(microphone->info.cond); + + /* Unlock the incoming sample queue (the main thread may resume) */ slock_unlock(microphone->info.fifo_lock); /* If underrun, fill rest with silence. */ - memset(buf + fifo_size, 0, microphone->period_size - fifo_size); + memset(buf + fifo_size, 0, microphone->info.stream_info.period_size - fifo_size); - frames = snd_pcm_writei(microphone->info.pcm, buf, microphone->period_frames); + frames = snd_pcm_readi(microphone->info.pcm, buf, microphone->info.stream_info.period_frames); if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) { if (snd_pcm_recover(microphone->info.pcm, frames, 1) < 0) { - RARCH_ERR("[ALSA] [capture thread %p]: (#2) Failed to recover from read error: %s\n", + RARCH_ERR("[ALSA] [capture thread %p]: Failed to recover from read error: %s\n", thread_id, snd_strerror(frames)); break; @@ -395,7 +403,7 @@ static void *alsa_thread_init_microphone(void *data, return NULL; } - if (alsa_init_pcm(&alsa->info.pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, &alsa->info.stream_info, new_rate, 0) < 0) + if (alsa_init_pcm(µphone->info.pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->info.stream_info, new_rate, 0) < 0) { goto error; } @@ -404,7 +412,7 @@ static void *alsa_thread_init_microphone(void *data, microphone->info.cond_lock = slock_new(); microphone->info.cond = scond_new(); microphone->info.buffer = fifo_new(microphone->info.stream_info.buffer_size); - if (!microphone->info.fifo_lock || !microphone->info.cond_lock || !microphone->info.cond || !microphone->info.buffer) + if (!microphone->info.fifo_lock || !microphone->info.cond_lock || !microphone->info.cond || !microphone->info.buffer || !microphone->info.pcm) goto error; microphone->info.worker_thread = sthread_create(alsa_microphone_worker_thread, microphone); @@ -413,6 +421,7 @@ static void *alsa_thread_init_microphone(void *data, RARCH_ERR("[ALSA]: Failed to initialize microphone worker thread\n"); goto error; } + RARCH_DBG("[ALSA]: Initialized microphone worker thread\n"); alsa->microphone = microphone; return microphone; @@ -473,9 +482,83 @@ static bool alsa_thread_set_microphone_state(void *data, void *microphone_contex return true; } -static ssize_t alsa_thread_read_microphone(void *driver_context, void *microphone_context, void *buf_, size_t size_) +/** @see alsa_microphone_worker_thread() */ +static ssize_t alsa_thread_read_microphone(void *driver_context, void *microphone_context, void *buf, size_t size) { + alsa_thread_t *alsa = (alsa_thread_t*)driver_context; + alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)microphone_context; + + if (!alsa || !microphone || !buf) /* If any of the parameters were invalid... */ + return -1; + + if (microphone->info.thread_dead) /* If the mic thread is shutting down... */ + return -1; + + if (alsa->nonblock) + { /* If driver interactions shouldn't block... */ + size_t avail; + size_t write_amt; + /* "Hey, I'm gonna borrow the queue." */ + slock_lock(microphone->info.fifo_lock); + + avail = FIFO_READ_AVAIL(microphone->info.buffer); + write_amt = MIN(avail, size); + + /* "It's okay if you don't have any new samples, I'll just check in on you later." */ + fifo_read(microphone->info.buffer, buf, write_amt); + + /* "Here, take this queue back." */ + slock_unlock(microphone->info.fifo_lock); + + return write_amt; + } + else + { + size_t written = 0; + while (written < size && !microphone->info.thread_dead) + { /* Until we've written all requested samples (or we're told to stop)... */ + size_t avail; + + /* "Hey, I'm gonna borrow the queue." */ + slock_lock(microphone->info.fifo_lock); + + avail = FIFO_READ_AVAIL(microphone->info.buffer); + + if (avail == 0) + { /* "Oh, wait, it's empty." */ + + /* "Here, take it back..." */ + slock_unlock(microphone->info.fifo_lock); + + /* "...I'll just wait right here." */ + slock_lock(microphone->info.cond_lock); + + /* "Unless we're closing up shop..." */ + if (!microphone->info.thread_dead) + /* "...let me know when you've produced some samples." */ + scond_wait(microphone->info.cond, microphone->info.cond_lock); + + /* "Oh, you're ready? Okay, I'm gonna continue." */ + slock_unlock(microphone->info.cond_lock); + } + else + { + size_t write_amt = MIN(size - written, avail); + + /* "I'll just go ahead and consume all these samples..." + * (As many as will fit in buf, or as many as are available.) */ + fifo_write(microphone->info.buffer,(const char*)buf + written, write_amt); + + /* "I'm done, you can take the queue back now." */ + slock_unlock(microphone->info.fifo_lock); + written += write_amt; + } + + /* "I'll be right back..." */ + } + return written; + } } audio_driver_t audio_alsathread = { From 643e7c7540e62f5abd6f35d11f8ec56ba856710b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 18 Jan 2023 07:45:16 -0500 Subject: [PATCH 113/309] Slight cleanups for clarity --- audio/drivers/alsa.c | 4 ++-- audio/drivers/alsathread.c | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 2dacd4f790a7..fad27c130eff 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -253,7 +253,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, goto error; } - RARCH_LOG("[ALSA]: Period size: %lu frames\n", stream_info->period_frames); + RARCH_LOG("[ALSA]: Period size: %lu frames (%lu bytes)\n", stream_info->period_frames, stream_info->period_size); if ((errnum = snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) < 0) { @@ -273,7 +273,6 @@ int alsa_init_pcm(snd_pcm_t **pcm, } } - RARCH_LOG("[ALSA]: Buffer size: %lu frames\n", buffer_size); stream_info->buffer_size = snd_pcm_frames_to_bytes(*pcm, buffer_size); if (stream_info->buffer_size < 0) @@ -283,6 +282,7 @@ int alsa_init_pcm(snd_pcm_t **pcm, snd_strerror(buffer_size)); goto error; } + RARCH_LOG("[ALSA]: Buffer size: %lu frames (%lu bytes)\n", buffer_size, stream_info->buffer_size); stream_info->can_pause = snd_pcm_hw_params_can_pause(params); diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index e3e2b792bcfc..96ecf153209c 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -63,14 +63,15 @@ static void alsa_worker_thread(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; uint8_t *buf = (uint8_t *)calloc(1, alsa->info.stream_info.period_size); - uintptr_t id = sthread_get_current_thread_id(); + uintptr_t thread_id = sthread_get_current_thread_id(); if (!buf) { - RARCH_ERR("[ALSA] [playback thread %u]: Failed to allocate audio buffer\n", id); + RARCH_ERR("[ALSA] [playback thread %u]: Failed to allocate audio buffer\n", thread_id); goto end; } + RARCH_DBG("[ALSA] [playback thread %p]: Beginning playback worker thread\n", thread_id); while (!alsa->info.thread_dead) { size_t avail; @@ -91,10 +92,10 @@ static void alsa_worker_thread(void *data) if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) { - if (snd_pcm_recover(alsa->info.pcm, frames, 1) < 0) + if (snd_pcm_recover(alsa->info.pcm, frames, false) < 0) { RARCH_ERR("[ALSA] [playback thread %u]: Failed to recover from error: %s\n", - id, + thread_id, snd_strerror(frames)); break; } @@ -104,7 +105,7 @@ static void alsa_worker_thread(void *data) else if (frames < 0) { RARCH_ERR("[ALSA] [playback thread %u]: Error writing audio to device: %s\n", - id, + thread_id, snd_strerror(frames)); break; } @@ -116,6 +117,7 @@ static void alsa_worker_thread(void *data) scond_signal(alsa->info.cond); slock_unlock(alsa->info.cond_lock); free(buf); + RARCH_DBG("[ALSA] [playback thread %p]: Ending playback worker thread\n", thread_id); } /** @see alsa_thread_read_microphone() */ @@ -133,6 +135,7 @@ static void alsa_microphone_worker_thread(void *data) goto end; } + RARCH_DBG("[ALSA] [capture thread %p]: Beginning microphone worker thread\n", thread_id); while (!microphone->info.thread_dead) { /* Until we're told to stop... */ size_t avail; @@ -160,7 +163,7 @@ static void alsa_microphone_worker_thread(void *data) if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) { - if (snd_pcm_recover(microphone->info.pcm, frames, 1) < 0) + if (snd_pcm_recover(microphone->info.pcm, frames, false) < 0) { RARCH_ERR("[ALSA] [capture thread %p]: Failed to recover from read error: %s\n", thread_id, @@ -185,6 +188,7 @@ static void alsa_microphone_worker_thread(void *data) scond_signal(microphone->info.cond); slock_unlock(microphone->info.cond_lock); free(buf); + RARCH_DBG("[ALSA] [capture thread %p]: Ending microphone worker thread\n", thread_id); } static void alsa_thread_free_info_members(alsa_thread_info_t *info) From 907dc8d103c56224dc1381aac4fad3a3b00a4eb4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 18 Jan 2023 10:44:05 -0500 Subject: [PATCH 114/309] Add alsa_set_mic_enabled_internal - For setting the state of a microphone while considering its current state --- audio/drivers/alsa.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ audio/drivers/alsa.h | 5 ++++ 2 files changed, 73 insertions(+) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index fad27c130eff..b65106677dba 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -782,6 +782,74 @@ static bool alsa_get_microphone_state(const void *data, const void *microphone_c * or it might be stopped because the entire audio driver is stopped. */ } +bool alsa_set_mic_enabled_internal(snd_pcm_t *microphone, bool enabled) +{ + snd_pcm_state_t microphone_state = snd_pcm_state(microphone); + int errnum = 0; + + if (enabled) + { /* If we're trying to unpause a mic (or maybe activate it for the first time)... */ + switch (microphone_state) + { + case SND_PCM_STATE_PAUSED: /* If we're unpausing a valid (but paused) mic... */ + if ((errnum = snd_pcm_pause(microphone, false)) < 0) + { /* ...but we failed... */ + goto error; + } + break; + case SND_PCM_STATE_PREPARED: + /* If we're activating this mic for the first time... */ + if ((errnum = snd_pcm_start(microphone)) < 0) + { /* ..but we failed... */ + goto error; + } + break; + default: + goto unexpected_state; + } + } + else + { /* We're pausing this mic */ + switch (microphone_state) + { + case SND_PCM_STATE_PREPARED: + case SND_PCM_STATE_RUNNING: + /* If we're pausing an active mic... */ + if ((errnum = snd_pcm_pause(microphone, true)) < 0) + { /* ...but we failed... */ + goto error; + } + break; + default: + goto unexpected_state; + } + } + + RARCH_DBG("[ALSA]: %s microphone \"%s\", transitioning from %s to %s\n", + enabled ? "Unpaused" : "Paused", + snd_pcm_name(microphone), + snd_pcm_state_name(microphone_state), + snd_pcm_state_name(snd_pcm_state(microphone))); + + return true; +error: + RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in state %s: %s\n", + enabled ? "unpause" : "pause", + snd_pcm_name(microphone), + snd_pcm_state_name(microphone_state), + snd_strerror(errnum)); + + return false; + +unexpected_state: + RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in unexpected state %s\n", + enabled ? "unpause" : "pause", + snd_pcm_name(microphone), + snd_pcm_state_name(microphone_state)); + + return false; +} + static bool alsa_set_microphone_state(void *data, void *microphone_context, bool enabled) { alsa_t *alsa = (alsa_t*)data; diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h index 3ebc7af85e99..305c45ca808a 100644 --- a/audio/drivers/alsa.h +++ b/audio/drivers/alsa.h @@ -45,4 +45,9 @@ void alsa_free_pcm(snd_pcm_t *pcm); void *alsa_device_list_new(void *data); void alsa_device_list_free(void *data, void *array_list_data); +/** + * Sets the state of the PCM stream without updating the mic state + */ +bool alsa_set_mic_enabled_internal(snd_pcm_t *microphone, bool enabled); + #endif /* _RETROARCH_ALSA */ From 01cec0f1cd98dcf4f6a48d236f65e5dc66200e08 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 18 Jan 2023 10:45:01 -0500 Subject: [PATCH 115/309] Use alsa_set_mic_enabled_internal --- audio/drivers/alsa.c | 15 ++++---------- audio/drivers/alsathread.c | 42 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index b65106677dba..36b71721d497 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -867,19 +867,12 @@ static bool alsa_set_microphone_state(void *data, void *microphone_context, bool if (!alsa->is_paused) { /* If the entire audio driver isn't paused... */ - int errnum = snd_pcm_pause(microphone->pcm, !enabled); - if (errnum < 0) + if (alsa_set_mic_enabled_internal(microphone->pcm, enabled)) { - RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\": %s\n", - enabled ? "unpause" : "pause", - snd_pcm_name(microphone->pcm), - snd_strerror(errnum)); - return false; + microphone->is_paused = !enabled; + return true; } - - microphone->is_paused = !enabled; - RARCH_DBG("[ALSA]: Set state of microphone \"%s\" to %s\n", - snd_pcm_name(microphone->pcm), enabled ? "enabled" : "disabled"); + return false; } return true; diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 96ecf153209c..9d6637a2d6c0 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -480,9 +480,23 @@ static bool alsa_thread_set_microphone_state(void *data, void *microphone_contex return false; /* Both params must be non-null */ - microphone->is_paused = !enabled; + if (!microphone->info.stream_info.can_pause) + { + RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->info.pcm)); + return true; + } + + if (!alsa->is_paused) + { /* If the entire audio driver isn't paused... */ + if (alsa_set_mic_enabled_internal(microphone->info.pcm, enabled)) + { + // TODO: Do I need to synchronize microphone->is_paused? + microphone->is_paused = !enabled; + return true; + } + return false; + } - // TODO: Do I need to synchronize this? return true; } @@ -491,6 +505,7 @@ static ssize_t alsa_thread_read_microphone(void *driver_context, void *microphon { alsa_thread_t *alsa = (alsa_thread_t*)driver_context; alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)microphone_context; + snd_pcm_state_t state; if (!alsa || !microphone || !buf) /* If any of the parameters were invalid... */ return -1; @@ -498,6 +513,29 @@ static ssize_t alsa_thread_read_microphone(void *driver_context, void *microphon if (microphone->info.thread_dead) /* If the mic thread is shutting down... */ return -1; + if (microphone->is_paused) + if (!alsa_thread_set_microphone_state(alsa, microphone, true)) + return -1; + + state = snd_pcm_state(microphone->info.pcm); + if (state != SND_PCM_STATE_RUNNING) + { + int errnum; + RARCH_WARN("[ALSA]: Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", + snd_pcm_name(microphone->info.pcm), + snd_pcm_state_name(state)); + + errnum = snd_pcm_start(microphone->info.pcm); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", + snd_pcm_name(microphone->info.pcm), + snd_strerror(errnum)); + + return -1; + } + } + if (alsa->nonblock) { /* If driver interactions shouldn't block... */ size_t avail; From 94a0dc3ceaacb7a27bf347944d6ade6b00fcdeb8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 18 Jan 2023 10:45:27 -0500 Subject: [PATCH 116/309] Log a little more info --- audio/drivers/alsa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 36b71721d497..7a8b7f451e23 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -595,8 +595,9 @@ static bool alsa_start(void *data, bool is_shutdown) int errnum = snd_pcm_pause(alsa->microphone->pcm, false); if (errnum < 0) { - RARCH_ERR("[ALSA]: Failed to unpause microphone \"%s\": %s\n", + RARCH_ERR("[ALSA]: Failed to unpause microphone \"%s\" in state %s: %s\n", snd_pcm_name(alsa->microphone->pcm), + snd_pcm_state_name(snd_pcm_state(alsa->microphone->pcm)), snd_strerror(errnum)); return false; From 533833f7369161219c7ebb6af42c8a6a05fc6e4f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 18 Jan 2023 11:08:13 -0500 Subject: [PATCH 117/309] Log when the audio driver is started/stopped --- audio/audio_driver.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 063de9ee47f5..ed3b30d7458e 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -1749,6 +1749,10 @@ bool audio_driver_start(bool is_shutdown) audio_st->context_audio_data, is_shutdown)) goto error; + RARCH_DBG("[Audio]: Started audio driver \"%s\" (is_shutdown=%s)\n", + audio_st->current_audio->ident, + is_shutdown ? "true" : "false"); + return true; error: @@ -1766,8 +1770,13 @@ bool audio_driver_stop(void) || !audio_driver_alive() ) return false; - return audio_driver_st.current_audio->stop( + bool stopped = audio_driver_st.current_audio->stop( audio_driver_st.context_audio_data); + + if (stopped) + RARCH_DBG("[Audio]: Stopped audio driver \"%s\"\n", audio_driver_st.current_audio->ident); + + return stopped; } bool audio_driver_supports_microphone(const audio_driver_t* driver) From e123ecbf0134f72d97bfeba2129f7cd81329d50c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 23 Jan 2023 16:46:07 -0500 Subject: [PATCH 118/309] Move base microphone driver code into a new directory - Add microphone_driver.c to Makefile.common - Rename functions as needed --- Makefile.common | 1 + audio/audio_driver.c | 470 +----------------------- audio/audio_driver.h | 209 +---------- microphone/microphone_driver.c | 644 +++++++++++++++++++++++++++++++++ microphone/microphone_driver.h | 505 ++++++++++++++++++++++++++ runloop.c | 17 +- 6 files changed, 1164 insertions(+), 682 deletions(-) create mode 100644 microphone/microphone_driver.c create mode 100644 microphone/microphone_driver.h diff --git a/Makefile.common b/Makefile.common index 547c9328561b..fdf38c6d86bf 100644 --- a/Makefile.common +++ b/Makefile.common @@ -314,6 +314,7 @@ OBJ += \ file_path_special.o \ $(LIBRETRO_COMM_DIR)/hash/lrc_hash.o \ audio/audio_driver.o \ + microphone/microphone_driver.o \ input/input_driver.o \ input/common/input_hid_common.o \ led/led_driver.o \ diff --git a/audio/audio_driver.c b/audio/audio_driver.c index ed3b30d7458e..5142640caf93 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -236,24 +236,6 @@ static bool audio_driver_free_devices_list(void) return true; } -/* TODO: When adding support for multiple microphones, - * make sure you clean them all up in here. */ -static bool audio_driver_free_microphones(void) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - const audio_driver_t *audio_driver = audio_st->current_audio; - - if ( !audio_driver || - !audio_driver->free_microphone || - !audio_st->context_audio_data || - !audio_st->current_microphone.active) - return false; - - audio_driver_free_microphone(&audio_st->current_microphone); - - return true; -} - #ifdef DEBUG static void report_audio_buffer_statistics(void) { @@ -305,10 +287,6 @@ static bool audio_driver_deinit_internal(bool audio_enable) memalign_free(audio_st->output_samples_conv_buf); audio_st->output_samples_conv_buf = NULL; - if (audio_st->input_samples_conv_buf) - memalign_free(audio_st->input_samples_conv_buf); - audio_st->input_samples_conv_buf = NULL; - if (audio_st->input_data) memalign_free(audio_st->input_data); @@ -325,7 +303,6 @@ static bool audio_driver_deinit_internal(bool audio_enable) if (!audio_enable) { audio_st->flags &= ~AUDIO_FLAG_ACTIVE; - audio_st->flags &= ~AUDIO_FLAG_MIC_ACTIVE; return false; } @@ -335,10 +312,6 @@ static bool audio_driver_deinit_internal(bool audio_enable) memalign_free(audio_st->output_samples_buf); audio_st->output_samples_buf = NULL; - if (audio_st->input_samples_buf) - memalign_free(audio_st->input_samples_buf); - audio_st->input_samples_buf = NULL; - #ifdef HAVE_DSP_FILTER audio_driver_dsp_filter_free(); #endif @@ -373,7 +346,6 @@ bool audio_driver_deinit(void) audio_driver_mixer_deinit(); #endif audio_driver_free_devices_list(); - audio_driver_free_microphones(); return audio_driver_deinit_internal( settings->bools.audio_enable); } @@ -585,8 +557,6 @@ static void audio_driver_flush( } } -static void audio_driver_init_microphone_internal(retro_microphone_t* microphone); - #ifdef HAVE_AUDIOMIXER audio_mixer_stream_t *audio_driver_mixer_get_stream(unsigned i) { @@ -612,11 +582,9 @@ bool audio_driver_init_internal( { unsigned new_rate = 0; float *out_samples_buf = NULL; - float *in_samples_buf = NULL; settings_t *settings = (settings_t*)settings_data; size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2; bool audio_enable = settings->bools.audio_enable; - bool audio_enable_microphone = settings->bools.audio_enable_input; bool audio_sync = settings->bools.audio_sync; bool audio_rate_control = settings->bools.audio_rate_control; float slowmotion_ratio = settings->floats.slowmotion_ratio; @@ -629,10 +597,7 @@ bool audio_driver_init_internal( #endif /* Accomodate rewind since at some point we might have two full buffers. */ size_t outsamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * AUDIO_MAX_RATIO * slowmotion_ratio; - size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; int16_t *out_conv_buf = (int16_t*)memalign_alloc(64, outsamples_max * sizeof(int16_t)); - int16_t *in_conv_buf = audio_enable_microphone ? - (int16_t*)memalign_alloc(64, insamples_max * sizeof(int16_t)) : NULL; size_t audio_buf_length = AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float); float *audio_buf = (float*)memalign_alloc(64, audio_buf_length); bool verbosity_enabled = verbosity_is_enabled(); @@ -640,9 +605,8 @@ bool audio_driver_init_internal( convert_s16_to_float_init_simd(); convert_float_to_s16_init_simd(); - if (!out_conv_buf || !audio_buf || (audio_enable_microphone && !in_conv_buf)) + if (!out_conv_buf || !audio_buf) goto error; - /* It's not an error for in_conv_buf to be null if we didn't ask for a mic */ memset(audio_buf, 0, AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float)); @@ -650,8 +614,6 @@ bool audio_driver_init_internal( audio_driver_st.input_data_length = audio_buf_length; audio_driver_st.output_samples_conv_buf = out_conv_buf; audio_driver_st.output_samples_conv_buf_length = outsamples_max * sizeof(int16_t); - audio_driver_st.input_samples_conv_buf = in_conv_buf; - audio_driver_st.input_samples_conv_buf_length = (in_conv_buf) ? insamples_max * sizeof(int16_t) : 0; audio_driver_st.chunk_block_size = AUDIO_CHUNK_SIZE_BLOCKING; audio_driver_st.chunk_nonblock_size = AUDIO_CHUNK_SIZE_NONBLOCKING; audio_driver_st.chunk_size = audio_driver_st.chunk_block_size; @@ -672,12 +634,6 @@ bool audio_driver_init_internal( return false; } - if (audio_enable_microphone) - audio_driver_st.flags |= AUDIO_FLAG_MIC_ACTIVE; - else - audio_driver_st.flags &= ~AUDIO_FLAG_MIC_ACTIVE; - /* Not an error if the mic is disabled */ - if (!(audio_driver_find_driver(settings, "audio driver", verbosity_enabled))) { @@ -692,14 +648,6 @@ bool audio_driver_init_internal( return false; } - if ((audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) - && !audio_driver_supports_microphone(audio_driver_st.current_audio)) - { - RARCH_WARN("[Audio]: Microphone support is enabled, but the %s driver doesn't support it. Will continue without it.\n", - audio_driver_st.current_audio->ident); - audio_driver_st.flags &= ~AUDIO_FLAG_MIC_ACTIVE; - } - #ifdef HAVE_THREADS if (audio_cb_inited) { @@ -797,17 +745,12 @@ bool audio_driver_init_internal( audio_driver_st.data_ptr = 0; out_samples_buf = (float*)memalign_alloc(64, outsamples_max * sizeof(float)); - in_samples_buf = (audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) ? - memalign_alloc(64, insamples_max * sizeof(float)) : NULL; - /* It's not an error for in_samples_buf to be NULL if we don't want the mic */ - if (!out_samples_buf || ((audio_driver_st.flags & AUDIO_FLAG_MIC_ACTIVE) && !in_samples_buf)) + if (!out_samples_buf) goto error; audio_driver_st.output_samples_buf = (float*)out_samples_buf; audio_driver_st.output_samples_buf_length = outsamples_max * sizeof(float); - audio_driver_st.input_samples_buf = (float*)in_samples_buf; - audio_driver_st.input_samples_buf_length = (in_samples_buf) ? insamples_max * sizeof(float) : 0; audio_driver_st.flags &= ~AUDIO_FLAG_CONTROL; if ( @@ -837,23 +780,6 @@ bool audio_driver_init_internal( audio_mixer_init(settings->uints.audio_output_sample_rate); #endif - if ( audio_driver_st.current_microphone.active && - !audio_driver_st.current_microphone.microphone_context) - { /* If the core requested a microphone before the driver was able to provide one...*/ - /* Now that the driver and driver context are ready, let's initialize the mid */ - audio_driver_init_microphone_internal(&audio_driver_st.current_microphone); - - if (audio_driver_st.current_microphone.microphone_context) - RARCH_DBG("[Audio]: Initialized a previously-pending microphone\n"); - else - { - RARCH_ERR("[Audio]: Failed to initialize a previously pending microphone; microphone will not be used\n"); - - audio_driver_free_microphone(&audio_driver_st.current_microphone); - } - - } - /* Threaded driver is initially stopped. */ if ( (audio_driver_st.flags & AUDIO_FLAG_ACTIVE) @@ -1779,398 +1705,6 @@ bool audio_driver_stop(void) return stopped; } -bool audio_driver_supports_microphone(const audio_driver_t* driver) -{ - return driver && - driver->init_microphone && - driver->free_microphone && - driver->set_microphone_state && - driver->get_microphone_state && - driver->read_microphone; -} - -static void audio_driver_microphone_handle_init(retro_microphone_t *microphone, void *microphone_context) -{ - if (microphone) - { - microphone->microphone_context = microphone_context; - microphone->pending_enabled = false; - microphone->active = true; - microphone->sample_buffer = NULL; - microphone->sample_buffer_length = 0; - microphone->most_recent_copy_length = 0; - microphone->error = false; - } -} - -static void audio_driver_microphone_handle_free(retro_microphone_t *microphone) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - const audio_driver_t *audio_driver = audio_st->current_audio; - void *context = audio_st->context_audio_data; - - if (!microphone) - return; - - if (!context) - RARCH_WARN("[Audio]: Attempted to free a microphone without an active driver context\n"); - - if (microphone->microphone_context) - { - audio_driver->free_microphone(context, microphone->microphone_context); - microphone->microphone_context = NULL; - } - - if (microphone->sample_buffer) - { - memalign_free(microphone->sample_buffer); - microphone->sample_buffer = NULL; - } - - memset(microphone, 0, sizeof(*microphone)); - /* Do NOT free the microphone handle itself! It's allocated statically! */ -} - -/** - * - * @param microphone Handle to the microphone to init with a context - * Check the value of microphone->microphone_context to see if this function succeeded - */ -static void audio_driver_init_microphone_internal(retro_microphone_t* microphone) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - settings_t *settings = config_get_ptr(); - const audio_driver_t *audio_driver = audio_st->current_audio; - void *context = audio_st->context_audio_data; - unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; - unsigned setting_audio_latency = settings->uints.audio_input_latency; - unsigned actual_sample_rate = 0; - unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? - runloop_audio_latency : setting_audio_latency; - float slowmotion_ratio = settings->floats.slowmotion_ratio; - size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; - - if (!microphone || !audio_driver) - return; - - microphone->sample_buffer_length = insamples_max * sizeof(int16_t); - microphone->error = false; - microphone->sample_buffer = - (int16_t*)memalign_alloc(64, microphone->sample_buffer_length); - - if (!microphone->sample_buffer) - goto error; - - microphone->microphone_context = audio_driver->init_microphone(context, - *settings->arrays.audio_input_device ? settings->arrays.audio_input_device : NULL, - settings->uints.audio_input_sample_rate, - audio_latency, - settings->uints.audio_input_block_frames, - &actual_sample_rate); - - if (!microphone->microphone_context) - goto error; - - audio_driver_set_microphone_state(microphone, microphone->pending_enabled); - - if (actual_sample_rate != 0) - { - RARCH_LOG("[Audio]: Requested microphone sample rate of %uHz, got %uHz. Updating settings with this value.\n", - settings->uints.audio_input_sample_rate, - actual_sample_rate - ); - configuration_set_uint(settings, settings->uints.audio_input_sample_rate, actual_sample_rate); - } - - RARCH_LOG("[Audio]: Initialized microphone\n", actual_sample_rate); - return; -error: - audio_driver_microphone_handle_free(microphone); - RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); -} - -/* NOTE: The core may request a microphone before the driver is ready. - * A pending handle will be provided in that case, and the frontend will - * initialize the microphone when the time is right; - * do not call this function twice on the same mic. */ -retro_microphone_t *audio_driver_init_microphone(void) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - const settings_t *settings = config_get_ptr(); - const audio_driver_t *audio_driver = audio_st->current_audio; - void *context = audio_st->context_audio_data; - - if (!settings) - { - RARCH_ERR("[Audio]: Failed to initialize microphone due to uninitialized config\n"); - return NULL; - } - - if (!audio_driver) - { - RARCH_ERR("[Audio]: Failed to initialize microphone due to uninitialized audio driver\n"); - return NULL; - } - - if (!settings->bools.audio_enable_input) - { /* Not checking audio_st->flags because they might not be set yet; - * don't forget, the user can ask for a mic - * before the audio driver is ready to create one. */ - RARCH_WARN("[Audio]: Refused to initialize microphone because it's disabled in the settings\n"); - return NULL; - } - - if (!audio_driver->init_microphone) - { - RARCH_ERR("[Audio]: Failed to initialize microphone due to lack of support in %s driver\n", - audio_driver->ident); - return NULL; - } - - if (audio_st->current_microphone.active) - { /* If the core has requested a second microphone... */ - RARCH_ERR("[Audio]: Failed to initialize a second microphone, frontend only supports one at a time right now\n"); - if (audio_st->current_microphone.microphone_context) - /* If that mic is initialized... */ - RARCH_ERR("[Audio]: An initialized microphone exists\n"); - else - /* That mic is pending */ - RARCH_ERR("[Audio]: A microphone is pending initialization\n"); - - return NULL; - } - - /* Cores might ask for a microphone before the audio driver is ready to provide them; - * if that happens, we have to initialize the microphones later. - * But the user still wants a handle, so we'll give them one. - */ - audio_driver_microphone_handle_init(&audio_st->current_microphone, context); - /* If context is NULL, the handle won't have a valid microphone context (but we'll create one later) */ - - if (context) - { /* If the audio driver is ready to initialize a microphone... */ - audio_driver_init_microphone_internal(&audio_st->current_microphone); - - if (audio_st->current_microphone.microphone_context) /* If the microphone was successfully initialized... */ - RARCH_LOG("[Audio]: Initialized the requested microphone successfully\n"); - else - goto mic_context_init_failed; - } - else - { /* If the audio driver isn't ready to create a microphone... */ - RARCH_LOG("[Audio]: Microphone requested before audio context was ready; deferring initialization\n"); - } - - return &audio_st->current_microphone; -mic_context_init_failed: - audio_driver_microphone_handle_free(&audio_st->current_microphone); - - return NULL; -} - -void audio_driver_free_microphone(retro_microphone_t *microphone) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - void *context = audio_st->context_audio_data; - const audio_driver_t *audio_driver = audio_st->current_audio; - - if ( microphone && - context && - audio_driver && - audio_driver->free_microphone) - { - audio_driver_microphone_handle_free(microphone); - } -} - -bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool state) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - void *context = audio_st->context_audio_data; - const audio_driver_t *audio_driver = audio_st->current_audio; - - if (!microphone || !microphone->active || !audio_driver || !audio_driver->set_microphone_state) - return false; - - if (context && microphone->microphone_context) - { /* If the driver is initialized... */ - bool success = audio_driver->set_microphone_state(context, microphone->microphone_context, state); - - if (success) - RARCH_DBG("[Audio]: Set initialized microphone state to %s\n", - state ? "enabled" : "disabled"); - else - RARCH_ERR("[Audio]: Failed to set initialized microphone state to %s\n", - state ? "enabled" : "disabled"); - - return success; - } - else - { /* The driver's not ready yet, so we'll make a note - * of what the mic's state should be */ - microphone->pending_enabled = state; - RARCH_DBG("[Audio]: Set pending microphone state to %s\n", - state ? "enabled" : "disabled"); - return true; - /* This isn't an error */ - } -} - -bool audio_driver_get_microphone_state(const retro_microphone_t *microphone) -{ - audio_driver_state_t *audio_st = &audio_driver_st; - void *context = audio_st->context_audio_data; - const audio_driver_t *audio_driver = audio_st->current_audio; - - if (!microphone || !microphone->active || !audio_driver || !audio_driver->get_microphone_state) - return false; - - if (context && microphone->microphone_context) - { /* If the driver is initialized... */ - return audio_driver->get_microphone_state(context, microphone->microphone_context); - } - else - { /* The driver's not ready yet, - * so we'll use that note we made of the mic's active state */ - return microphone->pending_enabled; - } -} - -/** - * Pull queued microphone samples from the driver - * and copy them to the provided buffer(s). - * - * Only one mic is supported right now, - * but the API is designed to accommodate multiple. - * If multi-mic support is implemented, - * you'll want to update this function. - * - * Note that microphone samples are provided in mono, - * so a "sample" and a "frame" are equivalent here. - * - * @param audio_st The overall state of the audio driver. - * @param slowmotion_ratio TODO - * @param audio_fastforward_mute True if no audio should be input while the game is in fast-forward. - * @param[out] frames The buffer in which the core will receive microphone samples. - * @param num_frames The size of \c frames, in samples. - * @param is_slowmotion True if the player is running the core in slow-motion. - * @param is_fastmotion True if the player is running the core in fast-forward. - * - * @see audio_driver_flush() - */ -static void audio_driver_flush_microphone_input( - audio_driver_state_t *audio_st, - retro_microphone_t *microphone, - float slowmotion_ratio, - bool audio_fastforward_mute, - int16_t *frames, size_t num_frames, - bool is_slowmotion, bool is_fastmotion) -{ - if ( audio_st && /* If the audio driver state is valid... */ - (audio_st->flags & AUDIO_FLAG_MIC_ACTIVE) && /* ...and mic support is on... */ - audio_st->current_audio && - audio_st->context_audio_data && /* ...and the audio driver is initialized... */ - audio_st->input_samples_buf && /* ...with scratch space... */ - microphone && - microphone->active && /* ...and the mic itself is initialized... */ - microphone->microphone_context && /* ...and ready... */ - microphone->sample_buffer && - microphone->sample_buffer_length && /* ...with a non-empty sample buffer... */ - - audio_st->current_audio->read_microphone && /* ...and valid function pointers... */ - audio_st->current_audio->get_microphone_state && - audio_st->current_audio->get_microphone_state( /* ...and it's enabled... */ - audio_st->current_audio, - microphone->microphone_context)) - { - void *buffer_source = NULL; - unsigned sample_size = audio_driver_get_sample_size(); - size_t bytes_to_read = MIN(audio_st->input_samples_buf_length, num_frames * sample_size); - ssize_t bytes_read = audio_st->current_audio->read_microphone( - audio_st->context_audio_data, - microphone->microphone_context, - audio_st->input_samples_buf, - bytes_to_read); - /* First, get the most recent mic data */ - - if (bytes_read < 0) - { /* If there was an error... */ - microphone->error = true; - return; - } - else if (bytes_read == 0) - { - microphone->error = false; - return; - } - - if (audio_st->flags & AUDIO_FLAG_USE_FLOAT) - { - convert_float_to_s16(audio_st->input_samples_conv_buf, audio_st->input_samples_buf, bytes_read / sample_size); - buffer_source = audio_st->input_samples_conv_buf; - } - else - { - buffer_source = audio_st->input_samples_buf; - } - - memcpy(frames, buffer_source, num_frames * sizeof(int16_t)); - } -} - - -int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* frames, size_t num_frames) -{ - uint32_t runloop_flags; - size_t frames_remaining = num_frames; - audio_driver_state_t *audio_st = &audio_driver_st; - - if (audio_st->flags & AUDIO_FLAG_SUSPENDED - || num_frames == 0 - || !frames - || !microphone - || !microphone->active - || !audio_driver_get_microphone_state(microphone) - ) - { /* If the driver is suspended, or the core didn't actually ask for frames, - * or the microphone is disabled... */ - return -1; - } - - if (!(audio_st->context_audio_data && microphone->microphone_context)) - { /* If the microphone isn't ready... */ - return 0; /* Not an error */ - } - - runloop_flags = runloop_get_flags(); - - do - { - size_t frames_to_read = - (frames_remaining > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)) ? - (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1) : frames_remaining; - - if (!( (runloop_flags & RUNLOOP_FLAG_PAUSED) - || !(audio_st->flags & (AUDIO_FLAG_ACTIVE | AUDIO_FLAG_MIC_ACTIVE)) - || !(audio_st->input_samples_buf))) - /* If the game is running, the audio driver and mic are running, - * and the input sample buffer is valid... */ - audio_driver_flush_microphone_input(audio_st, - microphone, - config_get_ptr()->floats.slowmotion_ratio, - config_get_ptr()->bools.audio_fastforward_mute, - frames, - frames_to_read, - runloop_flags & RUNLOOP_FLAG_SLOWMOTION, - runloop_flags & RUNLOOP_FLAG_FASTMOTION); - frames_remaining -= frames_to_read; - frames += frames_to_read << 1; - } - while (frames_remaining > 0); - - return num_frames; -} - #ifdef HAVE_REWIND void audio_driver_frame_is_reverse(void) { diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 97989d49e6b5..72f57044355a 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -76,47 +76,6 @@ typedef struct audio_mixer_stream_params } audio_mixer_stream_params_t; #endif -/** - * Driver object that tracks a microphone's state. - * Pointers to this object are provided to cores, - * to be used as opaque handles. - */ -struct retro_microphone -{ - /** - * Pointer to the context object created by the underlying driver. - */ - void *microphone_context; - - /** - * Pointer to the data that will be copied to cores. - */ - int16_t* sample_buffer; - - /** - * Length of sample_buffer in bytes, \em not samples. - */ - size_t sample_buffer_length; - - /** - * Number of bytes that were copied into the buffer in the most recent flush. - * Accounts for resampling - **/ - size_t most_recent_copy_length; - - /* May be enabled even before the driver is ready */ - bool pending_enabled; - - /** - * True if this object represents a valid or pending microphone. - * Mostly exists because retro_microphone is statically allocated, - * so there's no reason to check it against NULL. - */ - bool active; - - bool error; -}; - typedef struct audio_driver { /* Creates and initializes handle to audio driver. @@ -154,9 +113,7 @@ typedef struct audio_driver ssize_t (*write)(void *data, const void *buf, size_t size); /** - * Temporarily pauses the audio driver, including microphones. - * Microphone "paused" state will not be updated, - * but they will stop recording until start is called. + * Temporarily pauses the audio driver. * * @param data Opaque handle to the audio driver context * that was returned by \c init. @@ -167,8 +124,6 @@ typedef struct audio_driver /** * Resumes audio driver from the paused state. - * Microphones will resume recording \em if they were already active - * before the driver was stopped. **/ bool (*start)(void *data, bool is_shutdown); @@ -184,7 +139,7 @@ typedef struct audio_driver * */ void (*set_nonblock_state)(void *data, bool toggle); - /* Stops and frees driver data, including microphones. */ + /* Stops and frees driver. */ void (*free)(void *data); /* Defines if driver will take standard floating point samples, @@ -210,135 +165,6 @@ typedef struct audio_driver size_t (*write_avail)(void *data); size_t (*buffer_size)(void *data); - - /* The following microphone functions are all optional. - * For a driver to support microphone functionality, - * it must provide all of these functions. - * - * If your driver doesn't support microphones, - * leave these function pointers as NULL. */ - - /** - * Initializes a microphone using the audio driver. - * Cores that use microphone functionality will call this via - * retro_microphone_interface::init_microphone. - * - * @param data Handle to the driver context - * that was originally returned by ::init. - * @param device A specific device name (or other options) - * to create the microphone with. - * Each audio driver interprets this differently, - * and some may not honor it. - * @param rate The requested sampling rate of the new microphone. - * @param latency TODO - * @param block_frames TODO - * @param new_rate Pointer to the actual sample frequency, - * if the microphone couldn't be initialized with the value given by rate. - * If NULL, then the value will not be reported to the client; - * this is not an error. - * @return An opaque handle to the newly-initialized microphone - * if it was successfully created, - * or \c NULL if there was an error. - * May be \c NULL if no microphone is available, - * or if the maximum number of microphones has been created. - * The returned handle should be provided to the \c microphone_context - * parameter of all other microphone functions. - * - * @note Do not return \c NULL to indicate a lack of driver support; - * instead, don't implement this function. - * @note Additionally, don't check the settings for microphone support; - * the frontend's audio layer will do that for you. - */ - void *(*init_microphone)(void *data, const char *device, unsigned rate, - unsigned latency, unsigned block_frames, unsigned *new_rate); - - /** - * Releases the resources used by a particular microphone - * and stops its activity. - * Will do nothing if either \p data or \p microphone_context is \c NULL. - * - * @param data Opaque handle to the audio driver context - * that was used to create the provided microphone. - * Implementations may use this to help in cleaning up the microphone, - * but the driver context itself must \em not be released. - * @param microphone_context Opaque handle to the microphone that will be freed. - * Implementations should stop any recording activity before freeing resources. - * - * @post \p data will still be valid, - * while \p microphone_context will not. - */ - void (*free_microphone)(void *data, void *microphone_context); - - /** - * Queries the active state of a microphone. - * Use this to determine if a microphone is currently recording audio - * (i.e. if the mic is "hot"). - * @param data Opaque handle to the audio driver context - * that was used to create the provided microphone. - * @param microphone_context A handle to the microphone that will be freed. - * @return \c true if the provided microphone is actively recording audio. - * \c false if the provided microphone is idle, - * or if either parameter is \c NULL. - * - * @note Implementations should not modify - * the state of the driver or the microphone - * within this function. - */ - bool (*get_microphone_state)(const void *data, const void *microphone_context); - - /** - * @brief Enables or disables a microphone. - * Enabled microphones should record audio input with - * this driver's \c read_microphone implementation. - * Disabled microphones should not be processed, - * and should not impact overall application performance. - * @param data Opaque handle to the audio driver context - * that was used to create the provided microphone. - * @param microphone_context Opaque handle to the microphone - * whose state should be toggled. - * @param enabled The desired state of the provided microphone. - * Provide \c true to enable it and \c false to disable it. - * @return \c true if the microphone's state was successfully altered, - * or if it didn't need to be altered - * (i.e. when disabling an already-idle microphone). - * \c false if there was an error, - * or if the \c microphone_context was invalid. - */ - bool (*set_microphone_state)(void *data, void *microphone_context, bool enabled); - - /** - * @brief Read samples from the input driver, e.g. for microphones. - * - * Write data from the audio driver into the buffer. - * Microphone input is in mono, so a sample is equivalent to a frame - * for our purposes (I.e. 44.1kHz, 16-bit stereo has 44.1k samples/s). - * - * Each element of the provided array is a single sample. - * If the driver returns true in use_float(), a floating point - * format will be used, with range [-1.0, 1.0]. - * If not, signed 16-bit samples in native byte ordering will be used. - * - * - * This function returns the number of samples successfully read. - * If an error occurs, -1 should be returned. - * Note that non-blocking behavior that cannot read at this time - * should return 0 as returning -1 will terminate the driver. - * - * Unless said otherwise with set_nonblock_state(), all reads - * are blocking, and it should block till it has read all frames. - * - * @param[in] driver_context Opaque handle to the audio driver context - * that was used to create the provided microphone. - * @param[in] microphone_context Opaque handle to the microphone - * whose input will be received. - * @param[out] buf Buffer for received audio data. - * Should be large enough to hold one frame's worth of audio samples. - * @param[in] size Size of audio buffer, in bytes (\em not samples). - * Don't ask for more than you need per frame. - * @return The number of bytes that were read into \c buf, - * or -1 if there was an error. - */ - ssize_t (*read_microphone)(void *driver_context, void *microphone_context, void *buf, size_t size); } audio_driver_t; enum audio_driver_state_flags @@ -348,8 +174,7 @@ enum audio_driver_state_flags AUDIO_FLAG_SUSPENDED = (1 << 2), AUDIO_FLAG_MIXER_ACTIVE = (1 << 3), AUDIO_FLAG_HARD_DISABLE = (1 << 4), - AUDIO_FLAG_CONTROL = (1 << 5), - AUDIO_FLAG_MIC_ACTIVE = (1 << 6) + AUDIO_FLAG_CONTROL = (1 << 5) }; typedef struct @@ -368,8 +193,6 @@ typedef struct */ float *output_samples_buf; size_t output_samples_buf_length; - void *input_samples_buf; - size_t input_samples_buf_length; #ifdef HAVE_REWIND int16_t *rewind_buf; #endif @@ -380,8 +203,6 @@ typedef struct */ int16_t *output_samples_conv_buf; size_t output_samples_conv_buf_length; - int16_t *input_samples_conv_buf; - size_t input_samples_conv_buf_length; #ifdef HAVE_DSP_FILTER retro_dsp_filter_t *dsp; #endif @@ -394,13 +215,6 @@ typedef struct */ const audio_driver_t *current_audio; - /** - * The handle to the created microphone, if any. - * The libretro API is designed to expose multiple microphones, - * but RetroArch only supports one at a time for now. - * PRs welcome! - */ - retro_microphone_t current_microphone; void *context_audio_data; /** @@ -524,23 +338,6 @@ bool audio_driver_stop(void); */ unsigned audio_driver_get_sample_size(void); -/** - * - * @param driver The audio driver whose microphone support you're querying - * @return true if driver defines the necessary methods TODO - */ -bool audio_driver_supports_microphone(const audio_driver_t* driver); - -retro_microphone_t *audio_driver_init_microphone(void); - -void audio_driver_free_microphone(retro_microphone_t *microphone); - -bool audio_driver_set_microphone_state(retro_microphone_t *microphone, bool state); - -bool audio_driver_get_microphone_state(const retro_microphone_t *microphone); - -int audio_driver_get_microphone_input(retro_microphone_t *microphone, int16_t* frames, size_t num_frames); - #ifdef HAVE_TRANSLATE /* TODO/FIXME - Doesn't currently work. Fix this. */ bool audio_driver_is_ai_service_speech_running(void); diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c new file mode 100644 index 000000000000..165ba56bf4e1 --- /dev/null +++ b/microphone/microphone_driver.c @@ -0,0 +1,644 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include "microphone_driver.h" +#include "configuration.h" +#include "driver.h" +#include "verbosity.h" +#include "runloop.h" +#include "memalign.h" +#include "audio/conversion/s16_to_float.h" +#include "audio/conversion/float_to_s16.h" + +static microphone_driver_state_t mic_driver_st = {0}; /* double alignment */ + +microphone_driver_t microphone_null = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "null", + NULL, + NULL +}; + +microphone_driver_t *microphone_drivers[] = { +#ifdef HAVE_ALSA + µphone_alsa, +#if !defined(__QNX__) && !defined(MIYOO) && defined(HAVE_THREADS) + µphone_alsathread, +#endif +#endif +#ifdef HAVE_WASAPI + µphone_wasapi, +#endif +#ifdef HAVE_SDL2 + µphone_sdl, /* Microphones are not supported in SDL 1 */ +#endif + µphone_null, + NULL, +}; + +microphone_driver_state_t *microphone_state_get_ptr(void) +{ + return &mic_driver_st; +} + +unsigned mic_driver_get_sample_size(void) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + return (mic_st->flags & MICROPHONE_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t); +} + +bool microphone_driver_find_driver( + void *settings_data, + const char *prefix, + bool verbosity_enabled) +{ + settings_t *settings = (settings_t*)settings_data; + int i = (int)driver_find_index( + "microphone_driver", + settings->arrays.microphone_driver); + + if (i >= 0) + mic_driver_st.driver = (const microphone_driver_t *) + microphone_drivers[i]; + else + { + const microphone_driver_t *tmp = NULL; + if (verbosity_enabled) + { + unsigned d; + RARCH_ERR("Couldn't find any %s named \"%s\"\n", prefix, + settings->arrays.microphone_driver); + + RARCH_LOG_OUTPUT("Available %ss are:\n", prefix); + for (d = 0; microphone_drivers[d]; d++) + { + if (microphone_drivers[d]) + RARCH_LOG_OUTPUT("\t%s\n", microphone_drivers[d]->ident); + } + RARCH_WARN("Going to default to first %s...\n", prefix); + } + + tmp = (const microphone_driver_t *)microphone_drivers[0]; + + if (!tmp) + return false; + mic_driver_st.driver = tmp; + } + + return true; +} + +static void mic_driver_microphone_handle_init(retro_microphone_t *microphone) +{ + if (microphone) + { + microphone->microphone_context = NULL; + microphone->pending_enabled = false; + microphone->active = true; + microphone->sample_buffer = NULL; + microphone->sample_buffer_length = 0; + microphone->most_recent_copy_length = 0; + microphone->error = false; + } +} + +static void mic_driver_microphone_handle_free(retro_microphone_t *microphone) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + const microphone_driver_t *mic_driver = mic_st->driver; + void *driver_context = mic_st->driver_context; + + if (!microphone) + return; + + if (!driver_context) + RARCH_WARN("[Microphone]: Attempted to free a microphone without an active driver context\n"); + + if (microphone->microphone_context) + { + mic_driver->close_mic(driver_context, microphone->microphone_context); + microphone->microphone_context = NULL; + } + + if (microphone->sample_buffer) + { + memalign_free(microphone->sample_buffer); + microphone->sample_buffer = NULL; + } + + memset(microphone, 0, sizeof(*microphone)); + /* Do NOT free the microphone handle itself! It's allocated statically! */ +} + +static void mic_driver_open_mic_internal(retro_microphone_t* microphone); + +bool microphone_driver_init_internal(void *settings_data) +{ + float *in_samples_buf = NULL; + settings_t *settings = (settings_t*)settings_data; + size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2; + bool audio_enable_microphone = settings->bools.audio_enable_input; + bool audio_sync = settings->bools.audio_sync; + bool audio_rate_control = settings->bools.audio_rate_control; + float slowmotion_ratio = settings->floats.slowmotion_ratio; + size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; + int16_t *in_conv_buf = (int16_t*)memalign_alloc(64, insamples_max * sizeof(int16_t)); + size_t audio_buf_length = AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float); + bool verbosity_enabled = verbosity_is_enabled(); + + convert_s16_to_float_init_simd(); + convert_float_to_s16_init_simd(); + + if (!in_conv_buf) + goto error; + + mic_driver_st.input_samples_conv_buf = in_conv_buf; + mic_driver_st.input_samples_conv_buf_length = insamples_max; + + if (!audio_enable_microphone) + { + mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + return false; + } + + if (!(microphone_driver_find_driver(settings, + "microphone driver", verbosity_enabled))) + { + RARCH_ERR("[Microphone]: Failed to initialize microphone driver. Will continue without mic input.\n"); + goto error; + } + + if (!mic_driver_st.driver || !mic_driver_st.driver->init) + { + goto error; + } + + + mic_driver_st.driver_context = mic_driver_st.driver->init(); + + + if (!mic_driver_st.driver_context || !in_samples_buf) + { + goto error; + } + + RARCH_LOG("[Microphone]: Started synchronous microphone driver\n"); + + mic_driver_st.input_samples_buf = (float*)in_samples_buf; + mic_driver_st.input_samples_buf_length = insamples_max * sizeof(float); + mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_CONTROL; + + if ( mic_driver_st.microphone.active && + !mic_driver_st.microphone.microphone_context) + { /* If the core requested a microphone before the driver was able to provide one...*/ + /* Now that the driver and driver context are ready, let's initialize the mid */ + mic_driver_open_mic_internal(&mic_driver_st.microphone); + + if (mic_driver_st.microphone.microphone_context) + RARCH_DBG("[Microphone]: Initialized a previously-pending microphone\n"); + else + { + RARCH_ERR("[Microphone]: Failed to initialize a previously pending microphone; microphone will not be used\n"); + + microphone_driver_close_mic(&mic_driver_st.microphone); + } + + } + + return true; + +error: + RARCH_ERR("[Microphone]: Failed to initialize microphone driver. Will continue without audio input.\n"); + mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + return microphone_driver_deinit(); +} + +/** + * + * @param microphone Handle to the microphone to init with a context + * Check the value of microphone->microphone_context to see if this function succeeded + */ +static void mic_driver_open_mic_internal(retro_microphone_t* microphone) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + settings_t *settings = config_get_ptr(); + const microphone_driver_t *mic_driver = mic_st->driver; + void *driver_context = mic_st->driver_context; + unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; + unsigned setting_audio_latency = settings->uints.audio_input_latency; + unsigned actual_sample_rate = 0; + unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? + runloop_audio_latency : setting_audio_latency; + float slowmotion_ratio = settings->floats.slowmotion_ratio; + size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; + + if (!microphone || !mic_driver) + return; + + microphone->sample_buffer_length = insamples_max * sizeof(int16_t); + microphone->error = false; + microphone->sample_buffer = + (int16_t*)memalign_alloc(64, microphone->sample_buffer_length); + + if (!microphone->sample_buffer) + goto error; + + microphone->microphone_context = mic_driver->open_mic(driver_context, + *settings->arrays.audio_input_device ? settings->arrays.audio_input_device : NULL, + settings->uints.audio_input_sample_rate, + audio_latency, + settings->uints.audio_input_block_frames, + &actual_sample_rate); + + if (!microphone->microphone_context) + goto error; + + microphone_driver_set_mic_state(microphone, microphone->pending_enabled); + + if (actual_sample_rate != 0) + { + RARCH_LOG("[Audio]: Requested microphone sample rate of %uHz, got %uHz. Updating settings with this value.\n", + settings->uints.audio_input_sample_rate, + actual_sample_rate + ); + configuration_set_uint(settings, settings->uints.audio_input_sample_rate, actual_sample_rate); + } + + RARCH_LOG("[Audio]: Initialized microphone\n", actual_sample_rate); + return; +error: + mic_driver_microphone_handle_free(microphone); + RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); +} + +void microphone_driver_close_mic(retro_microphone_t *microphone) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + const microphone_driver_t *mic_driver = mic_st->driver; + void *driver_context = mic_st->driver_context; + + if ( microphone && + driver_context && + mic_driver && + mic_driver->close_mic) + { + mic_driver_microphone_handle_free(microphone); + } +} + +bool microphone_driver_set_mic_state(retro_microphone_t *microphone, bool state) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + const microphone_driver_t *mic_driver = mic_st->driver; + void *driver_context = mic_st->driver_context; + + if (!microphone || !microphone->active || !mic_driver || !mic_driver->set_mic_active) + return false; + + if (driver_context && microphone->microphone_context) + { /* If the driver is initialized... */ + bool success = mic_driver->set_mic_active(driver_context, microphone->microphone_context, state); + + if (success) + RARCH_DBG("[Microphone]: Set initialized mic state to %s\n", + state ? "enabled" : "disabled"); + else + RARCH_ERR("[Microphone]: Failed to set initialized mic state to %s\n", + state ? "enabled" : "disabled"); + + return success; + } + else + { /* The driver's not ready yet, so we'll make a note + * of what the mic's state should be */ + microphone->pending_enabled = state; + RARCH_DBG("[Microphone]: Set pending mic state to %s\n", + state ? "enabled" : "disabled"); + return true; + /* This isn't an error */ + } +} + +bool microphone_driver_get_mic_state(const retro_microphone_t *microphone) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + const microphone_driver_t *mic_driver = mic_st->driver; + void *driver_context = mic_st->driver_context; + + if (!microphone || !microphone->active || !mic_driver || !mic_driver->get_mic_active) + return false; + + if (driver_context && microphone->microphone_context) + { /* If the driver is initialized... */ + return mic_driver->get_mic_active(driver_context, microphone->microphone_context); + } + else + { /* The driver's not ready yet, + * so we'll use that note we made of the mic's active state */ + return microphone->pending_enabled; + } +} + +/** + * Pull queued microphone samples from the driver + * and copy them to the provided buffer(s). + * + * Only one mic is supported right now, + * but the API is designed to accommodate multiple. + * If multi-mic support is implemented, + * you'll want to update this function. + * + * Note that microphone samples are provided in mono, + * so a "sample" and a "frame" are equivalent here. + * + * @param mic_st The overall state of the audio driver. + * @param slowmotion_ratio TODO + * @param audio_fastforward_mute True if no audio should be input while the game is in fast-forward. + * @param[out] frames The buffer in which the core will receive microphone samples. + * @param num_frames The size of \c frames, in samples. + * @param is_slowmotion True if the player is running the core in slow-motion. + * @param is_fastmotion True if the player is running the core in fast-forward. + * + * @see audio_driver_flush() + */ +static void audio_driver_flush_microphone_input( + microphone_driver_state_t *mic_st, + retro_microphone_t *microphone, + float slowmotion_ratio, + bool audio_fastforward_mute, + int16_t *frames, size_t num_frames, + bool is_slowmotion, bool is_fastmotion) +{ + if (mic_st && /* If the driver state is valid... */ + (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) && /* ...and mic support is on... */ + mic_st->driver && + mic_st->driver_context && /* ...and the mic driver is initialized... */ + mic_st->input_samples_buf && /* ...with scratch space... */ + microphone && + microphone->active && /* ...and the mic itself is initialized... */ + microphone->microphone_context && /* ...and ready... */ + microphone->sample_buffer && + microphone->sample_buffer_length && /* ...with a non-empty sample buffer... */ + + mic_st->driver->read && /* ...and valid function pointers... */ + mic_st->driver->get_mic_active && + mic_st->driver->get_mic_active( /* ...and it's enabled... */ + mic_st->driver_context, + microphone->microphone_context)) + { + void *buffer_source = NULL; + unsigned sample_size = mic_driver_get_sample_size(); + size_t bytes_to_read = MIN(mic_st->input_samples_buf_length, num_frames * sample_size); + ssize_t bytes_read = mic_st->driver->read( + mic_st->driver_context, + microphone->microphone_context, + mic_st->input_samples_buf, + bytes_to_read); + /* First, get the most recent mic data */ + + if (bytes_read < 0) + { /* If there was an error... */ + microphone->error = true; + return; + } + else if (bytes_read == 0) + { + microphone->error = false; + return; + } + + if (mic_st->flags & MICROPHONE_FLAG_USE_FLOAT) + { + convert_float_to_s16(mic_st->input_samples_conv_buf, mic_st->input_samples_buf, bytes_read / sample_size); + buffer_source = mic_st->input_samples_conv_buf; + } + else + { + buffer_source = mic_st->input_samples_buf; + } + + memcpy(frames, buffer_source, num_frames * sizeof(int16_t)); + } +} + + +int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size_t num_frames) +{ + uint32_t runloop_flags; + size_t frames_remaining = num_frames; + microphone_driver_state_t *mic_st = &mic_driver_st; + + if (mic_st->flags & MICROPHONE_DRIVER_FLAG_SUSPENDED + || num_frames == 0 + || !frames + || !microphone + || !microphone->active + || !microphone_driver_get_mic_state(microphone) + ) + { /* If the driver is suspended, or the core didn't actually ask for frames, + * or the microphone is disabled... */ + return -1; + } + + if (!(mic_st->driver_context && microphone->microphone_context)) + { /* If the microphone isn't ready... */ + return 0; /* Not an error */ + } + + runloop_flags = runloop_get_flags(); + + do + { + size_t frames_to_read = + (frames_remaining > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)) ? + (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1) : frames_remaining; + + if (!( (runloop_flags & RUNLOOP_FLAG_PAUSED) + || !(mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) + || !(mic_st->input_samples_buf))) + /* If the game is running, the audio driver and mic are running, + * and the input sample buffer is valid... */ + audio_driver_flush_microphone_input(mic_st, + microphone, + config_get_ptr()->floats.slowmotion_ratio, + config_get_ptr()->bools.audio_fastforward_mute, + frames, + frames_to_read, + runloop_flags & RUNLOOP_FLAG_SLOWMOTION, + runloop_flags & RUNLOOP_FLAG_FASTMOTION); + frames_remaining -= frames_to_read; + frames += frames_to_read << 1; + } + while (frames_remaining > 0); + + return num_frames; +} + +/* NOTE: The core may request a microphone before the driver is ready. + * A pending handle will be provided in that case, and the frontend will + * initialize the microphone when the time is right; + * do not call this function twice on the same mic. */ +retro_microphone_t *microphone_driver_open_mic(void) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + const settings_t *settings = config_get_ptr(); + const microphone_driver_t *mic_driver = mic_st->driver; + void *driver_context = mic_st->driver_context; + + if (!settings) + { + RARCH_ERR("[Microphone]: Failed to open microphone due to uninitialized config\n"); + return NULL; + } + + if (!mic_driver) + { + RARCH_ERR("[Microphone]: Failed to initialize microphone due to uninitialized driver\n"); + return NULL; + } + + if (!settings->bools.audio_enable_input) + { /* Not checking mic_st->flags because they might not be set yet; + * don't forget, the core can ask for a mic + * before the audio driver is ready to create one. */ + RARCH_WARN("[Microphone]: Refused to initialize microphone because it's disabled in the settings\n"); + return NULL; + } + + if (mic_st->microphone.active) + { /* If the core has requested a second microphone... */ + RARCH_ERR("[Microphone]: Failed to initialize a second microphone, frontend only supports one at a time right now\n"); + if (mic_st->microphone.microphone_context) + /* If that mic is initialized... */ + RARCH_ERR("[Microphone]: An initialized microphone exists\n"); + else + /* That mic is pending */ + RARCH_ERR("[Microphone]: A microphone is pending initialization\n"); + + return NULL; + } + + /* Cores might ask for a microphone before the audio driver is ready to provide them; + * if that happens, we have to initialize the microphones later. + * But the user still wants a handle, so we'll give them one. + */ + mic_driver_microphone_handle_init(&mic_st->microphone); + /* If context is NULL, the handle won't have a valid microphone context (but we'll create one later) */ + + if (driver_context) + { /* If the microphone driver is ready to open a microphone... */ + mic_driver_open_mic_internal(&mic_st->microphone); + + if (mic_st->microphone.microphone_context) /* If the microphone was successfully initialized... */ + RARCH_LOG("[Audio]: Initialized the requested microphone successfully\n"); + else + goto error; + } + else + { /* If the audio driver isn't ready to create a microphone... */ + RARCH_LOG("[Audio]: Microphone requested before audio context was ready; deferring initialization\n"); + } + + return &mic_st->microphone; +error: + mic_driver_microphone_handle_free(&mic_st->microphone); + + return NULL; +} + +/* TODO: When adding support for multiple microphones, + * make sure you clean them all up in here. */ +static bool microphone_driver_close_microphones(void) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + const microphone_driver_t *mic_driver = mic_st->driver; + + if ( !mic_driver || + !mic_driver->close_mic || + !mic_st->driver_context || + !mic_st->microphone.active) + return false; + + microphone_driver_close_mic(&mic_st->microphone); + + return true; +} + +static bool microphone_driver_free_devices_list(void) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + if ( + !mic_st->driver + || !mic_st->driver->device_list_free + || !mic_st->driver_context + || !mic_st->devices_list) + return false; + + mic_st->driver->device_list_free(mic_st->driver_context, mic_st->devices_list); + mic_st->devices_list = NULL; + return true; +} + +static void report_microphone_buffer_statistics(void); +static bool microphone_driver_deinit_internal(bool microphone_enable) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + if (mic_st->driver + && mic_st->driver->free) + { + if (mic_st->driver_context) + mic_st->driver->free(mic_st->driver_context); + mic_st->driver_context = NULL; + } + + if (mic_st->input_samples_conv_buf) + memalign_free(mic_st->input_samples_conv_buf); + mic_st->input_samples_conv_buf = NULL; + + if (!microphone_enable) + { + mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + return false; + } + + if (mic_st->input_samples_buf) + memalign_free(mic_st->input_samples_buf); + mic_st->input_samples_buf = NULL; + +#ifdef DEBUG + report_microphone_buffer_statistics(); +#endif + + return true; +} + +bool microphone_driver_deinit(void) +{ + settings_t *settings = config_get_ptr(); + microphone_driver_free_devices_list(); + microphone_driver_close_microphones(); + return microphone_driver_deinit_internal( + settings->bools.audio_enable_input); +} \ No newline at end of file diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h new file mode 100644 index 000000000000..91aded0fe237 --- /dev/null +++ b/microphone/microphone_driver.h @@ -0,0 +1,505 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef RETROARCH_MICROPHONE_DRIVER_H +#define RETROARCH_MICROPHONE_DRIVER_H + +#include +#include +#include +#include +#include "audio/audio_defines.h" + +#define MAX_SUPPORTED_MICROPHONES 8 +#define MICROPHONE_BUFFER_FREE_SAMPLES_COUNT (8 * 1024) + +enum microphone_driver_state_flags +{ + MICROPHONE_DRIVER_FLAG_ACTIVE = (1 << 0), + MICROPHONE_FLAG_USE_FLOAT = (1 << 1), + MICROPHONE_DRIVER_FLAG_SUSPENDED = (1 << 2), + MICROPHONE_FLAG_HARD_DISABLE = (1 << 3), + MICROPHONE_DRIVER_FLAG_CONTROL = (1 << 4), +}; + +/** + * Driver object that tracks a microphone's state. + * Pointers to this object are provided to cores + * for use as opaque handles by \c retro_microphone_interface_t. + */ +struct retro_microphone +{ + /** + * Pointer to the context object created by the underlying driver. + * It will contain data that's specific to each driver, + * such as device IDs or sample queues. + */ + void *microphone_context; + + /** + * Pointer to the data that will be copied to cores. + */ + int16_t* sample_buffer; + + /** + * Length of \c sample_buffer in bytes, \em not samples. + */ + size_t sample_buffer_length; + + /** + * Number of bytes that were copied into the buffer in the most recent flush. + * Accounts for resampling + **/ + size_t most_recent_copy_length; + + /* May be enabled even before the driver is ready */ + bool pending_enabled; + + /** + * True if this object represents a valid or pending microphone. + * Mostly exists because retro_microphone is statically allocated, + * so there's no reason to check it against NULL. + */ + bool active; + + bool error; +}; + +/** + * Defines the implementation of a microphone driver. + * Similar to audio_driver_t. + * All functions are mandatory unless otherwise noted. + */ +typedef struct microphone_driver +{ + /** + * Initializes the microphone driver. + * This function should not open any actual microphones; + * instead, it should set up any prerequisites necessary + * to create a microphone. + * + * After this function is called, + * microphones can be opened with \c open_mic. + * + * @returns A handle to the general microphone driver context, + * or \c NULL if there was an error. + **/ + void *(*init)(void); + + + /** + * Frees the driver context and closes all microphones. + * There is no need to call close_mic after calling this function. + * Does nothing if \c driver_context is \c NULL. + * + * @param driver_context Pointer to the microphone driver context. + * Provide the pointer that was returned by \c ::init(), + * \em not one of the handles returned by \c ::mic_open(). + * + * @post The provided driver context is invalid, + * and all microphones are closed. + */ + void (*free)(void *driver_context); + + /** + * Read samples from the microphone into the provided buffer. + * + * Samples are provided in mono format, + * in either signed 16-bit integer or 32-bit floating point. + * Since samples are in mono, a "frame" and a "sample" mean the same thing + * in the context of microphone input. + * + * If \c use_float() returns \c true, + * samples will be in 32-bit \c float format with a range of [-1.0, 1.0]. + * format will be used, with range [-1.0, 1.0]. + * Otherwise, samples will be in signed 16-bit integer format. + * Data will be in native byte order either way. + * + * All reads should block until all requested frames are provided, + * unless set otherwise wioth set_nonblock_state(). + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] mic_context Pointer to the microphone context. + * Will be a value that was returned by \c ::open_mic(). + * @param[out] buffer Pointer to the buffer in which the read samples should be stored. + * @param buffer_size The available length of \c buffer in bytes, + * \em not samples or frames. + * @return The number of bytes that were successfully read, + * or \c -1 if there was an error. + * May be less than \c buffer_size if this microphone is non-blocking. + * If this microphone is in non-blocking mode and no new data is available, + * the driver should return 0 rather than -1. + */ + ssize_t (*read)(void *driver_context, void *mic_context, void *buffer, size_t buffer_size); + + /** + * Temporarily pauses all microphones. + * The return value of \c get_mic_state will not be updated, + * but all microphones will stop recording until \c start is called. + * + * @param driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @return \c true if microphone driver was successfully paused, + * \c false if there was an error. + **/ + bool (*stop)(void *driver_context); + + /** + * Resumes audio driver from the paused state. + * Microphones will resume recording \em if they were already active + * before the driver was stopped. + **/ + bool (*start)(void *data, bool is_shutdown); + + + /** + * Queries whether the driver is active. + * This only queries the overall driver state, + * not the state of individual microphones. + * + * @param driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @return \c true if the driver is active. + * \c false if not or if there was an error. + */ + bool (*alive)(const void *driver_context); + + /** + * Toggles the active state of the provided microphone. + * TODO mics are inactive when created + * TODO intended for cores that provide a mic button + * TODO paused driver won't resume, but will determine if mic resumes when unpaused + * TODO active mics retrieve samples + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] mic_context Pointer to a particular microphone's context. + * Will be a value that was returned by \c ::open_mic(). + * @param[in] active \c true if the provided microphone should be activated, + * false if it should be paused. + * @return \c true if the active state was updated, + * \c false if there was an error, + */ + bool (*set_mic_active)(void *driver_context, void *mic_context, bool active); + + /** + * Note that this describes the user-facing state of the microphone; + * this function can still return \c true if the mic driver is paused + * or muted. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] mic_context Pointer to a particular microphone's context. + * Will be a value that was returned by \c ::open_mic(). + * @return \c true if the provided microphone is active, + * \c false if not or if there was an error. + */ + bool (*get_mic_active)(const void *driver_context, const void *mic_context); + + /** + * Sets the nonblocking state of all microphones. + * Primarily used for fast-forwarding. + * If the driver is in blocking mode (the default), + * \c ::read() will block the current thread + * until all requested samples are provided. + * Otherwise, \c ::read() will return as many samples as it can (which may be none) + * and return without waiting. + * + * If a driver does not support nonblocking mode, + * leave this function pointer as \c NULL. + * + * @param driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] nonblock \c true if the driver should be in nonblocking mode, + * \c false if it should be in blocking mode. + * */ + void (*set_nonblock_state)(void *driver_context, bool nonblock); + + + /** + * @param driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param mic_context Pointer to a microphone context. + * Will be a value that was returned by \c ::open_mic(). + * @return True if this mic provides data in \c float format, + * false if it uses \c int16_t samples. + * + * @note Drivers should use \c int16_t samples if possible. + * Since the microphone driver does not resample input, + * integer samples can be provided directly to the core. + */ + bool (*use_float)(const void *driver_context, const void *mic_context); + + /** + * A human-readable name for this driver. + * Shown to the user in the driver selection menu. + */ + const char *ident; + + /** + * Returns a list of all microphones (aka "capture devices") + * that are currently available. + * The user can select from these devices on the options menu. + * + * Optional, but must be implemented if \c device_list_free is implemented. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @return Pointer to a list of available capture devices, + * an empty list if no capture devices are available, + * or \c NULL if there was an error. + **/ + struct string_list *(*device_list_new)(const void *driver_context); + + /** + * Frees the microphone list that was returned by \c device_list_new. + * Optional, but must be provided if \c device_list_new is implemented. + * Will do nothing if any parameter is \c NULL. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c init(). + * @param[in] devices Pointer to the device list + * that was returned by \c device_list_new. + */ + void (*device_list_free)(const void *driver_context, struct string_list *devices); + + /** + * The available space of the provided microphone's buffer, + * in bytes (\em not samples). + * Note that this function does *not* return the number of + * samples that can be read; + * Used to control the rate at which samples are read + * to avoid buffer overruns or underruns. + * + * Optional, but must be provided if \c buffer_size is implemented. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c init(). + * @param mic_context Pointer to a microphone context. + * Will be a value that was returned by \c ::open_mic(). + * @returns The number of bytes that are available from the + **/ + size_t (*read_avail)(const void *driver_context, const void *mic_context); + + /** + * + * @param mic_context + * @return + */ + size_t (*buffer_size)(const void *driver_context, const void *mic_context); + + /** + * Initializes a microphone using the audio driver. + * Cores that use microphone functionality will call this via + * retro_microphone_interface::init_microphone. + * + * The core may request a microphone before the driver is fully initialized, + * but driver implementations do not need to concern themselves with that; + * when the driver is ready, it will call this function. + * + * @param data Handle to the driver context + * that was originally returned by ::init. + * @param device A specific device name (or other options) + * to create the microphone with. + * Each microphone driver interprets this differently, + * and some may ignore it. + * @param rate The requested sampling rate of the new microphone, + * in Hz. + * @param latency TODO + * @param block_frames TODO + * @param new_rate Pointer to the actual sample frequency, + * if the microphone couldn't be initialized with the value given by rate. + * If NULL, then the value will not be reported to the caller; + * this is not an error. + * @return An opaque handle to the newly-initialized microphone + * if it was successfully created, + * or \c NULL if there was an error. + * May be \c NULL if no microphone is available, + * or if the maximum number of microphones has been created. + * The returned handle should be provided to the \c microphone_context + * parameter of all other microphone functions. + * + * @note Your driver should keep track of the mic context + */ + void *(*open_mic)(void *driver_context, const char *device, unsigned rate, + unsigned latency, unsigned block_frames, unsigned *new_rate); + + /** + * Releases the resources used by a particular microphone + * and stops its activity. + * Will do nothing if either \p data or \p microphone_context is \c NULL. + * + * @param data Opaque handle to the audio driver context + * that was used to create the provided microphone. + * Implementations may use this to help in cleaning up the microphone, + * but the driver context itself must \em not be released. + * @param microphone_context Opaque handle to the microphone that will be freed. + * Implementations should stop any recording activity before freeing resources. + * + * @post \p data will still be valid, + * while \p microphone_context will not. + */ + void (*close_mic)(void *driver_context, void *microphone_context); +} microphone_driver_t; + +typedef struct +{ + struct string_list *devices_list; + + /** + * A scratch buffer for audio output to be processed, + * up to (but excluding) the point where it's converted to 16-bit audio + * to give to the driver. + */ + void *input_samples_buf; + size_t input_samples_buf_length; + + /** + * A scratch buffer for processed audio output to be converted to 16-bit, + * so that it can be sent to the driver. + */ + int16_t *input_samples_conv_buf; + size_t input_samples_conv_buf_length; + + /** + * The current microphone driver. + */ + const microphone_driver_t *driver; + + /** + * The handle to the created microphone, if any. + * The libretro API is designed to expose multiple microphones, + * but RetroArch only supports one at a time for now. + * PRs welcome! + */ + retro_microphone_t microphone; + + /** + * Opaque handle to the driver-specific context. + */ + void *driver_context; + + + enum microphone_driver_state_flags flags; + + /** + * If \c true, all microphones return silence. + */ + bool mute_enable; +} microphone_driver_state_t; + +/** + * TODO + * @param is_shutdown + * @return + */ +bool microphone_driver_start(bool is_shutdown); + +/** + * TODO + * @return + */ +bool microphone_driver_stop(void); + +/** + * Driver function for opening a microphone. + * Provided to retro_microphone_interface::init_microphone(). + * @return TODO + */ +retro_microphone_t *microphone_driver_open_mic(void); + +/** + * TODO + */ +void microphone_driver_close_mic(retro_microphone_t *microphone); + +/** + * TODO + */ +bool microphone_driver_set_mic_state(retro_microphone_t *microphone, bool state); + +/** + * TODO + * @param microphone + * @return + */ +bool microphone_driver_get_mic_state(const retro_microphone_t *microphone); + +/** + * TODO + * @param microphone + * @param frames + * @param num_frames + * @return + */ +int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size_t num_frames); + +/** + * TODO + */ +extern microphone_driver_t microphone_alsa; + +/** + * TODO + */ +extern microphone_driver_t microphone_alsathread; + +/** + * TODO + */ +extern microphone_driver_t microphone_sdl; + +/** + * TODO + */ +extern microphone_driver_t microphone_wasapi; + +/** + * TODO + * @return + */ +microphone_driver_state_t *microphone_state_get_ptr(void); + +/** + * TODO + */ +extern microphone_driver_t *microphone_drivers[]; + + +/** + * TODO + * @param stats + * @return + */ +bool microphone_compute_buffer_statistics(audio_statistics_t *stats); + +float microphone_driver_monitor_adjust_system_rates( + double input_sample_rate, + double input_fps, + float video_refresh_rate, + unsigned video_swap_interval, + float audio_max_timing_skew); + +bool microphone_driver_init_internal(void *settings_data); + +bool microphone_driver_deinit(void); + +bool microphone_driver_find_driver( + void *settings_data, + const char *prefix, + bool verbosity_enabled); + +#endif /* RETROARCH_MICROPHONE_DRIVER_H */ \ No newline at end of file diff --git a/runloop.c b/runloop.c index 873ec0c428d9..8d67cb6d910b 100644 --- a/runloop.c +++ b/runloop.c @@ -159,6 +159,7 @@ #include "input/input_keymaps.h" #include "input/input_remapping.h" +#include "microphone/microphone_driver.h" #ifdef HAVE_CHEEVOS #include "cheevos/cheevos.h" @@ -3332,11 +3333,11 @@ bool runloop_environment_cb(unsigned cmd, void *data) case RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE: { struct retro_microphone_interface* microphone = (struct retro_microphone_interface *)data; - const audio_driver_t *audio_driver = audio_state_get_ptr()->current_audio; + const microphone_driver_t *mic_driver = microphone_state_get_ptr()->driver; RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE.\n"); - if (!audio_driver) + if (!mic_driver) { RARCH_DBG("[Environ]: Couldn't initialize microphone interface, driver is not initialized\n"); return false; @@ -3349,14 +3350,14 @@ bool runloop_environment_cb(unsigned cmd, void *data) if (!settings->bools.audio_enable_input) return false; - if (audio_driver_supports_microphone(audio_driver)) + if (mic_driver->init) { microphone->supported = true; - microphone->init_microphone = audio_driver_init_microphone; - microphone->free_microphone = audio_driver_free_microphone; - microphone->set_microphone_state = audio_driver_set_microphone_state; - microphone->get_microphone_state = audio_driver_get_microphone_state; - microphone->get_microphone_input = audio_driver_get_microphone_input; + microphone->init_microphone = microphone_driver_open_mic; + microphone->free_microphone = microphone_driver_close_mic; + microphone->set_microphone_state = microphone_driver_set_mic_state; + microphone->get_microphone_state = microphone_driver_get_mic_state; + microphone->get_microphone_input = microphone_driver_read; } else { From 84ff6d98ff15b56152028033308749783a63f807 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 11:33:31 -0500 Subject: [PATCH 119/309] Initialize and deinitialize the microphone driver --- configuration.h | 1 + driver.h | 6 ++++-- retroarch.c | 20 ++++++++++++++++---- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/configuration.h b/configuration.h index e46cd0060284..2cbd3ebed7bf 100644 --- a/configuration.h +++ b/configuration.h @@ -429,6 +429,7 @@ typedef struct settings char cheevos_custom_host[64]; char video_context_driver[32]; char audio_driver[32]; + char microphone_driver[32]; char audio_resampler[32]; char input_driver[32]; char input_joypad_driver[32]; diff --git a/driver.h b/driver.h index 8eef1776a775..7be5e4674acf 100644 --- a/driver.h +++ b/driver.h @@ -41,7 +41,8 @@ enum DRIVER_BLUETOOTH, DRIVER_WIFI, DRIVER_LED, - DRIVER_MIDI + DRIVER_MIDI, + DRIVER_MICROPHONE }; enum @@ -56,7 +57,8 @@ enum DRIVER_BLUETOOTH_MASK = 1 << DRIVER_BLUETOOTH, DRIVER_WIFI_MASK = 1 << DRIVER_WIFI, DRIVER_LED_MASK = 1 << DRIVER_LED, - DRIVER_MIDI_MASK = 1 << DRIVER_MIDI + DRIVER_MIDI_MASK = 1 << DRIVER_MIDI, + DRIVER_MICROPHONE_MASK = 1 << DRIVER_MICROPHONE }; enum driver_ctl_state diff --git a/retroarch.c b/retroarch.c index dd15236af10c..8ef4bbbec62b 100644 --- a/retroarch.c +++ b/retroarch.c @@ -121,6 +121,7 @@ #include "camera/camera_driver.h" #include "location_driver.h" #include "record/record_driver.h" +#include "microphone/microphone_driver.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -717,10 +718,11 @@ void drivers_init( int flags, bool verbosity_enabled) { - runloop_state_t *runloop_st = runloop_state_get_ptr(); - audio_driver_state_t *audio_st = audio_state_get_ptr(); - input_driver_state_t *input_st = input_state_get_ptr(); - video_driver_state_t *video_st = video_state_get_ptr(); + runloop_state_t *runloop_st = runloop_state_get_ptr(); + audio_driver_state_t *audio_st = audio_state_get_ptr(); + input_driver_state_t *input_st = input_state_get_ptr(); + video_driver_state_t *video_st = video_state_get_ptr(); + microphone_driver_state_t *mic_st = microphone_state_get_ptr(); #ifdef HAVE_MENU struct menu_state *menu_st = menu_state_get_ptr(); #endif @@ -790,6 +792,13 @@ void drivers_init( audio_st->context_audio_data); } + if (flags & DRIVER_MICROPHONE_MASK) + { + microphone_driver_init_internal(settings); + if (mic_st->driver && mic_st->driver->device_list_new && mic_st->driver_context) + mic_st->devices_list = mic_st->driver->device_list_new(mic_st->driver_context); + } + /* Regular display refresh rate startup autoswitch based on content av_info */ if (flags & (DRIVER_VIDEO_MASK | DRIVER_AUDIO_MASK)) { @@ -1054,6 +1063,9 @@ void driver_uninit(int flags) if ((flags & DRIVER_AUDIO_MASK)) audio_state_get_ptr()->context_audio_data = NULL; + if (flags & DRIVER_MICROPHONE_MASK) + microphone_driver_deinit(); + if (flags & DRIVER_MIDI_MASK) midi_driver_free(); From d8e4b7464a8f41f49789dc99ecb3f487f813b70c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 11:34:25 -0500 Subject: [PATCH 120/309] Implement sdl_microphone.c --- Makefile.common | 4 +- microphone/drivers/sdl_microphone.c | 464 ++++++++++++++++++++++++++++ 2 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 microphone/drivers/sdl_microphone.c diff --git a/Makefile.common b/Makefile.common index fdf38c6d86bf..032645c56e4b 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1607,7 +1607,9 @@ endif ifeq ($(HAVE_SDL2), 1) HAVE_SDL_COMMON = 1 OBJ += gfx/drivers/sdl2_gfx.o \ - gfx/common/sdl2_common.o + gfx/common/sdl2_common.o \ + microphone/drivers/sdl_microphone.o + # Microphones are not supported by SDL 1.x DEF_FLAGS += $(SDL2_CFLAGS) LIBS += $(SDL2_LIBS) else ifeq ($(HAVE_SDL), 1) diff --git a/microphone/drivers/sdl_microphone.c b/microphone/drivers/sdl_microphone.c new file mode 100644 index 000000000000..0516cd533235 --- /dev/null +++ b/microphone/drivers/sdl_microphone.c @@ -0,0 +1,464 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "SDL.h" +#include "SDL_audio.h" +#include "verbosity.h" +#include "retro_assert.h" +#include "retro_math.h" +#include "microphone/microphone_driver.h" +#include +#include + +typedef struct sdl_microphone_handle +{ +#ifdef HAVE_THREADS + slock_t *lock; + scond_t *cond; +#endif + + /** + * The queue used to store incoming samples from the driver. + */ + fifo_buffer_t *sample_buffer; + bool is_paused; + SDL_AudioDeviceID device_id; +} sdl_microphone_handle_t; + +typedef struct sdl_microphone +{ +#ifdef HAVE_THREADS + slock_t *lock; + scond_t *cond; +#endif + + bool nonblock; + bool is_paused; + + /* Only one microphone is supported right now; + * the driver state should track multiple microphone handles, + * but the driver *context* should track multiple microphone contexts */ + sdl_microphone_handle_t *microphone; +} sdl_microphone_t; + +static INLINE int find_num_frames(int rate, int latency) +{ + int frames = (rate * latency) / 1000; + + /* SDL only likes 2^n sized buffers. */ + + return next_pow2(frames); +} + +static void *sdl_microphone_init(void) +{ + sdl_microphone_t *sdl = NULL; + uint32_t sdl_subsystem_flags = SDL_WasInit(0); + + /* Initialise audio subsystem, if required */ + if (sdl_subsystem_flags == 0) + { + if (SDL_Init(SDL_INIT_AUDIO) < 0) + return NULL; + } + else if ((sdl_subsystem_flags & SDL_INIT_AUDIO) == 0) + { + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) + return NULL; + } + + sdl = (sdl_microphone_t*)calloc(1, sizeof(*sdl)); + if (!sdl) + return NULL; + +#ifdef HAVE_THREADS + sdl->lock = slock_new(); + sdl->cond = scond_new(); + if (!sdl->lock || !sdl->cond) + { + goto error; + } +#endif + + return sdl; + +error: +#ifdef HAVE_THREADS + if (sdl) + { + slock_free(sdl->lock); + scond_free(sdl->cond); + } +#endif + free(sdl); + return NULL; +} + +static void sdl_microphone_close_mic(void *data, void *microphone_context); + +static void sdl_microphone_free(void *data) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + + if (sdl) + { + if (sdl->microphone) + sdl_microphone_close_mic(sdl, sdl->microphone); + +#ifdef HAVE_THREADS + slock_free(sdl->lock); + scond_free(sdl->cond); +#endif + + SDL_QuitSubSystem(SDL_INIT_AUDIO); + } + free(sdl); +} + +static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + size_t avail = FIFO_WRITE_AVAIL(sdl->microphone->sample_buffer); + size_t read_size = len > (int)avail ? avail : len; + /* If the sample buffer is almost full, + * just write as much as we can into it*/ + + fifo_write(sdl->microphone->sample_buffer, stream, read_size); +#ifdef HAVE_THREADS + scond_signal(sdl->microphone->cond); +#endif +} + +static void *sdl_microphone_open_mic(void *data, + const char *device, + unsigned rate, + unsigned latency, + unsigned block_frames, + unsigned *new_rate) +{ + int frames; + size_t bufsize; + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + sdl_microphone_handle_t *microphone = NULL; + SDL_AudioSpec desired_spec = {0}; + SDL_AudioSpec out; + void *tmp = NULL; + + if (!sdl || !SDL_WasInit(SDL_INIT_AUDIO)) + { /* If the audio driver wasn't initialized yet... */ + RARCH_ERR("[SDL mic]: Attempted to initialize input device before initializing the audio subsystem\n"); + return NULL; + } + + if (sdl->microphone) + { + RARCH_ERR("[SDL mic]: Attempted to initialize a second microphone, but the driver only supports one right now\n"); + return NULL; + } + + microphone = (sdl_microphone_handle_t *)calloc(1, sizeof(sdl_microphone_handle_t)); + if (!microphone) + return NULL; + + if (verbosity_is_enabled()) + { /* Only print SDL audio devices if verbose logging is enabled */ + int i; + int num_available_microphones = SDL_GetNumAudioDevices(true); + RARCH_DBG("[SDL mic]: %d audio capture devices found:\n", num_available_microphones); + for (i = 0; i < num_available_microphones; ++i) { + RARCH_DBG("[SDL mic]: - %s\n", SDL_GetAudioDeviceName(i, true)); + } + } + + /* We have to buffer up some data ourselves, so we let SDL + * carry approximately half of the latency. + * + * SDL double buffers audio and we do as well. */ + frames = find_num_frames(rate, latency / 4); + + desired_spec.freq = rate; + desired_spec.format = AUDIO_S16SYS; + desired_spec.channels = 1; /* Microphones only usually provide input in mono */ + desired_spec.samples = frames; + desired_spec.userdata = sdl; + desired_spec.callback = sdl_audio_record_cb; + + microphone->device_id = SDL_OpenAudioDevice(NULL, true, &desired_spec, &out, 0); + if (microphone->device_id == 0) + { + RARCH_ERR("[SDL mic]: Failed to open SDL audio input device: %s\n", SDL_GetError()); + goto error; + } + RARCH_DBG("[SDL mic]: Opened SDL audio input device with ID %u\n", + microphone->device_id); + RARCH_DBG("[SDL mic]: Requested a microphone frequency of %u Hz, got %u Hz\n", + desired_spec.freq, out.freq); + RARCH_DBG("[SDL mic]: Requested %u channels for microphone, got %u\n", + desired_spec.channels, out.channels); + RARCH_DBG("[SDL mic]: Requested a %u-sample microphone buffer, got %u samples (%u bytes)\n", + frames, out.samples, out.size); + RARCH_DBG("[SDL mic]: Got a microphone silence value of %u\n", out.silence); + RARCH_DBG("[SDL mic]: Requested microphone audio format: %u-bit %s %s %s endian\n", + SDL_AUDIO_BITSIZE(desired_spec.format), + SDL_AUDIO_ISSIGNED(desired_spec.format) ? "signed" : "unsigned", + SDL_AUDIO_ISFLOAT(desired_spec.format) ? "floating-point" : "integer", + SDL_AUDIO_ISBIGENDIAN(desired_spec.format) ? "big" : "little"); + + RARCH_DBG("[SDL mic]: Received microphone audio format: %u-bit %s %s %s endian\n", + SDL_AUDIO_BITSIZE(desired_spec.format), + SDL_AUDIO_ISSIGNED(desired_spec.format) ? "signed" : "unsigned", + SDL_AUDIO_ISFLOAT(desired_spec.format) ? "floating-point" : "integer", + SDL_AUDIO_ISBIGENDIAN(desired_spec.format) ? "big" : "little"); + + if (new_rate) + *new_rate = out.freq; + +#ifdef HAVE_THREADS + microphone->lock = slock_new(); + microphone->cond = scond_new(); +#endif + + RARCH_LOG("[SDL audio]: Requested %u ms latency for input device, got %d ms\n", + latency, (int)(out.samples * 4 * 1000 / out.freq)); + + /* Create a buffer twice as big as needed and prefill the buffer. */ + bufsize = out.samples * 2 * sizeof(int16_t); + tmp = calloc(1, bufsize); + microphone->sample_buffer = fifo_new(bufsize); + + RARCH_DBG("[SDL audio]: Initialized microphone sample queue with %u bytes\n", bufsize); + + if (tmp) + { + fifo_write(microphone->sample_buffer, tmp, bufsize); + free(tmp); + } + + sdl->microphone = microphone; + + RARCH_LOG("[SDL audio]: Initialized microphone with device ID %u\n", microphone->device_id); + return microphone; + +error: + free(microphone); + return NULL; +} + +static void sdl_microphone_close_mic(void *data, void *microphone_context) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t *)microphone_context; + + if (sdl && microphone) + { + retro_assert(sdl->microphone == microphone); + /* Driver only supports one microphone for now; when multi-mic support + * is added, you may still want to assert that the microphone + * was indeed created by the driver */ + + if (microphone->device_id > 0) + { /* If the microphone was originally initialized successfully... */ + SDL_CloseAudioDevice(microphone->device_id); + } + + if (microphone->sample_buffer) + { + fifo_free(microphone->sample_buffer); + } + +#ifdef HAVE_THREADS + slock_free(microphone->lock); + scond_free(microphone->cond); +#endif + + RARCH_LOG("[SDL audio]: Freed microphone with former device ID %u\n", microphone->device_id); + sdl->microphone = NULL; + free(microphone); + } +} + +static bool sdl_microphone_get_mic_active(const void *data, const void *microphone_context) +{ + const sdl_microphone_t *sdl = (const sdl_microphone_t*)data; + const sdl_microphone_handle_t *microphone = (const sdl_microphone_handle_t*)microphone_context; + + if (!sdl || !microphone) + return false; + /* Both params must be non-null */ + + return !microphone->is_paused; + /* The mic might be paused due to app requirements, + * or it might be stopped because the entire audio driver is stopped. */ +} + +static bool sdl_microphone_set_mic_active(void *data, void *microphone_context, bool enabled) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t*)microphone_context; + + if (!sdl || !microphone) + return false; + + microphone->is_paused = !enabled; + if (!sdl->is_paused) + { /* If the entire audio driver isn't paused... */ + SDL_PauseAudioDevice(microphone->device_id, microphone->is_paused); + } + RARCH_DBG("[SDL audio]: Set state of microphone %u to %s\n", + microphone->device_id, enabled ? "enabled" : "disabled"); + return true; +} + +static void sdl_microphone_set_nonblock_state(void *data, bool state) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + if (sdl) + sdl->nonblock = state; +} + +static bool sdl_microphone_use_float(const void *data, const void *mic_context) +{ + (void)data; + (void)mic_context; + + return false; +} + +static ssize_t sdl_microphone_read(void *data, void *microphone_context, void *buf, size_t size) +{ + ssize_t ret = 0; + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t*)microphone_context; + + if (!sdl || !microphone || !buf) + return -1; + + if (sdl->nonblock) + { /* If we shouldn't block on an empty queue... */ + size_t avail, read_amt; + + SDL_LockAudioDevice(microphone->device_id); /* Stop the SDL mic thread */ + avail = FIFO_READ_AVAIL(microphone->sample_buffer); + read_amt = avail > size ? size : avail; + if (read_amt > 0) + { /* If the incoming queue isn't empty... */ + fifo_read(microphone->sample_buffer, buf, read_amt); + /* ...then read as much data as will fit in buf */ + } + SDL_UnlockAudioDevice(microphone->device_id); /* Let the mic thread run again */ + ret = read_amt; + } + else + { + size_t read = 0; + + while (read < size) + { /* Until we've given the caller as much data as they've asked for... */ + size_t avail; + + SDL_LockAudioDevice(microphone->device_id); + /* Stop the SDL microphone thread from running */ + avail = FIFO_READ_AVAIL(microphone->sample_buffer); + + if (avail == 0) + { /* If the incoming sample queue is empty... */ + SDL_UnlockAudioDevice(microphone->device_id); + /* Let the SDL microphone thread run so it can push some incoming samples */ +#ifdef HAVE_THREADS + slock_lock(microphone->lock); + /* Let *only* the SDL microphone thread access the incoming sample queue. */ + + scond_wait(microphone->cond, microphone->lock); + /* Wait until the SDL microphone thread tells us it's added some samples. */ + + slock_unlock(microphone->lock); + /* Allow this thread to access the incoming sample queue, which we'll do next iteration */ +#endif + } + else + { + size_t read_amt = MIN(size - read, avail); + fifo_read(microphone->sample_buffer, buf + read, read_amt); + /* Read as many samples as we have available without underflowing the queue */ + + SDL_UnlockAudioDevice(microphone->device_id); + /* Let the SDL microphone thread run again */ + read += read_amt; + } + } + ret = read; + } + + return ret; +} + +static bool sdl_microphone_stop(void *data) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + sdl->is_paused = true; + + if (sdl->microphone) + { + /* Stop the microphone independently of whether it's paused; + * note that upon sdl_audio_start, the microphone won't resume + * if it was previously paused */ + SDL_PauseAudioDevice(sdl->microphone->device_id, true); + } + + return true; +} + +static bool sdl_microphone_alive(void *data) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + if (!sdl) + return false; + return !sdl->is_paused; +} + +static bool sdl_microphone_start(void *data, bool is_shutdown) +{ + sdl_microphone_t *sdl = (sdl_microphone_t*)data; + sdl->is_paused = false; + + if (sdl->microphone) + { + if (!sdl->microphone->is_paused) + SDL_PauseAudioDevice(sdl->microphone->device_id, false); + /* If the microphone wasn't paused beforehand... */ + } + + return true; +} + +microphone_driver_t microphone_sdl = { + sdl_microphone_init, + sdl_microphone_free, + sdl_microphone_read, + sdl_microphone_stop, + sdl_microphone_start, + sdl_microphone_alive, + sdl_microphone_set_mic_active, + sdl_microphone_get_mic_active, + sdl_microphone_set_nonblock_state, + sdl_microphone_use_float, + "sdl2", + NULL, + NULL, + NULL, + NULL, + sdl_microphone_open_mic, + sdl_microphone_close_mic +}; \ No newline at end of file From 52aa83b27a717f01a8232e3600468da278917755 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 11:34:57 -0500 Subject: [PATCH 121/309] Un-const an argument - In case the driver context needs to do any locking --- microphone/microphone_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index 91aded0fe237..ffb01de31d49 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -175,7 +175,7 @@ typedef struct microphone_driver * @return \c true if the driver is active. * \c false if not or if there was an error. */ - bool (*alive)(const void *driver_context); + bool (*alive)(void *driver_context); /** * Toggles the active state of the provided microphone. From b8b17fbe9084176b58d32f9f0d00ccd0600da8dd Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 12:04:55 -0500 Subject: [PATCH 122/309] Revise comments for microphone_driver.h --- microphone/microphone_driver.h | 106 ++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index ffb01de31d49..f698778a8608 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -93,7 +93,7 @@ typedef struct microphone_driver * After this function is called, * microphones can be opened with \c open_mic. * - * @returns A handle to the general microphone driver context, + * @returns A handle to the microphone driver context, * or \c NULL if there was an error. **/ void *(*init)(void); @@ -116,19 +116,17 @@ typedef struct microphone_driver /** * Read samples from the microphone into the provided buffer. * - * Samples are provided in mono format, - * in either signed 16-bit integer or 32-bit floating point. + * Samples are provided in mono. * Since samples are in mono, a "frame" and a "sample" mean the same thing * in the context of microphone input. * * If \c use_float() returns \c true, * samples will be in 32-bit \c float format with a range of [-1.0, 1.0]. - * format will be used, with range [-1.0, 1.0]. * Otherwise, samples will be in signed 16-bit integer format. * Data will be in native byte order either way. * * All reads should block until all requested frames are provided, - * unless set otherwise wioth set_nonblock_state(). + * unless set otherwise with set_nonblock_state(). * * @param[in] driver_context Pointer to the driver context. * Will be the value that was returned by \c ::init(). @@ -158,13 +156,12 @@ typedef struct microphone_driver bool (*stop)(void *driver_context); /** - * Resumes audio driver from the paused state. - * Microphones will resume recording \em if they were already active + * Resumes the driver from the paused state. + * Microphones will resume activity \em if they were already active * before the driver was stopped. **/ bool (*start)(void *data, bool is_shutdown); - /** * Queries whether the driver is active. * This only queries the overall driver state, @@ -178,11 +175,10 @@ typedef struct microphone_driver bool (*alive)(void *driver_context); /** - * Toggles the active state of the provided microphone. - * TODO mics are inactive when created - * TODO intended for cores that provide a mic button - * TODO paused driver won't resume, but will determine if mic resumes when unpaused - * TODO active mics retrieve samples + * Sets the active state of the provided microphone. + * Microphones must be active in order to read samples from them. + * Microphones are inactive when created, + * so cores will need to activate them explicitly with this function. * * @param[in] driver_context Pointer to the driver context. * Will be the value that was returned by \c ::init(). @@ -196,6 +192,7 @@ typedef struct microphone_driver bool (*set_mic_active)(void *driver_context, void *mic_context, bool active); /** + * Returns the active state of the provided microphone. * Note that this describes the user-facing state of the microphone; * this function can still return \c true if the mic driver is paused * or muted. @@ -340,16 +337,16 @@ typedef struct microphone_driver /** * Releases the resources used by a particular microphone * and stops its activity. - * Will do nothing if either \p data or \p microphone_context is \c NULL. + * Will do nothing if either \p driver_context or \p microphone_context is \c NULL. * - * @param data Opaque handle to the audio driver context + * @param driver_context Opaque handle to the audio driver context * that was used to create the provided microphone. * Implementations may use this to help in cleaning up the microphone, * but the driver context itself must \em not be released. * @param microphone_context Opaque handle to the microphone that will be freed. * Implementations should stop any recording activity before freeing resources. * - * @post \p data will still be valid, + * @post \p driver_context will still be valid, * while \p microphone_context will not. */ void (*close_mic)(void *driver_context, void *microphone_context); @@ -360,25 +357,29 @@ typedef struct struct string_list *devices_list; /** - * A scratch buffer for audio output to be processed, - * up to (but excluding) the point where it's converted to 16-bit audio - * to give to the driver. + * A scratch buffer for audio input to be received. */ void *input_samples_buf; size_t input_samples_buf_length; /** * A scratch buffer for processed audio output to be converted to 16-bit, - * so that it can be sent to the driver. + * so that it can be sent to the core. */ int16_t *input_samples_conv_buf; size_t input_samples_conv_buf_length; /** * The current microphone driver. + * Will be a pointer to one of the elements of \c microphone_drivers. */ const microphone_driver_t *driver; + /** + * Opaque handle to the driver-specific context. + */ + void *driver_context; + /** * The handle to the created microphone, if any. * The libretro API is designed to expose multiple microphones, @@ -387,12 +388,6 @@ typedef struct */ retro_microphone_t microphone; - /** - * Opaque handle to the driver-specific context. - */ - void *driver_context; - - enum microphone_driver_state_flags flags; /** @@ -402,27 +397,35 @@ typedef struct } microphone_driver_state_t; /** - * TODO - * @param is_shutdown - * @return + * Starts the active microphone driver. + * @param is_shutdown TODO + * @return \c true if the configured driver was started, + * \c false if there was an error. */ bool microphone_driver_start(bool is_shutdown); /** - * TODO - * @return + * Stops the active microphone driver. + * Microphones will not receive any input + * until \c microphone_driver_start is called again. + * @return \c true if the driver was stopped, + * \c false if there was an error. */ bool microphone_driver_stop(void); /** * Driver function for opening a microphone. * Provided to retro_microphone_interface::init_microphone(). - * @return TODO + * @return Pointer to the newly-opened microphone, + * or \c NULL if there was an error. */ retro_microphone_t *microphone_driver_open_mic(void); /** - * TODO + * Driver function for closing an open microphone. + * Does nothing if the provided microphone is \c NULL. + * @param microphone Pointer to the microphone to close. + * Will be a value that was returned by \c microphone_driver_open_mic. */ void microphone_driver_close_mic(retro_microphone_t *microphone); @@ -434,47 +437,56 @@ bool microphone_driver_set_mic_state(retro_microphone_t *microphone, bool state) /** * TODO * @param microphone - * @return + * @return The active state of \c microphone. + * \c true if the microphone is ready to accept input, + * \c false if not. */ bool microphone_driver_get_mic_state(const retro_microphone_t *microphone); /** - * TODO - * @param microphone - * @param frames - * @param num_frames - * @return + * Reads a particular number of samples from a microphone + * and stores it in a buffer. + * This should be called by the core each frame if there's an active microphone. + * Will block until the buffer is filled, + * so don't ask for more than you'll use in a single frame. + * + * @param microphone The microphone from which samples will be read. + * @param samples The buffer in which incoming samples will be stored. + * @param num_samples The available size of the provided buffer, + * in samples (\em not bytes). + * @return The number of samples that were read, or -1 if there was an error. */ -int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size_t num_frames); +int microphone_driver_read(retro_microphone_t *microphone, int16_t* samples, size_t num_samples); /** - * TODO + * The ALSA-backed microphone driver. */ extern microphone_driver_t microphone_alsa; /** - * TODO + * The multithreaded ALSA-backed microphone driver. */ extern microphone_driver_t microphone_alsathread; /** - * TODO + * The SDL-backed microphone driver. */ extern microphone_driver_t microphone_sdl; /** - * TODO + * The WASAPI-backed microphone driver. */ extern microphone_driver_t microphone_wasapi; /** - * TODO - * @return + * @return Pointer to the global microphone driver state. */ microphone_driver_state_t *microphone_state_get_ptr(void); /** - * TODO + * All microphone drivers available for use in this build. + * The contents of this array depend on the build configuration + * and target platform. */ extern microphone_driver_t *microphone_drivers[]; From 590d298f1d6a176d66cdc23f208d6e390fc77ff2 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 12:05:09 -0500 Subject: [PATCH 123/309] Remove an unimplemented function --- microphone/microphone_driver.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 165ba56bf4e1..6938505f7f57 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -601,7 +601,6 @@ static bool microphone_driver_free_devices_list(void) return true; } -static void report_microphone_buffer_statistics(void); static bool microphone_driver_deinit_internal(bool microphone_enable) { microphone_driver_state_t *mic_st = &mic_driver_st; @@ -627,10 +626,6 @@ static bool microphone_driver_deinit_internal(bool microphone_enable) memalign_free(mic_st->input_samples_buf); mic_st->input_samples_buf = NULL; -#ifdef DEBUG - report_microphone_buffer_statistics(); -#endif - return true; } From 48f688df85995a8fa374901dde334a2c073592d5 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 12:05:21 -0500 Subject: [PATCH 124/309] Remove some functions from the mic driver --- microphone/drivers/sdl_microphone.c | 11 -------- microphone/microphone_driver.h | 40 ----------------------------- 2 files changed, 51 deletions(-) diff --git a/microphone/drivers/sdl_microphone.c b/microphone/drivers/sdl_microphone.c index 0516cd533235..6641d66d0061 100644 --- a/microphone/drivers/sdl_microphone.c +++ b/microphone/drivers/sdl_microphone.c @@ -328,14 +328,6 @@ static void sdl_microphone_set_nonblock_state(void *data, bool state) sdl->nonblock = state; } -static bool sdl_microphone_use_float(const void *data, const void *mic_context) -{ - (void)data; - (void)mic_context; - - return false; -} - static ssize_t sdl_microphone_read(void *data, void *microphone_context, void *buf, size_t size) { ssize_t ret = 0; @@ -453,12 +445,9 @@ microphone_driver_t microphone_sdl = { sdl_microphone_set_mic_active, sdl_microphone_get_mic_active, sdl_microphone_set_nonblock_state, - sdl_microphone_use_float, "sdl2", NULL, NULL, - NULL, - NULL, sdl_microphone_open_mic, sdl_microphone_close_mic }; \ No newline at end of file diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index f698778a8608..356bae788af0 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -225,21 +225,6 @@ typedef struct microphone_driver * */ void (*set_nonblock_state)(void *driver_context, bool nonblock); - - /** - * @param driver_context Pointer to the driver context. - * Will be the value that was returned by \c ::init(). - * @param mic_context Pointer to a microphone context. - * Will be a value that was returned by \c ::open_mic(). - * @return True if this mic provides data in \c float format, - * false if it uses \c int16_t samples. - * - * @note Drivers should use \c int16_t samples if possible. - * Since the microphone driver does not resample input, - * integer samples can be provided directly to the core. - */ - bool (*use_float)(const void *driver_context, const void *mic_context); - /** * A human-readable name for this driver. * Shown to the user in the driver selection menu. @@ -273,31 +258,6 @@ typedef struct microphone_driver */ void (*device_list_free)(const void *driver_context, struct string_list *devices); - /** - * The available space of the provided microphone's buffer, - * in bytes (\em not samples). - * Note that this function does *not* return the number of - * samples that can be read; - * Used to control the rate at which samples are read - * to avoid buffer overruns or underruns. - * - * Optional, but must be provided if \c buffer_size is implemented. - * - * @param[in] driver_context Pointer to the driver context. - * Will be the value that was returned by \c init(). - * @param mic_context Pointer to a microphone context. - * Will be a value that was returned by \c ::open_mic(). - * @returns The number of bytes that are available from the - **/ - size_t (*read_avail)(const void *driver_context, const void *mic_context); - - /** - * - * @param mic_context - * @return - */ - size_t (*buffer_size)(const void *driver_context, const void *mic_context); - /** * Initializes a microphone using the audio driver. * Cores that use microphone functionality will call this via From d045ab090003189fe4db1c1d2757be6b71f707a7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 12:06:12 -0500 Subject: [PATCH 125/309] Remove mic functions from audio_thread_wrapper --- audio/audio_thread_wrapper.c | 106 +---------------------------------- 1 file changed, 1 insertion(+), 105 deletions(-) diff --git a/audio/audio_thread_wrapper.c b/audio/audio_thread_wrapper.c index ef63dc07684c..f52feefc7b1f 100644 --- a/audio/audio_thread_wrapper.c +++ b/audio/audio_thread_wrapper.c @@ -270,105 +270,6 @@ static ssize_t audio_thread_write(void *data, const void *buf, size_t size) return ret; } -static void *audio_thread_init_microphone(void *data, const char *device, unsigned rate, - unsigned latency, unsigned block_frames, unsigned *new_rate) -{ - // TODO: Implement properly - audio_thread_t *thr = (audio_thread_t*)data; - void *microphone = NULL; - - if (!thr) - return NULL; - - if ( thr->driver && - thr->driver_data && - thr->driver->init_microphone) - microphone = thr->driver->init_microphone(thr->driver_data, - device, - rate, - latency, - block_frames, - new_rate - ); - - return microphone; -} - -/** - * Should only be called on the main thread. - */ -static void audio_thread_free_microphone(void *data, void *microphone_context) -{ - // TODO: Implement properly - audio_thread_t *thr = (audio_thread_t*)data; - - if (!thr) - return; - - if ( thr->driver && - thr->driver_data && - thr->driver->free_microphone && - microphone_context) - thr->driver->free_microphone(thr->driver_data, microphone_context); -} - -/** - * Can be called from the main thread or the audio callback. - */ -static bool audio_thread_get_microphone_state(const void *data, const void *microphone_context) -{ - audio_thread_t *thr = (audio_thread_t*)data; - - if (!thr) - return false; - - if ( thr->driver && - thr->driver_data && - thr->driver->get_microphone_state) - return thr->driver->get_microphone_state(thr->driver_data, microphone_context); - /* Don't lock here, because this function could be called each frame */ - - return false; -} - -static bool audio_thread_set_microphone_state(void *data, void *microphone_context, bool enabled) -{ - // TODO: Implement properly - audio_thread_t *thr = (audio_thread_t*)data; - - if (!thr) - return false; - - if ( thr->driver && - thr->driver_data && - thr->driver->set_microphone_state) - return thr->driver->set_microphone_state(thr->driver_data, microphone_context, enabled); - - return false; -} - -static ssize_t audio_thread_read_microphone(void *data, void *microphone_context, void *buf, size_t size) -{ - // TODO: Implement properly - int ret; - audio_thread_t *thr = (audio_thread_t*)data; - - if (!thr) - return 0; - - ret = thr->driver->read_microphone(thr->driver_data, microphone_context, buf, size); - - if (ret < 0) - { - slock_lock(thr->lock); - thr->alive = false; - scond_signal(thr->cond); - slock_unlock(thr->lock); - } - - return ret; -} - static const audio_driver_t audio_thread = { NULL, /* No need to wrap init, it's called at the start of the thread loop */ audio_thread_write, @@ -382,12 +283,7 @@ static const audio_driver_t audio_thread = { NULL, /* No point in using rate control with threaded audio. */ NULL, NULL, - NULL, - audio_thread_init_microphone, - audio_thread_free_microphone, - audio_thread_get_microphone_state, - audio_thread_set_microphone_state, - audio_thread_read_microphone + NULL }; /** From acdc5a73fd6228321614a12c2c696be0a5897ad4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 12:06:29 -0500 Subject: [PATCH 126/309] Remove mic functions from sdl_audio --- audio/drivers/sdl_audio.c | 335 +------------------------------------- 1 file changed, 1 insertion(+), 334 deletions(-) diff --git a/audio/drivers/sdl_audio.c b/audio/drivers/sdl_audio.c index 142772deed05..15255df9afa1 100644 --- a/audio/drivers/sdl_audio.c +++ b/audio/drivers/sdl_audio.c @@ -76,28 +76,6 @@ typedef Uint32 SDL_AudioDeviceID; #define SDL_AUDIO_ISUNSIGNED(x) (!SDL_AUDIO_ISSIGNED(x)) #endif -#ifdef SDL_DRIVER_MIC_SUPPORT -typedef struct sdl_audio_microphone -{ -#ifdef HAVE_THREADS - slock_t *lock; - scond_t *cond; -#endif - - /** - * The queue used to store incoming samples from the driver. - * Audio from the microphone is stored here, - * the first stop before the audio driver processes it - * and makes it ready for the core. - */ - fifo_buffer_t *sample_buffer; - bool is_paused; - SDL_AudioDeviceID device_id; -} sdl_audio_microphone_t; - -static void sdl_audio_free_microphone(void *data, void *microphone_context); -#endif - typedef struct sdl_audio { #ifdef HAVE_THREADS @@ -113,13 +91,6 @@ typedef struct sdl_audio bool nonblock; bool is_paused; SDL_AudioDeviceID speaker_device; - -#ifdef SDL_DRIVER_MIC_SUPPORT - /* Only one microphone is supported right now; - * the driver state should track multiple microphone handles, - * but the driver *context* should track multiple microphone contexts */ - sdl_audio_microphone_t *microphone; -#endif } sdl_audio_t; static void sdl_audio_playback_cb(void *data, Uint8 *stream, int len) @@ -137,22 +108,6 @@ static void sdl_audio_playback_cb(void *data, Uint8 *stream, int len) memset(stream + write_size, 0, len - write_size); } -#ifdef SDL_DRIVER_MIC_SUPPORT -static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) -{ - sdl_audio_t *sdl = (sdl_audio_t*)data; - size_t avail = FIFO_WRITE_AVAIL(sdl->microphone->sample_buffer); - size_t read_size = len > (int)avail ? avail : len; - /* If the sample buffer is almost full, - * just write as much as we can into it*/ - - fifo_write(sdl->microphone->sample_buffer, stream, read_size); -#ifdef HAVE_THREADS - scond_signal(sdl->microphone->cond); -#endif -} -#endif - static INLINE int find_num_frames(int rate, int latency) { int frames = (rate * latency) / 1000; @@ -339,16 +294,6 @@ static bool sdl_audio_stop(void *data) sdl->is_paused = true; SDL_PauseAudioDevice(sdl->speaker_device, true); -#if SDL_DRIVER_MIC_SUPPORT - if (sdl->microphone) - { - /* Stop the microphone independently of whether it's paused; - * note that upon sdl_audio_start, the microphone won't resume - * if it was previously paused */ - SDL_PauseAudioDevice(sdl->microphone->device_id, true); - } -#endif - return true; } @@ -367,15 +312,6 @@ static bool sdl_audio_start(void *data, bool is_shutdown) SDL_PauseAudioDevice(sdl->speaker_device, false); -#if SDL_DRIVER_MIC_SUPPORT - if (sdl->microphone) - { - if (!sdl->microphone->is_paused) - SDL_PauseAudioDevice(sdl->microphone->device_id, false); - /* If the microphone wasn't paused beforehand... */ - } -#endif - return true; } @@ -402,11 +338,6 @@ static void sdl_audio_free(void *data) fifo_free(sdl->speaker_buffer); } -#if SDL_DRIVER_MIC_SUPPORT - if (sdl->microphone) - sdl_audio_free_microphone(sdl, sdl->microphone); -#endif - #ifdef HAVE_THREADS slock_free(sdl->lock); scond_free(sdl->cond); @@ -430,257 +361,6 @@ static size_t sdl_audio_write_avail(void *data) return 0; } -#if SDL_DRIVER_MIC_SUPPORT -static void *sdl_audio_init_microphone(void *data, - const char *device, - unsigned rate, - unsigned latency, - unsigned block_frames, - unsigned *new_rate) -{ - int frames; - size_t bufsize; - sdl_audio_t *sdl = (sdl_audio_t*)data; - sdl_audio_microphone_t *microphone = NULL; - SDL_AudioSpec desired_spec = {0}; - SDL_AudioSpec out; - void *tmp = NULL; - - if (!sdl || !SDL_WasInit(SDL_INIT_AUDIO)) - { /* If the audio driver wasn't initialized yet... */ - RARCH_ERR("[SDL audio]: Attempted to initialize input device before initializing the audio driver\n"); - return NULL; - } - - if (sdl->microphone) - { - RARCH_ERR("[SDL audio]: Attempted to initialize a second microphone, but the driver only supports one right now\n"); - return NULL; - } - - microphone = (sdl_audio_microphone_t *)calloc(1, sizeof(sdl_audio_microphone_t)); - if (!microphone) - return NULL; - - if (verbosity_is_enabled()) - { /* Only print SDL audio devices if verbose logging is enabled */ - int i; - int num_available_microphones = SDL_GetNumAudioDevices(true); - RARCH_DBG("[SDL audio]: %d audio capture devices found:\n", num_available_microphones); - for (i = 0; i < num_available_microphones; ++i) { - RARCH_DBG("[SDL audio]: - %s\n", SDL_GetAudioDeviceName(i, true)); - } - } - - /* We have to buffer up some data ourselves, so we let SDL - * carry approximately half of the latency. - * - * SDL double buffers audio and we do as well. */ - frames = find_num_frames(rate, latency / 4); - - desired_spec.freq = rate; - desired_spec.format = AUDIO_S16SYS; - desired_spec.channels = 1; /* Microphones only usually provide input in mono */ - desired_spec.samples = frames; - desired_spec.userdata = sdl; - desired_spec.callback = sdl_audio_record_cb; - - microphone->device_id = SDL_OpenAudioDevice(NULL, true, &desired_spec, &out, 0); - if (microphone->device_id == 0) - { - RARCH_ERR("[SDL audio]: Failed to open SDL audio input device: %s\n", SDL_GetError()); - goto error; - } - RARCH_DBG("[SDL audio]: Opened SDL audio input device with ID %u\n", - microphone->device_id); - RARCH_DBG("[SDL audio]: Requested a microphone frequency of %u Hz, got %u Hz\n", - desired_spec.freq, out.freq); - RARCH_DBG("[SDL audio]: Requested %u channels for microphone, got %u\n", - desired_spec.channels, out.channels); - RARCH_DBG("[SDL audio]: Requested a %u-sample microphone buffer, got %u samples (%u bytes)\n", - frames, out.samples, out.size); - RARCH_DBG("[SDL audio]: Got a microphone silence value of %u\n", out.silence); - RARCH_DBG("[SDL audio]: Requested microphone audio format: %u-bit %s %s %s endian\n", - SDL_AUDIO_BITSIZE(desired_spec.format), - SDL_AUDIO_ISSIGNED(desired_spec.format) ? "signed" : "unsigned", - SDL_AUDIO_ISFLOAT(desired_spec.format) ? "floating-point" : "integer", - SDL_AUDIO_ISBIGENDIAN(desired_spec.format) ? "big" : "little"); - - RARCH_DBG("[SDL audio]: Received microphone audio format: %u-bit %s %s %s endian\n", - SDL_AUDIO_BITSIZE(desired_spec.format), - SDL_AUDIO_ISSIGNED(desired_spec.format) ? "signed" : "unsigned", - SDL_AUDIO_ISFLOAT(desired_spec.format) ? "floating-point" : "integer", - SDL_AUDIO_ISBIGENDIAN(desired_spec.format) ? "big" : "little"); - - if (new_rate) - *new_rate = out.freq; - -#ifdef HAVE_THREADS - microphone->lock = slock_new(); - microphone->cond = scond_new(); -#endif - - RARCH_LOG("[SDL audio]: Requested %u ms latency for input device, got %d ms\n", - latency, (int)(out.samples * 4 * 1000 / out.freq)); - - /* Create a buffer twice as big as needed and prefill the buffer. */ - bufsize = out.samples * 2 * sizeof(int16_t); - tmp = calloc(1, bufsize); - microphone->sample_buffer = fifo_new(bufsize); - - RARCH_DBG("[SDL audio]: Initialized microphone sample queue with %u bytes\n", bufsize); - - if (tmp) - { - fifo_write(microphone->sample_buffer, tmp, bufsize); - free(tmp); - } - - sdl->microphone = microphone; - - RARCH_LOG("[SDL audio]: Initialized microphone with device ID %u\n", microphone->device_id); - return microphone; - -error: - free(microphone); - return NULL; -} - -static void sdl_audio_free_microphone(void *data, void *microphone_context) -{ - sdl_audio_t *sdl = (sdl_audio_t*)data; - sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t *)microphone_context; - - if (sdl && microphone) - { - retro_assert(sdl->microphone == microphone); - /* Driver only supports one microphone for now; when multi-mic support - * is added, you may still want to assert that the microphone - * was indeed created by the driver */ - - if (microphone->device_id > 0) - { /* If the microphone was originally initialized successfully... */ - SDL_CloseAudioDevice(microphone->device_id); - } - - if (microphone->sample_buffer) - { - fifo_free(microphone->sample_buffer); - } - -#ifdef HAVE_THREADS - slock_free(microphone->lock); - scond_free(microphone->cond); -#endif - - RARCH_LOG("[SDL audio]: Freed microphone with former device ID %u\n", microphone->device_id); - sdl->microphone = NULL; - free(microphone); - } -} - -static bool sdl_audio_microphone_get_state(const void *data, const void *microphone_context) -{ - const sdl_audio_t *sdl = (const sdl_audio_t*)data; - const sdl_audio_microphone_t *microphone = (const sdl_audio_microphone_t*)microphone_context; - - if (!sdl || !microphone) - return false; - /* Both params must be non-null */ - - return !microphone->is_paused; - /* The mic might be paused due to app requirements, - * or it might be stopped because the entire audio driver is stopped. */ -} - -static bool sdl_audio_microphone_set_state(void *data, void *microphone_context, bool enabled) -{ - sdl_audio_t *sdl = (sdl_audio_t*)data; - sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t*)microphone_context; - - if (!sdl || !microphone) - return false; - - microphone->is_paused = !enabled; - if (!sdl->is_paused) - { /* If the entire audio driver isn't paused... */ - SDL_PauseAudioDevice(microphone->device_id, microphone->is_paused); - } - RARCH_DBG("[SDL audio]: Set state of microphone %u to %s\n", - microphone->device_id, enabled ? "enabled" : "disabled"); - return true; -} - -static ssize_t sdl_audio_read_microphone(void *data, void *microphone_context, void *buf, size_t size) -{ - ssize_t ret = 0; - sdl_audio_t *sdl = (sdl_audio_t*)data; - sdl_audio_microphone_t *microphone = (sdl_audio_microphone_t*)microphone_context; - - if (!sdl || !microphone || !buf) - return -1; - - if (sdl->nonblock) - { /* If we shouldn't block on an empty queue... */ - size_t avail, read_amt; - - SDL_LockAudioDevice(microphone->device_id); /* Stop the SDL mic thread */ - avail = FIFO_READ_AVAIL(microphone->sample_buffer); - read_amt = avail > size ? size : avail; - if (read_amt > 0) - { /* If the incoming queue isn't empty... */ - fifo_read(microphone->sample_buffer, buf, read_amt); - /* ...then read as much data as will fit in buf */ - } - SDL_UnlockAudioDevice(microphone->device_id); /* Let the mic thread run again */ - ret = read_amt; - } - else - { - size_t read = 0; - - while (read < size) - { /* Until we've given the caller as much data as they've asked for... */ - size_t avail; - - SDL_LockAudioDevice(microphone->device_id); - /* Stop the SDL microphone thread from running */ - avail = FIFO_READ_AVAIL(microphone->sample_buffer); - - if (avail == 0) - { /* If the incoming sample queue is empty... */ - SDL_UnlockAudioDevice(microphone->device_id); - /* Let the SDL microphone thread run so it can push some incoming samples */ -#ifdef HAVE_THREADS - slock_lock(microphone->lock); - /* Let *only* the SDL microphone thread access the incoming sample queue. */ - - scond_wait(microphone->cond, microphone->lock); - /* Wait until the SDL microphone thread tells us it's added some samples. */ - - slock_unlock(microphone->lock); - /* Allow this thread to access the incoming sample queue, which we'll do next iteration */ -#endif - } - else - { - size_t read_amt = MIN(size - read, avail); - fifo_read(microphone->sample_buffer, buf + read, read_amt); - /* Read as many samples as we have available without underflowing the queue */ - - SDL_UnlockAudioDevice(microphone->device_id); - /* Let the SDL microphone thread run again */ - read += read_amt; - } - } - ret = read; - } - - return ret; -} - -#endif - audio_driver_t audio_sdl = { sdl_audio_init, sdl_audio_write, @@ -698,18 +378,5 @@ audio_driver_t audio_sdl = { NULL, NULL, sdl_audio_write_avail, - NULL, -#if SDL_DRIVER_MIC_SUPPORT - sdl_audio_init_microphone, - sdl_audio_free_microphone, - sdl_audio_microphone_get_state, - sdl_audio_microphone_set_state, - sdl_audio_read_microphone -#else - NULL, /* Microphone support for this driver requires SDL 2 */ - NULL, - NULL, - NULL, - NULL -#endif + NULL }; From 5b45a87c4d73200b85699d09ab69f7c9acf03ed7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 12:07:30 -0500 Subject: [PATCH 127/309] Fix microphone_null --- microphone/microphone_driver.c | 1 - 1 file changed, 1 deletion(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 6938505f7f57..6212feb9a732 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -35,7 +35,6 @@ microphone_driver_t microphone_null = { NULL, NULL, NULL, - NULL, "null", NULL, NULL From ee18df1f490db76ee836b74c6cfed9f8b3fa0e99 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 21:39:22 -0500 Subject: [PATCH 128/309] Split the mic code for the alsa audio drivers into microphone drivers --- Makefile.common | 6 +- audio/drivers/alsa.c | 383 +------------------------ audio/drivers/alsa.h | 18 ++ audio/drivers/alsathread.c | 323 +--------------------- microphone/drivers/alsa.c | 475 ++++++++++++++++++++++++++++++++ microphone/drivers/alsathread.c | 451 ++++++++++++++++++++++++++++++ 6 files changed, 963 insertions(+), 693 deletions(-) create mode 100644 microphone/drivers/alsa.c create mode 100644 microphone/drivers/alsathread.c diff --git a/Makefile.common b/Makefile.common index 032645c56e4b..22045fd7247e 100644 --- a/Makefile.common +++ b/Makefile.common @@ -863,7 +863,8 @@ endif endif ifeq ($(HAVE_ALSA), 1) - OBJ += audio/drivers/alsa.o + OBJ += audio/drivers/alsa.o \ + microphone/drivers/alsa.o ifneq ($(HAVE_HAKCHI), 1) ifneq ($(HAVE_SEGAM), 1) @@ -875,7 +876,8 @@ ifeq ($(HAVE_ALSA), 1) ifneq ($(MIYOO), 1) ifeq ($(HAVE_THREADS), 1) - OBJ += audio/drivers/alsathread.o + OBJ += audio/drivers/alsathread.o \ + microphone/drivers/alsathread.o endif endif diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 7a8b7f451e23..0e754b2e11fd 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -26,20 +26,9 @@ #include "alsa.h" #include "../../verbosity.h" -typedef struct alsa_microphone -{ - snd_pcm_t *pcm; - alsa_stream_info_t stream_info; - bool is_paused; -} alsa_microphone_t; - typedef struct alsa { snd_pcm_t *pcm; - /* Only one microphone is supported right now; - * the driver state should track multiple microphone handles, - * but the driver *context* should track multiple microphone contexts */ - alsa_microphone_t *microphone; /* The error handler that was set before this driver was initialized. * Likely to be equal to the default, but kept and restored just in case. */ @@ -253,7 +242,10 @@ int alsa_init_pcm(snd_pcm_t **pcm, goto error; } - RARCH_LOG("[ALSA]: Period size: %lu frames (%lu bytes)\n", stream_info->period_frames, stream_info->period_size); + RARCH_LOG("[ALSA]: Period: %u periods per buffer (%lu frames, %lu bytes)\n", + periods, + stream_info->period_frames, + stream_info->period_size); if ((errnum = snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) < 0) { @@ -308,8 +300,6 @@ int alsa_init_pcm(snd_pcm_t **pcm, if ((errnum = snd_pcm_sw_params_set_start_threshold(*pcm, sw_params, buffer_size / 2)) < 0) { - // TODO: Should "2" be a separate parameter, or equal to channels? - RARCH_ERR("[ALSA]: Failed to set start %lu-frame threshold for %s device \"%s\": %s\n", buffer_size / 2, snd_pcm_stream_name(stream), @@ -523,21 +513,6 @@ static bool alsa_stop(void *data) alsa->is_paused = true; } - if (alsa->microphone) - { - /* Stop the microphone independently of whether it's paused; - * note that upon alsa_start, the microphone won't resume - * if it was previously paused */ - int errnum = snd_pcm_pause(alsa->microphone->pcm, true); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to pause microphone \"%s\": %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_strerror(errnum)); - return false; - } - } - return true; } @@ -551,7 +526,7 @@ static bool alsa_start(void *data, bool is_shutdown) { alsa_t *alsa = (alsa_t*)data; if (!alsa->is_paused) - return true; + return true; if (alsa->stream_info.can_pause && alsa->is_paused) @@ -566,58 +541,10 @@ static bool alsa_start(void *data, bool is_shutdown) } alsa->is_paused = false; - - if (alsa->microphone && !alsa->microphone->is_paused) - { /* If the mic wasn't paused at the time the overall driver was paused... */ - snd_pcm_state_t mic_state = snd_pcm_state(alsa->microphone->pcm); - - /* If we're calling this function with a pending microphone, - * (as happens when a core requests a microphone at the start), - * the mic will be in the PREPARED state rather than the PAUSED state. - **/ - switch (mic_state) - { - case SND_PCM_STATE_PREPARED: - { - int errnum = snd_pcm_start(alsa->microphone->pcm); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_strerror(errnum)); - - return false; - } - break; - } - case SND_PCM_STATE_PAUSED: - { - int errnum = snd_pcm_pause(alsa->microphone->pcm, false); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to unpause microphone \"%s\" in state %s: %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_pcm_state_name(snd_pcm_state(alsa->microphone->pcm)), - snd_strerror(errnum)); - - return false; - } - break; - } - default: - { - RARCH_ERR("[ALSA]: Expected microphone \"%s\" to be in state PREPARED or PAUSED, it was in %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_pcm_state_name(mic_state)); - return false; - } - } - } } return true; } -static void alsa_free_microphone(void *data, void *microphone_context); static void alsa_free(void *data) { alsa_t *alsa = (alsa_t*)data; @@ -625,7 +552,6 @@ static void alsa_free(void *data) if (alsa) { alsa_free_pcm(alsa->pcm); - alsa_free_microphone(alsa, alsa->microphone); if (alsa->prev_error_handler) { /* If we ever changed the error handler, put it back. */ @@ -654,7 +580,7 @@ static size_t alsa_buffer_size(void *data) return alsa->stream_info.buffer_size; } -void *alsa_device_list_new(void *data) +struct string_list *alsa_device_list_type_new(const char* type) { void **hints, **n; union string_list_elem_attr attr; @@ -679,7 +605,7 @@ void *alsa_device_list_new(void *data) /* description of device IOID - input / output identifcation * ("Input" or "Output"), NULL means both) */ - if (!io || (string_is_equal(io, "Output"))) + if (!io || (string_is_equal(io, type))) string_list_append(s, name, attr); if (name) @@ -697,11 +623,16 @@ void *alsa_device_list_new(void *data) return s; -error: + error: string_list_free(s); return NULL; } +void *alsa_device_list_new(void *data) +{ + return alsa_device_list_type_new("Output"); +} + void alsa_device_list_free(void *data, void *array_list_data) { struct string_list *s = (struct string_list*)array_list_data; @@ -712,289 +643,6 @@ void alsa_device_list_free(void *data, void *array_list_data) string_list_free(s); } -static void *alsa_init_microphone(void *data, - const char *device, - unsigned rate, - unsigned latency, - unsigned block_frames, - unsigned *new_rate) -{ - alsa_t *alsa = (alsa_t*)data; - alsa_microphone_t *microphone = NULL; - - if (!alsa) /* If we weren't given a valid ALSA context... */ - return NULL; - - microphone = calloc(1, sizeof(alsa_microphone_t)); - - if (!microphone) /* If the microphone context couldn't be allocated... */ - return NULL; - - /* channels hardcoded to 1, because we only support mono mic input */ - if (alsa_init_pcm(µphone->pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->stream_info, new_rate, SND_PCM_NONBLOCK) < 0) - { - goto error; - } - - if (microphone->stream_info.has_float != alsa->stream_info.has_float) - { /* If the mic and speaker don't both use floating point or integer samples... */ - RARCH_WARN("[ALSA]: Microphone and speaker use different sample formats\n"); - RARCH_WARN("[ALSA]: (%s and %s, respectively)\n", - microphone->stream_info.has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16", - alsa->stream_info.has_float ? "SND_PCM_FORMAT_FLOAT" : "SND_PCM_FORMAT_S16"); - } - - alsa->microphone = microphone; - return microphone; - -error: - RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); - - alsa_free_microphone(alsa, microphone); - - return NULL; -} - -static void alsa_free_microphone(void *data, void *microphone_context) -{ - alsa_t *alsa = (alsa_t*)data; - alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; - - if (alsa && microphone) - { - alsa_free_pcm(microphone->pcm); - - alsa->microphone = NULL; - free(microphone); - } -} - -static bool alsa_get_microphone_state(const void *data, const void *microphone_context) -{ - alsa_t *alsa = (alsa_t*)data; - alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; - - if (!alsa || !microphone) - return false; - /* Both params must be non-null */ - - return !microphone->is_paused; - /* The mic might be paused due to app requirements, - * or it might be stopped because the entire audio driver is stopped. */ -} - -bool alsa_set_mic_enabled_internal(snd_pcm_t *microphone, bool enabled) -{ - snd_pcm_state_t microphone_state = snd_pcm_state(microphone); - int errnum = 0; - - if (enabled) - { /* If we're trying to unpause a mic (or maybe activate it for the first time)... */ - switch (microphone_state) - { - case SND_PCM_STATE_PAUSED: /* If we're unpausing a valid (but paused) mic... */ - if ((errnum = snd_pcm_pause(microphone, false)) < 0) - { /* ...but we failed... */ - goto error; - } - break; - case SND_PCM_STATE_PREPARED: - /* If we're activating this mic for the first time... */ - if ((errnum = snd_pcm_start(microphone)) < 0) - { /* ..but we failed... */ - goto error; - } - break; - default: - goto unexpected_state; - } - } - else - { /* We're pausing this mic */ - switch (microphone_state) - { - case SND_PCM_STATE_PREPARED: - case SND_PCM_STATE_RUNNING: - /* If we're pausing an active mic... */ - if ((errnum = snd_pcm_pause(microphone, true)) < 0) - { /* ...but we failed... */ - goto error; - } - break; - default: - goto unexpected_state; - } - } - - RARCH_DBG("[ALSA]: %s microphone \"%s\", transitioning from %s to %s\n", - enabled ? "Unpaused" : "Paused", - snd_pcm_name(microphone), - snd_pcm_state_name(microphone_state), - snd_pcm_state_name(snd_pcm_state(microphone))); - - return true; -error: - RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in state %s: %s\n", - enabled ? "unpause" : "pause", - snd_pcm_name(microphone), - snd_pcm_state_name(microphone_state), - snd_strerror(errnum)); - - return false; - -unexpected_state: - RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in unexpected state %s\n", - enabled ? "unpause" : "pause", - snd_pcm_name(microphone), - snd_pcm_state_name(microphone_state)); - - return false; -} - -static bool alsa_set_microphone_state(void *data, void *microphone_context, bool enabled) -{ - alsa_t *alsa = (alsa_t*)data; - alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; - - if (!alsa || !microphone) - return false; - /* Both params must be non-null */ - - if (!microphone->stream_info.can_pause) - { - RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->pcm)); - return true; - } - - if (!alsa->is_paused) - { /* If the entire audio driver isn't paused... */ - if (alsa_set_mic_enabled_internal(microphone->pcm, enabled)) - { - microphone->is_paused = !enabled; - return true; - } - return false; - } - - return true; -} - -static ssize_t alsa_read_microphone(void *driver_context, void *microphone_context, void *buf_, size_t size_) -{ - alsa_t *alsa = (alsa_t*)driver_context; - alsa_microphone_t *microphone = (alsa_microphone_t*)microphone_context; - uint8_t *buf = (uint8_t*)buf_; - snd_pcm_sframes_t read = 0; - int errnum = 0; - snd_pcm_sframes_t size; - size_t frames_size; - snd_pcm_state_t state; - - if (!alsa || !microphone || !buf) - return -1; - - size = BYTES_TO_FRAMES(size_, microphone->stream_info.frame_bits); - frames_size = microphone->stream_info.has_float ? sizeof(float) : sizeof(int16_t); - - /* Workaround buggy menu code. - * If a read happens while we're paused, we might never progress. */ - if (microphone->is_paused) - if (!alsa_set_microphone_state(alsa, microphone, true)) - return -1; - - state = snd_pcm_state(microphone->pcm); - if (state != SND_PCM_STATE_RUNNING) - { - RARCH_WARN("[ALSA]: Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", - snd_pcm_name(microphone->pcm), - snd_pcm_state_name(state)); - - errnum = snd_pcm_start(microphone->pcm); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", - snd_pcm_name(microphone->pcm), - snd_strerror(errnum)); - - return -1; - } - } - - if (alsa->nonblock) - { - while (size) - { - snd_pcm_sframes_t frames = snd_pcm_readi(microphone->pcm, buf, size); - - if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) - { - errnum = snd_pcm_recover(microphone->pcm, frames, 0); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to read from microphone: %s\n", snd_strerror(frames)); - RARCH_ERR("[ALSA]: Additionally, recovery failed with: %s\n", snd_strerror(errnum)); - return -1; - } - - break; - } - else if (frames == -EAGAIN) - break; - else if (frames < 0) - return -1; - - read += frames; - buf += frames_size; - size -= frames; - } - } - else - { - bool eagain_retry = true; - - while (size) - { - snd_pcm_sframes_t frames; - int rc = snd_pcm_wait(microphone->pcm, -1); - - if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR) - { - if (snd_pcm_recover(microphone->pcm, rc, 1) < 0) - return -1; - continue; - } - - frames = snd_pcm_readi(microphone->pcm, buf, size); - - if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) - { - if (snd_pcm_recover(microphone->pcm, frames, 1) < 0) - return -1; - - break; - } - else if (frames == -EAGAIN) - { - /* Definitely not supposed to happen. */ - if (eagain_retry) - { - eagain_retry = false; - continue; - } - break; - } - else if (frames < 0) - return -1; - - read += frames; - buf += frames_size; - size -= frames; - } - } - - return FRAMES_TO_BYTES(read, microphone->stream_info.frame_bits); -} - audio_driver_t audio_alsa = { alsa_init, alsa_write, @@ -1009,9 +657,4 @@ audio_driver_t audio_alsa = { alsa_device_list_free, alsa_write_avail, alsa_buffer_size, - alsa_init_microphone, - alsa_free_microphone, - alsa_get_microphone_state, - alsa_set_microphone_state, - alsa_read_microphone, }; diff --git a/audio/drivers/alsa.h b/audio/drivers/alsa.h index 305c45ca808a..e97888527c9e 100644 --- a/audio/drivers/alsa.h +++ b/audio/drivers/alsa.h @@ -17,6 +17,9 @@ #ifndef _RETROARCH_ALSA #define _RETROARCH_ALSA +#include +#include "queues/fifo_queue.h" +#include "rthreads/rthreads.h" /* Header file for common functions that are used by alsa and alsathread. */ /** @@ -32,6 +35,18 @@ typedef struct alsa_stream_info { bool can_pause; } alsa_stream_info_t; +typedef struct alsa_thread_info +{ + snd_pcm_t *pcm; + fifo_buffer_t *buffer; + sthread_t *worker_thread; + slock_t *fifo_lock; + scond_t *cond; + slock_t *cond_lock; + alsa_stream_info_t stream_info; + volatile bool thread_dead; +} alsa_thread_info_t; + int alsa_init_pcm(snd_pcm_t **pcm, const char* device, snd_pcm_stream_t stream, @@ -43,8 +58,11 @@ int alsa_init_pcm(snd_pcm_t **pcm, int mode); void alsa_free_pcm(snd_pcm_t *pcm); void *alsa_device_list_new(void *data); +struct string_list *alsa_device_list_type_new(const char* type); void alsa_device_list_free(void *data, void *array_list_data); +void alsa_thread_free_info_members(alsa_thread_info_t *info); + /** * Sets the state of the PCM stream without updating the mic state */ diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 9d6637a2d6c0..99d662332450 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -30,31 +30,9 @@ #include "alsa.h" #include "../../verbosity.h" -typedef struct alsa_thread_info -{ - snd_pcm_t *pcm; - fifo_buffer_t *buffer; - sthread_t *worker_thread; - slock_t *fifo_lock; - scond_t *cond; - slock_t *cond_lock; - alsa_stream_info_t stream_info; - volatile bool thread_dead; -} alsa_thread_info_t; - -typedef struct alsa_thread_microphone -{ - alsa_thread_info_t info; - bool is_paused; -} alsa_thread_microphone_t; - typedef struct alsa_thread { alsa_thread_info_t info; - /* Only one microphone is supported right now; - * the driver state should track multiple microphone handles, - * but the driver *context* should track multiple microphone contexts */ - alsa_thread_microphone_t *microphone; bool nonblock; bool is_paused; } alsa_thread_t; @@ -120,78 +98,7 @@ static void alsa_worker_thread(void *data) RARCH_DBG("[ALSA] [playback thread %p]: Ending playback worker thread\n", thread_id); } -/** @see alsa_thread_read_microphone() */ -static void alsa_microphone_worker_thread(void *data) -{ - alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)data; - uint8_t *buf = NULL; - uintptr_t thread_id = sthread_get_current_thread_id(); - - retro_assert(microphone != NULL); - buf = (uint8_t *)calloc(1, microphone->info.stream_info.period_size); - if (!buf) - { - RARCH_ERR("[ALSA] [capture thread %p]: Failed to allocate audio buffer\n", thread_id); - goto end; - } - - RARCH_DBG("[ALSA] [capture thread %p]: Beginning microphone worker thread\n", thread_id); - while (!microphone->info.thread_dead) - { /* Until we're told to stop... */ - size_t avail; - size_t fifo_size; - snd_pcm_sframes_t frames; - - /* Lock the incoming sample queue (the main thread may block) */ - slock_lock(microphone->info.fifo_lock); - - /* Fill the incoming sample queue with whatever we recently read */ - avail = FIFO_WRITE_AVAIL(microphone->info.buffer); - fifo_size = MIN(microphone->info.stream_info.period_size, avail); - fifo_write(microphone->info.buffer, buf, fifo_size); - - /* Tell the main thread that it's okay to query the mic again */ - scond_signal(microphone->info.cond); - - /* Unlock the incoming sample queue (the main thread may resume) */ - slock_unlock(microphone->info.fifo_lock); - - /* If underrun, fill rest with silence. */ - memset(buf + fifo_size, 0, microphone->info.stream_info.period_size - fifo_size); - - frames = snd_pcm_readi(microphone->info.pcm, buf, microphone->info.stream_info.period_frames); - - if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) - { - if (snd_pcm_recover(microphone->info.pcm, frames, false) < 0) - { - RARCH_ERR("[ALSA] [capture thread %p]: Failed to recover from read error: %s\n", - thread_id, - snd_strerror(frames)); - break; - } - - continue; - } - else if (frames < 0) - { - RARCH_ERR("[ALSA] [capture thread %p]: Read error: %s.\n", - thread_id, - snd_strerror(frames)); - break; - } - } - -end: - slock_lock(microphone->info.cond_lock); - microphone->info.thread_dead = true; - scond_signal(microphone->info.cond); - slock_unlock(microphone->info.cond_lock); - free(buf); - RARCH_DBG("[ALSA] [capture thread %p]: Ending microphone worker thread\n", thread_id); -} - -static void alsa_thread_free_info_members(alsa_thread_info_t *info) +void alsa_thread_free_info_members(alsa_thread_info_t *info) { if (info) { @@ -225,16 +132,12 @@ static bool alsa_thread_use_float(void *data) return alsa->info.stream_info.has_float; } -static void alsa_thread_free_microphone(void *data, void *microphone_context); static void alsa_thread_free(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; if (alsa) { - if (alsa->microphone) - alsa_thread_free_microphone(alsa, alsa->microphone); - alsa_thread_free_info_members(&alsa->info); free(alsa); } @@ -386,223 +289,6 @@ static size_t alsa_thread_buffer_size(void *data) return alsa->info.stream_info.buffer_size; } -static void *alsa_thread_init_microphone(void *data, - const char *device, - unsigned rate, - unsigned latency, - unsigned block_frames, - unsigned *new_rate) -{ - alsa_thread_t *alsa = (alsa_thread_t*)data; - alsa_thread_microphone_t *microphone = NULL; - - if (!alsa) /* If we weren't given a valid ALSA context... */ - return NULL; - - microphone = calloc(1, sizeof(alsa_thread_microphone_t)); - - if (!microphone) - { /* If the microphone context couldn't be allocated... */ - RARCH_ERR("[ALSA] Failed to allocate microphone context\n"); - return NULL; - } - - if (alsa_init_pcm(µphone->info.pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->info.stream_info, new_rate, 0) < 0) - { - goto error; - } - - microphone->info.fifo_lock = slock_new(); - microphone->info.cond_lock = slock_new(); - microphone->info.cond = scond_new(); - microphone->info.buffer = fifo_new(microphone->info.stream_info.buffer_size); - if (!microphone->info.fifo_lock || !microphone->info.cond_lock || !microphone->info.cond || !microphone->info.buffer || !microphone->info.pcm) - goto error; - - microphone->info.worker_thread = sthread_create(alsa_microphone_worker_thread, microphone); - if (!microphone->info.worker_thread) - { - RARCH_ERR("[ALSA]: Failed to initialize microphone worker thread\n"); - goto error; - } - RARCH_DBG("[ALSA]: Initialized microphone worker thread\n"); - - alsa->microphone = microphone; - return microphone; - -error: - RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); - - if (microphone) - { - if (microphone->info.pcm) - { - snd_pcm_close(microphone->info.pcm); - } - - alsa_thread_free_microphone(alsa, microphone); - } - alsa->microphone = NULL; - return NULL; -} - -static void alsa_thread_free_microphone(void *data, void *microphone_context) -{ - alsa_thread_t *alsa = (alsa_thread_t*)data; - alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)microphone_context; - - if (alsa && microphone) - { - alsa_thread_free_info_members(µphone->info); - - alsa->microphone = NULL; - free(microphone); - } -} - -static bool alsa_thread_get_microphone_state(const void *data, const void *microphone_context) -{ - alsa_thread_t *alsa = (alsa_thread_t*)data; - alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t *)microphone_context; - - if (!alsa || !microphone) - return false; - - return !microphone->is_paused; -} - -static bool alsa_thread_set_microphone_state(void *data, void *microphone_context, bool enabled) -{ - alsa_thread_t *alsa = (alsa_thread_t*)data; - alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)microphone_context; - - if (!alsa || !microphone) - return false; - /* Both params must be non-null */ - - if (!microphone->info.stream_info.can_pause) - { - RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->info.pcm)); - return true; - } - - if (!alsa->is_paused) - { /* If the entire audio driver isn't paused... */ - if (alsa_set_mic_enabled_internal(microphone->info.pcm, enabled)) - { - // TODO: Do I need to synchronize microphone->is_paused? - microphone->is_paused = !enabled; - return true; - } - return false; - } - - return true; -} - -/** @see alsa_microphone_worker_thread() */ -static ssize_t alsa_thread_read_microphone(void *driver_context, void *microphone_context, void *buf, size_t size) -{ - alsa_thread_t *alsa = (alsa_thread_t*)driver_context; - alsa_thread_microphone_t *microphone = (alsa_thread_microphone_t*)microphone_context; - snd_pcm_state_t state; - - if (!alsa || !microphone || !buf) /* If any of the parameters were invalid... */ - return -1; - - if (microphone->info.thread_dead) /* If the mic thread is shutting down... */ - return -1; - - if (microphone->is_paused) - if (!alsa_thread_set_microphone_state(alsa, microphone, true)) - return -1; - - state = snd_pcm_state(microphone->info.pcm); - if (state != SND_PCM_STATE_RUNNING) - { - int errnum; - RARCH_WARN("[ALSA]: Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", - snd_pcm_name(microphone->info.pcm), - snd_pcm_state_name(state)); - - errnum = snd_pcm_start(microphone->info.pcm); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", - snd_pcm_name(microphone->info.pcm), - snd_strerror(errnum)); - - return -1; - } - } - - if (alsa->nonblock) - { /* If driver interactions shouldn't block... */ - size_t avail; - size_t write_amt; - - /* "Hey, I'm gonna borrow the queue." */ - slock_lock(microphone->info.fifo_lock); - - avail = FIFO_READ_AVAIL(microphone->info.buffer); - write_amt = MIN(avail, size); - - /* "It's okay if you don't have any new samples, I'll just check in on you later." */ - fifo_read(microphone->info.buffer, buf, write_amt); - - /* "Here, take this queue back." */ - slock_unlock(microphone->info.fifo_lock); - - return write_amt; - } - else - { - size_t written = 0; - while (written < size && !microphone->info.thread_dead) - { /* Until we've written all requested samples (or we're told to stop)... */ - size_t avail; - - /* "Hey, I'm gonna borrow the queue." */ - slock_lock(microphone->info.fifo_lock); - - avail = FIFO_READ_AVAIL(microphone->info.buffer); - - if (avail == 0) - { /* "Oh, wait, it's empty." */ - - /* "Here, take it back..." */ - slock_unlock(microphone->info.fifo_lock); - - /* "...I'll just wait right here." */ - slock_lock(microphone->info.cond_lock); - - /* "Unless we're closing up shop..." */ - if (!microphone->info.thread_dead) - /* "...let me know when you've produced some samples." */ - scond_wait(microphone->info.cond, microphone->info.cond_lock); - - /* "Oh, you're ready? Okay, I'm gonna continue." */ - slock_unlock(microphone->info.cond_lock); - } - else - { - size_t write_amt = MIN(size - written, avail); - - /* "I'll just go ahead and consume all these samples..." - * (As many as will fit in buf, or as many as are available.) */ - fifo_write(microphone->info.buffer,(const char*)buf + written, write_amt); - - /* "I'm done, you can take the queue back now." */ - slock_unlock(microphone->info.fifo_lock); - written += write_amt; - } - - /* "I'll be right back..." */ - } - return written; - } -} - audio_driver_t audio_alsathread = { alsa_thread_init, alsa_thread_write, @@ -616,10 +302,5 @@ audio_driver_t audio_alsathread = { alsa_device_list_new, /* Reusing these functions from alsa.c */ alsa_device_list_free, /* because they don't use the driver context */ alsa_thread_write_avail, - alsa_thread_buffer_size, - alsa_thread_init_microphone, - alsa_thread_free_microphone, - alsa_thread_get_microphone_state, - alsa_thread_set_microphone_state, - alsa_thread_read_microphone, + alsa_thread_buffer_size }; diff --git a/microphone/drivers/alsa.c b/microphone/drivers/alsa.c new file mode 100644 index 000000000000..7574d7879c6e --- /dev/null +++ b/microphone/drivers/alsa.c @@ -0,0 +1,475 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +#include "audio/drivers/alsa.h" /* For some common functions/types */ +#include "microphone/microphone_driver.h" +#include "verbosity.h" + + +#define BYTES_TO_FRAMES(bytes, frame_bits) ((bytes) * 8 / frame_bits) +#define FRAMES_TO_BYTES(frames, frame_bits) ((frames) * frame_bits / 8) + +typedef struct alsa_microphone_handle +{ + snd_pcm_t *pcm; + alsa_stream_info_t stream_info; + bool is_paused; +} alsa_microphone_handle_t; + +typedef struct alsa +{ + /* Only one microphone is supported right now; + * the driver state should track multiple microphone handles, + * but the driver *context* should track multiple microphone contexts */ + alsa_microphone_handle_t *microphone; + + bool nonblock; + bool is_paused; +} alsa_microphone_t; + +static void *alsa_microphone_init(void) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)calloc(1, sizeof(alsa_microphone_t)); + + if (!alsa) + { + RARCH_ERR("[ALSA]: Failed to allocate driver context\n"); + return NULL; + } + + RARCH_LOG("[ALSA]: Using ALSA version %s\n", snd_asoundlib_version()); + + return alsa; +} + +static void alsa_microphone_close_mic(void *driver_context, void *microphone_context); +static void alsa_microphone_free(void *driver_context) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + + if (alsa) + { + alsa_microphone_close_mic(alsa, alsa->microphone); + + snd_config_update_free_global(); + free(alsa); + } +} + +static bool alsa_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled); +static ssize_t alsa_microphone_read(void *driver_context, void *microphone_context, void *buf_, size_t size_) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + uint8_t *buf = (uint8_t*)buf_; + snd_pcm_sframes_t read = 0; + int errnum = 0; + snd_pcm_sframes_t size; + size_t frames_size; + snd_pcm_state_t state; + + if (!alsa || !microphone || !buf) + return -1; + + size = BYTES_TO_FRAMES(size_, microphone->stream_info.frame_bits); + frames_size = microphone->stream_info.has_float ? sizeof(float) : sizeof(int16_t); + + /* Workaround buggy menu code. + * If a read happens while we're paused, we might never progress. */ + if (microphone->is_paused) + if (!alsa_microphone_set_mic_active(alsa, microphone, true)) + return -1; + + state = snd_pcm_state(microphone->pcm); + if (state != SND_PCM_STATE_RUNNING) + { + RARCH_WARN("[ALSA]: Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", + snd_pcm_name(microphone->pcm), + snd_pcm_state_name(state)); + + errnum = snd_pcm_start(microphone->pcm); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", + snd_pcm_name(microphone->pcm), + snd_strerror(errnum)); + + return -1; + } + } + + if (alsa->nonblock) + { + while (size) + { + snd_pcm_sframes_t frames = snd_pcm_readi(microphone->pcm, buf, size); + + if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) + { + errnum = snd_pcm_recover(microphone->pcm, frames, 0); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to read from microphone: %s\n", snd_strerror(frames)); + RARCH_ERR("[ALSA]: Additionally, recovery failed with: %s\n", snd_strerror(errnum)); + return -1; + } + + break; + } + else if (frames == -EAGAIN) + break; + else if (frames < 0) + return -1; + + read += frames; + buf += frames_size; + size -= frames; + } + } + else + { + bool eagain_retry = true; + + while (size) + { + snd_pcm_sframes_t frames; + int rc = snd_pcm_wait(microphone->pcm, -1); + + if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR) + { + if (snd_pcm_recover(microphone->pcm, rc, 1) < 0) + return -1; + continue; + } + + frames = snd_pcm_readi(microphone->pcm, buf, size); + + if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) + { + if (snd_pcm_recover(microphone->pcm, frames, 1) < 0) + return -1; + + break; + } + else if (frames == -EAGAIN) + { + /* Definitely not supposed to happen. */ + if (eagain_retry) + { + eagain_retry = false; + continue; + } + break; + } + else if (frames < 0) + return -1; + + read += frames; + buf += frames_size; + size -= frames; + } + } + + return FRAMES_TO_BYTES(read, microphone->stream_info.frame_bits); +} + +static bool alsa_microphone_stop(void *driver_context) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + if (alsa->is_paused) + return true; + + if (alsa->microphone) + { + /* Stop the microphone independently of whether it's paused; + * note that upon alsa_microphone_start, the microphone won't resume + * if it was previously paused */ + int errnum = snd_pcm_pause(alsa->microphone->pcm, true); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to pause microphone \"%s\": %s\n", + snd_pcm_name(alsa->microphone->pcm), + snd_strerror(errnum)); + return false; + } + } + + return true; +} + +static bool alsa_microphone_start(void *driver_context, bool is_shutdown) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + if (!alsa->is_paused) + return true; + + alsa->is_paused = false; + + if (alsa->microphone && !alsa->microphone->is_paused) + { /* If the mic wasn't paused at the time the overall driver was paused... */ + snd_pcm_state_t mic_state = snd_pcm_state(alsa->microphone->pcm); + + /* If we're calling this function with a pending microphone, + * (as happens when a core requests a microphone at the start), + * the mic will be in the PREPARED state rather than the PAUSED state. + **/ + switch (mic_state) + { + case SND_PCM_STATE_PREPARED: + { + int errnum = snd_pcm_start(alsa->microphone->pcm); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", + snd_pcm_name(alsa->microphone->pcm), + snd_strerror(errnum)); + + return false; + } + break; + } + case SND_PCM_STATE_PAUSED: + { + int errnum = snd_pcm_pause(alsa->microphone->pcm, false); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to unpause microphone \"%s\" in state %s: %s\n", + snd_pcm_name(alsa->microphone->pcm), + snd_pcm_state_name(snd_pcm_state(alsa->microphone->pcm)), + snd_strerror(errnum)); + + return false; + } + break; + } + default: + { + RARCH_ERR("[ALSA]: Expected microphone \"%s\" to be in state PREPARED or PAUSED, it was in %s\n", + snd_pcm_name(alsa->microphone->pcm), + snd_pcm_state_name(mic_state)); + return false; + } + } + } + + return true; +} + +static bool alsa_microphone_alive(void *driver_context) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + if (!alsa) + return false; + return !alsa->is_paused; +} + +bool alsa_set_mic_enabled_internal(snd_pcm_t *microphone, bool enabled) +{ + snd_pcm_state_t microphone_state = snd_pcm_state(microphone); + int errnum = 0; + + if (enabled) + { /* If we're trying to unpause a mic (or maybe activate it for the first time)... */ + switch (microphone_state) + { + case SND_PCM_STATE_PAUSED: /* If we're unpausing a valid (but paused) mic... */ + if ((errnum = snd_pcm_pause(microphone, false)) < 0) + { /* ...but we failed... */ + goto error; + } + break; + case SND_PCM_STATE_PREPARED: + /* If we're activating this mic for the first time... */ + if ((errnum = snd_pcm_start(microphone)) < 0) + { /* ..but we failed... */ + goto error; + } + break; + default: + goto unexpected_state; + } + } + else + { /* We're pausing this mic */ + switch (microphone_state) + { + case SND_PCM_STATE_PREPARED: + case SND_PCM_STATE_RUNNING: + /* If we're pausing an active mic... */ + if ((errnum = snd_pcm_pause(microphone, true)) < 0) + { /* ...but we failed... */ + goto error; + } + break; + default: + goto unexpected_state; + } + } + + RARCH_DBG("[ALSA]: %s microphone \"%s\", transitioning from %s to %s\n", + enabled ? "Unpaused" : "Paused", + snd_pcm_name(microphone), + snd_pcm_state_name(microphone_state), + snd_pcm_state_name(snd_pcm_state(microphone))); + + return true; + error: + RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in state %s: %s\n", + enabled ? "unpause" : "pause", + snd_pcm_name(microphone), + snd_pcm_state_name(microphone_state), + snd_strerror(errnum)); + + return false; + + unexpected_state: + RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in unexpected state %s\n", + enabled ? "unpause" : "pause", + snd_pcm_name(microphone), + snd_pcm_state_name(microphone_state)); + + return false; +} + +static bool alsa_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + + if (!alsa || !microphone) + return false; + /* Both params must be non-null */ + + if (!microphone->stream_info.can_pause) + { + RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->pcm)); + return true; + } + + if (!alsa->is_paused) + { /* If the entire audio driver isn't paused... */ + if (alsa_set_mic_enabled_internal(microphone->pcm, enabled)) + { + microphone->is_paused = !enabled; + return true; + } + return false; + } + + return true; +} + +static bool alsa_microphone_get_mic_active(const void *driver_context, const void *microphone_context) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + + if (!alsa || !microphone) + return false; + /* Both params must be non-null */ + + return !microphone->is_paused; + /* The mic might be paused due to app requirements, + * or it might be stopped because the entire audio driver is stopped. */ +} + +static void alsa_microphone_set_nonblock_state(void *driver_context, bool nonblock) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + alsa->nonblock = nonblock; +} + +static struct string_list *alsa_microphone_device_list_new(const void *data) +{ + (void)data; + return alsa_device_list_type_new("Input"); +} + +static void alsa_microphone_device_list_free(const void *driver_context, struct string_list *devices) +{ + (void)driver_context; + string_list_free(devices); + /* Does nothing if devices is NULL */ +} + +static void *alsa_microphone_open_mic(void *driver_context, + const char *device, + unsigned rate, + unsigned latency, + unsigned block_frames, + unsigned *new_rate) +{ + alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + alsa_microphone_handle_t *microphone = NULL; + + if (!alsa) /* If we weren't given a valid ALSA context... */ + return NULL; + + microphone = calloc(1, sizeof(alsa_microphone_handle_t)); + + if (!microphone) /* If the microphone context couldn't be allocated... */ + return NULL; + + /* channels hardcoded to 1, because we only support mono mic input */ + if (alsa_init_pcm(µphone->pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->stream_info, new_rate, SND_PCM_NONBLOCK) < 0) + { + goto error; + } + + alsa->microphone = microphone; + return microphone; + +error: + RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); + + alsa_microphone_close_mic(alsa, microphone); + + return NULL; + +} +static void alsa_microphone_close_mic(void *driver_context, void *microphone_context) +{ + alsa_microphone_t *alsa = (alsa_microphone_t *)driver_context; + alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + + if (alsa && microphone) + { + alsa_free_pcm(microphone->pcm); + + alsa->microphone = NULL; + free(microphone); + } +} + +microphone_driver_t microphone_alsa = { + alsa_microphone_init, + alsa_microphone_free, + alsa_microphone_read, + alsa_microphone_stop, + alsa_microphone_start, + alsa_microphone_alive, + alsa_microphone_set_mic_active, + alsa_microphone_get_mic_active, + alsa_microphone_set_nonblock_state, + "alsa", + alsa_microphone_device_list_new, + alsa_microphone_device_list_free, + alsa_microphone_open_mic, + alsa_microphone_close_mic +}; diff --git a/microphone/drivers/alsathread.c b/microphone/drivers/alsathread.c new file mode 100644 index 000000000000..d0bc6e7f8f0a --- /dev/null +++ b/microphone/drivers/alsathread.c @@ -0,0 +1,451 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +#include "audio/drivers/alsa.h" /* For some common functions/types */ +#include "microphone/microphone_driver.h" +#include "verbosity.h" +#include "retro_assert.h" + + +typedef struct alsa_thread_microphone_handle +{ + alsa_thread_info_t info; + bool is_paused; +} alsa_thread_microphone_handle_t; + +typedef struct alsa_thread +{ + /* Only one microphone is supported right now; + * the driver state should track multiple microphone handles, + * but the driver *context* should track multiple microphone contexts */ + alsa_thread_microphone_handle_t *microphone; + bool nonblock; + bool is_paused; +} alsa_thread_microphone_t; + +static void *alsa_thread_microphone_init(void) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)calloc(1, sizeof(alsa_thread_microphone_t)); + + if (!alsa) + { + RARCH_ERR("[ALSA] Failed to allocate driver context\n"); + return NULL; + } + + RARCH_LOG("[ALSA] Using ALSA version %s\n", snd_asoundlib_version()); + + return alsa; +} + +static void alsa_thread_microphone_close_mic(void *driver_context, void *microphone_context); +static void alsa_thread_microphone_free(void *driver_context) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + + if (alsa) + { + if (alsa->microphone) + alsa_thread_microphone_close_mic(alsa, alsa->microphone); + + free(alsa); + } +} + +/** @see alsa_thread_read_microphone() */ +static void alsa_microphone_worker_thread(void *microphone_context) +{ + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + uint8_t *buf = NULL; + uintptr_t thread_id = sthread_get_current_thread_id(); + + retro_assert(microphone != NULL); + buf = (uint8_t *)calloc(1, microphone->info.stream_info.period_size); + if (!buf) + { + RARCH_ERR("[ALSA] [capture thread %p]: Failed to allocate audio buffer\n", thread_id); + goto end; + } + + RARCH_DBG("[ALSA] [capture thread %p]: Beginning microphone worker thread\n", thread_id); + RARCH_DBG("[ALSA] [capture thread %p]: Microphone \"%s\" is in state %s\n", + thread_id, + snd_pcm_name(microphone->info.pcm), + snd_pcm_state_name(snd_pcm_state(microphone->info.pcm))); + + while (!microphone->info.thread_dead) + { /* Until we're told to stop... */ + size_t avail; + size_t fifo_size; + snd_pcm_sframes_t frames; + int errnum = 0; + + /* Lock the incoming sample queue (the main thread may block) */ + slock_lock(microphone->info.fifo_lock); + + /* Fill the incoming sample queue with whatever we recently read */ + avail = FIFO_WRITE_AVAIL(microphone->info.buffer); + fifo_size = MIN(microphone->info.stream_info.period_size, avail); + fifo_write(microphone->info.buffer, buf, fifo_size); + + /* Tell the main thread that it's okay to query the mic again */ + scond_signal(microphone->info.cond); + + /* Unlock the incoming sample queue (the main thread may resume) */ + slock_unlock(microphone->info.fifo_lock); + + /* If underrun, fill rest with silence. */ + memset(buf + fifo_size, 0, microphone->info.stream_info.period_size - fifo_size); + + errnum = snd_pcm_wait(microphone->info.pcm, -1); + + if (errnum == -EPIPE || errnum == -ESTRPIPE || errnum == -EINTR) + { + RARCH_WARN("[ALSA] [capture thread %p]: Wait error: %s\n", + thread_id, + snd_strerror(errnum)); + + if ((errnum = snd_pcm_recover(microphone->info.pcm, errnum, false)) < 0) + { + RARCH_ERR("[ALSA] [capture thread %p]: Failed to recover from prior wait error: %s\n", + thread_id, + snd_strerror(errnum)); + + break; + } + + continue; + } + + frames = snd_pcm_readi(microphone->info.pcm, buf, microphone->info.stream_info.period_frames); + + if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE) + { + RARCH_WARN("[ALSA] [capture thread %p]: Read error: %s\n", + thread_id, + snd_strerror(frames)); + + if ((errnum = snd_pcm_recover(microphone->info.pcm, frames, false)) < 0) + { + RARCH_ERR("[ALSA] [capture thread %p]: Failed to recover from prior read error: %s\n", + thread_id, + snd_strerror(errnum)); + break; + } + + continue; + } + else if (frames < 0) + { + RARCH_ERR("[ALSA] [capture thread %p]: Read error: %s.\n", + thread_id, + snd_strerror(frames)); + break; + } + } + + end: + slock_lock(microphone->info.cond_lock); + microphone->info.thread_dead = true; + scond_signal(microphone->info.cond); + slock_unlock(microphone->info.cond_lock); + free(buf); + RARCH_DBG("[ALSA] [capture thread %p]: Ending microphone worker thread\n", thread_id); +} + +static bool alsa_thread_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled); +static ssize_t alsa_thread_microphone_read(void *driver_context, void *microphone_context, void *buf, size_t size) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + snd_pcm_state_t state; + + if (!alsa || !microphone || !buf) /* If any of the parameters were invalid... */ + return -1; + + if (microphone->info.thread_dead) /* If the mic thread is shutting down... */ + return -1; + + if (microphone->is_paused) + if (!alsa_thread_microphone_set_mic_active(alsa, microphone, true)) + return -1; + + state = snd_pcm_state(microphone->info.pcm); + if (state != SND_PCM_STATE_RUNNING) + { + int errnum; + RARCH_WARN("[ALSA]: Expected microphone \"%s\" to be in state RUNNING, was in state %s\n", + snd_pcm_name(microphone->info.pcm), + snd_pcm_state_name(state)); + + errnum = snd_pcm_start(microphone->info.pcm); + if (errnum < 0) + { + RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", + snd_pcm_name(microphone->info.pcm), + snd_strerror(errnum)); + + return -1; + } + } + + if (alsa->nonblock) + { /* If driver interactions shouldn't block... */ + size_t avail; + size_t write_amt; + + /* "Hey, I'm gonna borrow the queue." */ + slock_lock(microphone->info.fifo_lock); + + avail = FIFO_READ_AVAIL(microphone->info.buffer); + write_amt = MIN(avail, size); + + /* "It's okay if you don't have any new samples, I'll just check in on you later." */ + fifo_read(microphone->info.buffer, buf, write_amt); + + /* "Here, take this queue back." */ + slock_unlock(microphone->info.fifo_lock); + + return write_amt; + } + else + { + size_t written = 0; + while (written < size && !microphone->info.thread_dead) + { /* Until we've written all requested samples (or we're told to stop)... */ + size_t avail; + + /* "Hey, I'm gonna borrow the queue." */ + slock_lock(microphone->info.fifo_lock); + + avail = FIFO_READ_AVAIL(microphone->info.buffer); + + if (avail == 0) + { /* "Oh, wait, it's empty." */ + + /* "Here, take it back..." */ + slock_unlock(microphone->info.fifo_lock); + + /* "...I'll just wait right here." */ + slock_lock(microphone->info.cond_lock); + + /* "Unless we're closing up shop..." */ + if (!microphone->info.thread_dead) + /* "...let me know when you've produced some samples." */ + scond_wait(microphone->info.cond, microphone->info.cond_lock); + + /* "Oh, you're ready? Okay, I'm gonna continue." */ + slock_unlock(microphone->info.cond_lock); + } + else + { + size_t write_amt = MIN(size - written, avail); + + /* "I'll just go ahead and consume all these samples..." + * (As many as will fit in buf, or as many as are available.) */ + fifo_write(microphone->info.buffer,(const char*)buf + written, write_amt); + + /* "I'm done, you can take the queue back now." */ + slock_unlock(microphone->info.fifo_lock); + written += write_amt; + } + + /* "I'll be right back..." */ + } + return written; + } +} + +static bool alsa_thread_microphone_start(void *driver_context, bool is_shutdown) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + + if (alsa) + alsa->is_paused = false; + return true; +} + +static bool alsa_thread_microphone_alive(void *driver_context) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + if (!alsa) + return false; + return !alsa->is_paused; +} + +static bool alsa_thread_microphone_stop(void *driver_context) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + + if (alsa) + alsa->is_paused = true; + return true; +} + + +static void alsa_thread_microphone_close_mic(void *driver_context, void *microphone_context); +static void *alsa_thread_microphone_open_mic(void *driver_context, + const char *device, + unsigned rate, + unsigned latency, + unsigned block_frames, + unsigned *new_rate) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + alsa_thread_microphone_handle_t *microphone = NULL; + + if (!alsa) /* If we weren't given a valid ALSA context... */ + return NULL; + + microphone = calloc(1, sizeof(alsa_thread_microphone_handle_t)); + + if (!microphone) + { /* If the microphone context couldn't be allocated... */ + RARCH_ERR("[ALSA] Failed to allocate microphone context\n"); + return NULL; + } + + if (alsa_init_pcm(µphone->info.pcm, device, SND_PCM_STREAM_CAPTURE, rate, latency, 1, µphone->info.stream_info, new_rate, 0) < 0) + { + goto error; + } + + microphone->info.fifo_lock = slock_new(); + microphone->info.cond_lock = slock_new(); + microphone->info.cond = scond_new(); + microphone->info.buffer = fifo_new(microphone->info.stream_info.buffer_size); + if (!microphone->info.fifo_lock || !microphone->info.cond_lock || !microphone->info.cond || !microphone->info.buffer || !microphone->info.pcm) + goto error; + + microphone->info.worker_thread = sthread_create(alsa_microphone_worker_thread, microphone); + if (!microphone->info.worker_thread) + { + RARCH_ERR("[ALSA]: Failed to initialize microphone worker thread\n"); + goto error; + } + RARCH_DBG("[ALSA]: Initialized microphone worker thread\n"); + + alsa->microphone = microphone; + return microphone; + + error: + RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); + + if (microphone) + { + if (microphone->info.pcm) + { + snd_pcm_close(microphone->info.pcm); + } + + alsa_thread_microphone_close_mic(alsa, microphone); + } + alsa->microphone = NULL; + return NULL; +} + +static void alsa_thread_microphone_close_mic(void *driver_context, void *microphone_context) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t *)driver_context; + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + + if (alsa && microphone) + { + alsa_thread_free_info_members(µphone->info); + + alsa->microphone = NULL; + free(microphone); + } +} + +static bool alsa_thread_microphone_get_mic_active(const void *driver_context, const void *microphone_context) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t *)microphone_context; + + if (!alsa || !microphone) + return false; + + return !microphone->is_paused; +} + +static bool alsa_thread_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + + if (!alsa || !microphone) + return false; + /* Both params must be non-null */ + + if (!microphone->info.stream_info.can_pause) + { + RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->info.pcm)); + return true; + } + + if (!alsa->is_paused) + { /* If the entire audio driver isn't paused... */ + if (alsa_set_mic_enabled_internal(microphone->info.pcm, enabled)) + { + // TODO: Do I need to synchronize microphone->is_paused? + microphone->is_paused = !enabled; + return true; + } + return false; + } + + return true; +} + +static void alsa_thread_microphone_set_nonblock_state(void *driver_context, bool state) +{ + alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; + alsa->nonblock = state; +} + +static struct string_list *alsa_thread_microphone_device_list_new(const void *data) +{ + (void)data; + return alsa_device_list_type_new("Input"); +} + +static void alsa_thread_microphone_device_list_free(const void *driver_context, struct string_list *devices) +{ + (void)driver_context; + string_list_free(devices); + /* Does nothing if devices is NULL */ +} + +microphone_driver_t microphone_alsathread = { + alsa_thread_microphone_init, + alsa_thread_microphone_free, + alsa_thread_microphone_read, + alsa_thread_microphone_stop, + alsa_thread_microphone_start, + alsa_thread_microphone_alive, + alsa_thread_microphone_set_mic_active, + alsa_thread_microphone_get_mic_active, + alsa_thread_microphone_set_nonblock_state, + "alsathread", + alsa_thread_microphone_device_list_new, + alsa_thread_microphone_device_list_free, + alsa_thread_microphone_open_mic, + alsa_thread_microphone_close_mic +}; From 282e64d5915581ffff5918f8317f13d34f5f350c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Jan 2023 21:53:50 -0500 Subject: [PATCH 129/309] Fix an extra struct member --- audio/audio_driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 5142640caf93..cc0775c6cd58 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -76,8 +76,7 @@ audio_driver_t audio_null = { NULL, NULL, NULL, /* write_avail */ - NULL, /* read_avail */ - NULL + NULL /* buffer_size */ }; audio_driver_t *audio_drivers[] = { From 5c054670e94e761e82ec473d2f318228c84d2927 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 12:21:58 -0500 Subject: [PATCH 130/309] Add a setting for the mic driver --- configuration.c | 61 +++++++++++++++++++++++++++++++++- configuration.h | 9 +++++ intl/msg_hash_lbl.h | 4 +++ list_special.h | 1 + menu/cbs/menu_cbs_get_value.c | 1 + menu/cbs/menu_cbs_sublabel.c | 4 +++ menu/menu_displaylist.c | 1 + menu/menu_setting.c | 9 +++++ microphone/microphone_driver.c | 17 +++++++--- msg_hash.h | 1 + retroarch.c | 9 +++++ retroarch.h | 9 +++++ 12 files changed, 121 insertions(+), 5 deletions(-) diff --git a/configuration.c b/configuration.c index 2bf49a88a4b4..6780411f1b5d 100644 --- a/configuration.c +++ b/configuration.c @@ -139,9 +139,18 @@ enum audio_driver_enum AUDIO_NULL }; +enum microphone_driver_enum +{ + MICROPHONE_ALSA = AUDIO_NULL + 1, + MICROPHONE_ALSATHREAD, + MICROPHONE_SDL2, + MICROPHONE_WASAPI, + MICROPHONE_NULL, +}; + enum audio_resampler_driver_enum { - AUDIO_RESAMPLER_CC = AUDIO_NULL + 1, + AUDIO_RESAMPLER_CC = MICROPHONE_NULL + 1, AUDIO_RESAMPLER_SINC, AUDIO_RESAMPLER_NEAREST, AUDIO_RESAMPLER_NULL @@ -518,6 +527,21 @@ static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_EXT; static const enum audio_driver_enum AUDIO_DEFAULT_DRIVER = AUDIO_NULL; #endif +#if (defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && defined(HAVE_WASAPI) +/* The default mic driver on Windows is WASAPI if it's available. */ +static const enum microphone_driver_enum MICROPHONE_DEFAULT_DRIVER = MICROPHONE_WASAPI; +#elif defined(HAVE_ALSA) && defined(HAVE_THREADS) +/* The default mic driver on Linux is the threaded ALSA driver, if available. */ +static const enum microphone_driver_enum MICROPHONE_DEFAULT_DRIVER = MICROPHONE_ALSATHREAD; +#elif defined(HAVE_ALSA) +static const enum microphone_driver_enum MICROPHONE_DEFAULT_DRIVER = MICROPHONE_ALSA; +#elif defined(HAVE_SDL2) +/* The default fallback driver is SDL2, if available. */ +static const enum microphone_driver_enum MICROPHONE_DEFAULT_DRIVER = MICROPHONE_SDL2; +#else +static const enum microphone_driver_enum MICROPHONE_DEFAULT_DRIVER = MICROPHONE_NULL; +#endif + #if defined(RS90) || defined(MIYOO) static const enum audio_resampler_driver_enum AUDIO_DEFAULT_RESAMPLER_DRIVER = AUDIO_RESAMPLER_NEAREST; #elif defined(PSP) || defined(EMSCRIPTEN) @@ -903,6 +927,35 @@ const char *config_get_default_audio(void) return "null"; } +/** + * config_get_default_microphone: + * + * Gets default microphone driver. + * + * Returns: Default microphone driver. + **/ +const char *config_get_default_microphone(void) +{ + enum microphone_driver_enum default_driver = MICROPHONE_DEFAULT_DRIVER; + + switch (default_driver) + { + case MICROPHONE_ALSA: + return "alsa"; + case MICROPHONE_ALSATHREAD: + return "alsathread"; + case MICROPHONE_WASAPI: + return "wasapi"; + case MICROPHONE_SDL2: + return "sdl2"; + case MICROPHONE_NULL: + break; + } + + return "null"; +} + + const char *config_get_default_record(void) { enum record_driver_enum default_driver = RECORD_DEFAULT_DRIVER; @@ -1427,6 +1480,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings #endif SETTING_ARRAY("video_context_driver", settings->arrays.video_context_driver, false, NULL, true); SETTING_ARRAY("audio_driver", settings->arrays.audio_driver, false, NULL, true); + SETTING_ARRAY("microphone_driver", settings->arrays.microphone_driver, false, NULL, true); SETTING_ARRAY("audio_resampler", settings->arrays.audio_resampler, false, NULL, true); SETTING_ARRAY("input_driver", settings->arrays.input_driver, false, NULL, true); SETTING_ARRAY("input_joypad_driver", settings->arrays.input_joypad_driver, false, NULL, true); @@ -2513,6 +2567,7 @@ void config_set_defaults(void *data) int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder); const char *def_video = config_get_default_video(); const char *def_audio = config_get_default_audio(); + const char *def_microphone = config_get_default_microphone(); const char *def_audio_resampler = config_get_default_audio_resampler(); const char *def_input = config_get_default_input(); const char *def_joypad = config_get_default_joypad(); @@ -2616,6 +2671,10 @@ void config_set_defaults(void *data) configuration_set_string(settings, settings->arrays.audio_driver, def_audio); + if (def_microphone) + configuration_set_string(settings, + settings->arrays.microphone_driver, + def_microphone); if (def_audio_resampler) configuration_set_string(settings, settings->arrays.audio_resampler, diff --git a/configuration.h b/configuration.h index 2cbd3ebed7bf..92679add9b55 100644 --- a/configuration.h +++ b/configuration.h @@ -1039,6 +1039,15 @@ const char *config_get_default_video(void); **/ const char *config_get_default_audio(void); +/** + * config_get_default_microphone: + * + * Gets default microphone driver. + * + * Returns: Default microphone driver. + **/ +const char *config_get_default_microphone(void); + /** * config_get_default_audio_resampler: * diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 65773ee98923..22e1263a9a3f 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -222,6 +222,10 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH, "audio_wasapi_sh_buffer_length" ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_DRIVER, + "microphone_driver" + ) MSG_HASH( MENU_ENUM_LABEL_AUTOSAVE_INTERVAL, "autosave_interval" diff --git a/list_special.h b/list_special.h index 671b66ad28c1..c4b1a336c691 100644 --- a/list_special.h +++ b/list_special.h @@ -48,6 +48,7 @@ enum string_list_type STRING_LIST_WIFI_DRIVERS, STRING_LIST_LOCATION_DRIVERS, STRING_LIST_AUDIO_DRIVERS, + STRING_LIST_MICROPHONE_DRIVERS, STRING_LIST_AUDIO_RESAMPLER_DRIVERS, STRING_LIST_VIDEO_DRIVERS, STRING_LIST_INPUT_DRIVERS, diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 45b88b0c2ede..8174347e4d11 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -1841,6 +1841,7 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( { case MENU_ENUM_LABEL_VIDEO_DRIVER: case MENU_ENUM_LABEL_AUDIO_DRIVER: + case MENU_ENUM_LABEL_MICROPHONE_DRIVER: case MENU_ENUM_LABEL_INPUT_DRIVER: case MENU_ENUM_LABEL_JOYPAD_DRIVER: case MENU_ENUM_LABEL_AUDIO_RESAMPLER_DRIVER: diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 5dcd178577a6..7e8ab662a928 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -755,6 +755,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_remember_selection, MENU_ #endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_driver, MENU_ENUM_SUBLABEL_VIDEO_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_driver, MENU_ENUM_SUBLABEL_AUDIO_DRIVER) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_driver, MENU_ENUM_SUBLABEL_MICROPHONE_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_driver, MENU_ENUM_SUBLABEL_INPUT_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_joypad_driver, MENU_ENUM_SUBLABEL_JOYPAD_DRIVER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_resampler_driver, MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_DRIVER) @@ -3603,6 +3604,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_DRIVER: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_driver); break; + case MENU_ENUM_LABEL_MICROPHONE_DRIVER: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_driver); + break; case MENU_ENUM_LABEL_VIDEO_DRIVER: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_driver); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 094602afbe4f..bd1af253990a 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -10221,6 +10221,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_MENU_DRIVER, PARSE_ONLY_STRING_OPTIONS}, {MENU_ENUM_LABEL_VIDEO_DRIVER, PARSE_ONLY_STRING_OPTIONS}, {MENU_ENUM_LABEL_AUDIO_DRIVER, PARSE_ONLY_STRING_OPTIONS}, + {MENU_ENUM_LABEL_MICROPHONE_DRIVER, PARSE_ONLY_STRING_OPTIONS}, #if 0 /* This is better suited under audio options only */ {MENU_ENUM_LABEL_AUDIO_RESAMPLER_DRIVER,PARSE_ONLY_STRING_OPTIONS}, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index c8bd064b686b..99d2bd84b361 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -10481,6 +10481,15 @@ static bool setting_append_list( j++; + string_options_entries[j].target = settings->arrays.microphone_driver; + string_options_entries[j].len = sizeof(settings->arrays.microphone_driver); + string_options_entries[j].name_enum_idx = MENU_ENUM_LABEL_MICROPHONE_DRIVER; + string_options_entries[j].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_MICROPHONE_DRIVER; + string_options_entries[j].default_value = config_get_default_microphone(); + string_options_entries[j].values = config_get_microphone_driver_options(); + + j++; + string_options_entries[j].target = settings->arrays.audio_resampler; string_options_entries[j].len = sizeof(settings->arrays.audio_resampler); string_options_entries[j].name_enum_idx = MENU_ENUM_LABEL_AUDIO_RESAMPLER_DRIVER; diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 6212feb9a732..dc60c0eecd7c 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -22,6 +22,7 @@ #include "memalign.h" #include "audio/conversion/s16_to_float.h" #include "audio/conversion/float_to_s16.h" +#include "list_special.h" static microphone_driver_state_t mic_driver_st = {0}; /* double alignment */ @@ -68,6 +69,18 @@ unsigned mic_driver_get_sample_size(void) return (mic_st->flags & MICROPHONE_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t); } +/** + * config_get_microphone_driver_options: + * + * Get an enumerated list of all microphone driver names, separated by '|'. + * + * Returns: string listing of all microphone driver names, separated by '|'. + **/ +const char *config_get_microphone_driver_options(void) +{ + return char_list_new_special(STRING_LIST_MICROPHONE_DRIVERS, NULL); +} + bool microphone_driver_find_driver( void *settings_data, const char *prefix, @@ -157,14 +170,10 @@ bool microphone_driver_init_internal(void *settings_data) { float *in_samples_buf = NULL; settings_t *settings = (settings_t*)settings_data; - size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2; bool audio_enable_microphone = settings->bools.audio_enable_input; - bool audio_sync = settings->bools.audio_sync; - bool audio_rate_control = settings->bools.audio_rate_control; float slowmotion_ratio = settings->floats.slowmotion_ratio; size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; int16_t *in_conv_buf = (int16_t*)memalign_alloc(64, insamples_max * sizeof(int16_t)); - size_t audio_buf_length = AUDIO_CHUNK_SIZE_NONBLOCKING * 2 * sizeof(float); bool verbosity_enabled = verbosity_is_enabled(); convert_s16_to_float_init_simd(); diff --git a/msg_hash.h b/msg_hash.h index 5c2d0a423e46..21ad467d4438 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2339,6 +2339,7 @@ enum msg_hash_enums /* Driver settings */ MENU_LABEL(AUDIO_DRIVER), + MENU_LABEL(MICROPHONE_DRIVER), MENU_LABEL(JOYPAD_DRIVER), MENU_LABEL(MENU_DRIVER), MENU_LABEL(LOCATION_DRIVER), diff --git a/retroarch.c b/retroarch.c index 8ef4bbbec62b..988d4c6e0b90 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1439,6 +1439,15 @@ struct string_list *string_list_new_special(enum string_list_type type, string_list_append(s, opt, attr); } break; + case STRING_LIST_MICROPHONE_DRIVERS: + for (i = 0; microphone_drivers[i]; i++) + { + const char *opt = microphone_drivers[i]->ident; + *len += strlen(opt) + 1; + + string_list_append(s, opt, attr); + } + break; case STRING_LIST_AUDIO_RESAMPLER_DRIVERS: for (i = 0; audio_resampler_driver_find_handle(i); i++) { diff --git a/retroarch.h b/retroarch.h index 2e91f8c4bc6d..d403a00168b1 100644 --- a/retroarch.h +++ b/retroarch.h @@ -141,6 +141,15 @@ void rarch_favorites_deinit(void); **/ const char* config_get_audio_driver_options(void); +/** + * config_get_microphone_driver_options: + * + * Get an enumerated list of all microphone driver names, separated by '|'. + * + * Returns: string listing of all microphone driver names, separated by '|'. + **/ +const char* config_get_microphone_driver_options(void); + /* Camera */ unsigned int retroarch_get_rotation(void); From 9d4297c4a61c13593d4a82c22c2f3dae4f412835 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 13:12:00 -0500 Subject: [PATCH 131/309] Add a command to reinitialize the microphone driver --- command.h | 2 ++ retroarch.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/command.h b/command.h index fcced8bb95a2..e33e8529af9b 100644 --- a/command.h +++ b/command.h @@ -231,6 +231,8 @@ enum event_command CMD_EVENT_NETPLAY_HOST_TOGGLE, /* Reinitializes audio driver. */ CMD_EVENT_AUDIO_REINIT, + /* Reinitializes microphone driver. */ + CMD_EVENT_MICROPHONE_REINIT, /* Resizes windowed scale. Will reinitialize video driver. */ CMD_EVENT_RESIZE_WINDOWED_SCALE, /* Toggles disk eject. */ diff --git a/retroarch.c b/retroarch.c index 988d4c6e0b90..350dd7bb0133 100644 --- a/retroarch.c +++ b/retroarch.c @@ -3180,6 +3180,10 @@ bool command_event(enum event_command cmd, void *data) audio_driver_load_system_sounds(); #endif break; + case CMD_EVENT_MICROPHONE_REINIT: + driver_uninit(DRIVER_MICROPHONE_MASK); + drivers_init(settings, DRIVER_MICROPHONE_MASK, verbosity_is_enabled()); + break; case CMD_EVENT_SHUTDOWN: #if defined(__linux__) && !defined(ANDROID) if (settings->bools.config_save_on_exit) From 4e997deeb86e40a00da8abbe5d777101b7ce846d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 13:13:23 -0500 Subject: [PATCH 132/309] Rename mic-related settings --- config.def.h | 2 +- configuration.c | 16 ++++----- configuration.h | 8 ++--- intl/msg_hash_lbl.h | 44 +++++++++++------------ intl/msg_hash_us.c | 1 - intl/msg_hash_us.h | 46 ++++++++++++------------ menu/cbs/menu_cbs_deferred_push.c | 8 ++--- menu/cbs/menu_cbs_ok.c | 10 +++--- menu/cbs/menu_cbs_sublabel.c | 28 +++++++-------- menu/cbs/menu_cbs_title.c | 4 +-- menu/drivers/materialui.c | 2 +- menu/menu_cbs.h | 2 +- menu/menu_displaylist.c | 25 +++++-------- menu/menu_displaylist.h | 2 +- menu/menu_setting.c | 59 +++++++++++-------------------- microphone/microphone_driver.c | 12 +++---- msg_hash.h | 15 ++++---- 17 files changed, 124 insertions(+), 160 deletions(-) diff --git a/config.def.h b/config.def.h index e62fec68ccf6..7ef6634f7afb 100644 --- a/config.def.h +++ b/config.def.h @@ -1076,7 +1076,7 @@ /* Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults. */ #define DEFAULT_AUDIO_DEVICE NULL -#define DEFAULT_AUDIO_INPUT_DEVICE NULL +#define DEFAULT_MICROPHONE_DEVICE NULL /* Desired audio latency in milliseconds. Might not be honored * if driver can't provide given latency. */ diff --git a/configuration.c b/configuration.c index 6780411f1b5d..c99c986ebd8c 100644 --- a/configuration.c +++ b/configuration.c @@ -1469,7 +1469,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings SETTING_ARRAY("menu_driver", settings->arrays.menu_driver, false, NULL, true); #endif SETTING_ARRAY("audio_device", settings->arrays.audio_device, false, NULL, true); - SETTING_ARRAY("audio_input_device", settings->arrays.audio_input_device, false, NULL, true); + SETTING_ARRAY("microphone_device", settings->arrays.microphone_device, false, NULL, true); SETTING_ARRAY("camera_device", settings->arrays.camera_device, false, NULL, true); #ifdef HAVE_CHEEVOS SETTING_ARRAY("cheevos_custom_host", settings->arrays.cheevos_custom_host, false, NULL, true); @@ -2245,10 +2245,10 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("input_rumble_gain", &settings->uints.input_rumble_gain, true, DEFAULT_RUMBLE_GAIN, false); SETTING_UINT("input_auto_game_focus", &settings->uints.input_auto_game_focus, true, DEFAULT_INPUT_AUTO_GAME_FOCUS, false); SETTING_UINT("audio_latency", &settings->uints.audio_latency, false, 0 /* TODO */, false); - SETTING_UINT("audio_input_latency", &settings->uints.audio_input_latency, false, 0 /* TODO */, false); + SETTING_UINT("microphone_latency", &settings->uints.microphone_latency, false, 0 /* TODO */, false); SETTING_UINT("audio_resampler_quality", &settings->uints.audio_resampler_quality, true, DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL, false); SETTING_UINT("audio_block_frames", &settings->uints.audio_block_frames, true, 0, false); - SETTING_UINT("audio_input_block_frames", &settings->uints.audio_input_block_frames, true, 0, false); + SETTING_UINT("microphone_block_frames", &settings->uints.microphone_block_frames, true, 0, false); #ifdef ANDROID SETTING_UINT("input_block_timeout", &settings->uints.input_block_timeout, true, 0, false); #endif @@ -2350,7 +2350,7 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("cheevos_visibility_summary", &settings->uints.cheevos_visibility_summary, true, DEFAULT_CHEEVOS_VISIBILITY_SUMMARY, false); #endif SETTING_UINT("audio_out_rate", &settings->uints.audio_output_sample_rate, true, DEFAULT_OUTPUT_RATE, false); - SETTING_UINT("audio_in_rate", &settings->uints.audio_input_sample_rate, true, DEFAULT_INPUT_RATE, false); + SETTING_UINT("microphone_rate", &settings->uints.microphone_sample_rate, true, DEFAULT_INPUT_RATE, false); SETTING_UINT("custom_viewport_width", &settings->video_viewport_custom.width, false, 0 /* TODO */, false); SETTING_UINT("crt_switch_resolution_super", &settings->uints.crt_switch_resolution_super, true, DEFAULT_CRT_SWITCH_RESOLUTION_SUPER, false); SETTING_UINT("custom_viewport_height", &settings->video_viewport_custom.height, false, 0 /* TODO */, false); @@ -2745,10 +2745,10 @@ void config_set_defaults(void *data) settings->arrays.audio_device, DEFAULT_AUDIO_DEVICE); - if (DEFAULT_AUDIO_INPUT_DEVICE) + if (DEFAULT_MICROPHONE_DEVICE) configuration_set_string(settings, - settings->arrays.audio_input_device, - DEFAULT_AUDIO_INPUT_DEVICE); + settings->arrays.microphone_device, + DEFAULT_MICROPHONE_DEVICE); if (!g_defaults.settings_out_latency) g_defaults.settings_out_latency = DEFAULT_OUT_LATENCY; @@ -2758,7 +2758,7 @@ void config_set_defaults(void *data) if (!g_defaults.settings_in_latency) g_defaults.settings_in_latency = DEFAULT_IN_LATENCY; - settings->uints.audio_input_latency = g_defaults.settings_in_latency; + settings->uints.microphone_latency = g_defaults.settings_in_latency; audio_set_float(AUDIO_ACTION_VOLUME_GAIN, settings->floats.audio_volume); #ifdef HAVE_AUDIOMIXER diff --git a/configuration.h b/configuration.h index 92679add9b55..b462f4c69896 100644 --- a/configuration.h +++ b/configuration.h @@ -156,11 +156,11 @@ typedef struct settings unsigned led_map[MAX_LEDS]; unsigned audio_output_sample_rate; - unsigned audio_input_sample_rate; + unsigned microphone_sample_rate; unsigned audio_block_frames; - unsigned audio_input_block_frames; + unsigned microphone_block_frames; unsigned audio_latency; - unsigned audio_input_latency; + unsigned microphone_latency; unsigned fps_update_interval; unsigned memory_update_interval; @@ -444,7 +444,7 @@ typedef struct settings #endif char audio_device[255]; - char audio_input_device[255]; + char microphone_device[255]; char camera_device[255]; char netplay_mitm_server[255]; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 22e1263a9a3f..71e6792363dd 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -106,17 +106,13 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_BLOCK_FRAMES, "audio_block_frames" ) -MSG_HASH( - MENU_ENUM_LABEL_AUDIO_INPUT_BLOCK_FRAMES, - "audio_input_block_frames" - ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_DEVICE, "audio_device" ) MSG_HASH( - MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE, - "audio_input_device" + MENU_ENUM_LABEL_MICROPHONE_DEVICE, + "microphone_device" ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_DRIVER, @@ -134,10 +130,6 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_ENABLE, "audio_enable" ) -MSG_HASH( - MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, - "audio_enable_microphone" - ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_FILTER_DIR, "audio_filter_dir" @@ -150,10 +142,6 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_LATENCY, "audio_latency" ) -MSG_HASH( - MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, - "audio_input_latency" - ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_MAX_TIMING_SKEW, "audio_max_timing_skew" @@ -170,10 +158,6 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE, "audio_output_rate" ) -MSG_HASH( - MENU_ENUM_LABEL_AUDIO_INPUT_RATE, - "audio_input_rate" -) MSG_HASH( MENU_ENUM_LABEL_AUDIO_RATE_CONTROL_DELTA, "audio_rate_control_delta" @@ -198,10 +182,6 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS, "audio_output_settings" ) -MSG_HASH( - MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, - "audio_input_settings" - ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_SYNC, "audio_sync" @@ -222,10 +202,26 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH, "audio_wasapi_sh_buffer_length" ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_SETTINGS, + "microphone_settings" + ) MSG_HASH( MENU_ENUM_LABEL_MICROPHONE_DRIVER, "microphone_driver" ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE, + "microphone_input_rate" +) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_LATENCY, + "microphone_latency" + ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_BLOCK_FRAMES, + "microphone_block_frames" + ) MSG_HASH( MENU_ENUM_LABEL_AUTOSAVE_INTERVAL, "autosave_interval" @@ -5866,8 +5862,8 @@ MSG_HASH( "deferred_audio_output_settings_list" ) MSG_HASH( - MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, - "deferred_audio_input_settings_list" + MENU_ENUM_LABEL_DEFERRED_MICROPHONE_SETTINGS_LIST, + "deferred_microphone_settings_list" ) MSG_HASH( MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 837b518fb1f0..6c7cfa06c81b 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -336,7 +336,6 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) #endif break; case MENU_ENUM_LABEL_AUDIO_DEVICE: - case MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE: { strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_HELP_AUDIO_DEVICE), len); #ifdef HAVE_ALSA diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 76d9e1a12cbd..ee657886d843 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1516,6 +1516,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_DRIVER, "Audio driver to use." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MICROPHONE_DRIVER, + "Microphone" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MICROPHONE_DRIVER, + "Microphone driver to use." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_DRIVER, "Audio Resampler" @@ -2366,11 +2374,11 @@ MSG_HASH( "Change audio output settings." ) MSG_HASH( - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_SETTINGS, - "Input" + MENU_ENUM_LABEL_VALUE_MICROPHONE_SETTINGS, + "Microphone" ) MSG_HASH( - MENU_ENUM_SUBLABEL_AUDIO_INPUT_SETTINGS, + MENU_ENUM_SUBLABEL_MICROPHONE_SETTINGS, "Change audio input settings." ) MSG_HASH( @@ -2547,31 +2555,31 @@ MSG_HASH( /* Settings > Audio > Input */ MSG_HASH( - MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, - "Microphone Input" + MENU_ENUM_LABEL_VALUE_MICROPHONE_DEVICE, + "Device" ) MSG_HASH( - MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MICROPHONE, - "Enable audio input. Requires audio output to be enabled as well." + MENU_ENUM_SUBLABEL_MICROPHONE_DEVICE, + "Override the default input device the audio driver uses. This is driver dependent." ) MSG_HASH( - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_DEVICE, - "Device" + MENU_ENUM_LABEL_HELP_MICROPHONE_DEVICE, + "Override the default input device the audio driver uses. This is driver dependent." ) MSG_HASH( - MENU_ENUM_SUBLABEL_AUDIO_INPUT_DEVICE, - "Override the default input device the audio driver uses. This is driver dependent." + MENU_ENUM_LABEL_VALUE_MICROPHONE_INPUT_RATE, + "Input Rate (Hz)" ) MSG_HASH( - MENU_ENUM_LABEL_HELP_AUDIO_INPUT_DEVICE, - "Override the default input device the audio driver uses. This is driver dependent." + MENU_ENUM_SUBLABEL_MICROPHONE_INPUT_RATE, + "Audio input sample rate." ) MSG_HASH( - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_LATENCY, + MENU_ENUM_LABEL_VALUE_MICROPHONE_LATENCY, "Audio Input Latency (ms)" ) MSG_HASH( - MENU_ENUM_SUBLABEL_AUDIO_INPUT_LATENCY, + MENU_ENUM_SUBLABEL_MICROPHONE_LATENCY, "Desired audio input latency in milliseconds. Might not be honored if the audio driver can't provide given latency." ) @@ -2593,14 +2601,6 @@ MSG_HASH( MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE, "Audio output sample rate." ) -MSG_HASH( - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_RATE, - "Input Rate (Hz)" - ) -MSG_HASH( - MENU_ENUM_SUBLABEL_AUDIO_INPUT_RATE, - "Audio input sample rate." - ) /* Settings > Audio > Synchronization */ diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 4a0b5d721533..a6054090424a 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -223,7 +223,7 @@ GENERIC_DEFERRED_PUSH(deferred_push_privacy_settings_list, DISPLAYLIST_ GENERIC_DEFERRED_PUSH(deferred_push_midi_settings_list, DISPLAYLIST_MIDI_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_settings_list, DISPLAYLIST_AUDIO_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_output_settings_list, DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST) -GENERIC_DEFERRED_PUSH(deferred_push_audio_input_settings_list, DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST) +GENERIC_DEFERRED_PUSH(deferred_push_microphone_settings_list, DISPLAYLIST_MICROPHONE_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_resampler_settings_list, DISPLAYLIST_AUDIO_RESAMPLER_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_synchronization_settings_list, DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST) GENERIC_DEFERRED_PUSH(deferred_push_audio_mixer_settings_list, DISPLAYLIST_AUDIO_MIXER_SETTINGS_LIST) @@ -782,7 +782,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label( {MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, deferred_push_audio_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, deferred_push_audio_synchronization_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST, deferred_push_audio_output_settings_list}, - {MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, deferred_push_audio_input_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_MICROPHONE_SETTINGS_LIST, deferred_push_microphone_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST, deferred_push_audio_resampler_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_MIXER_SETTINGS_LIST, deferred_push_audio_mixer_settings_list}, {MENU_ENUM_LABEL_DEFERRED_LATENCY_SETTINGS_LIST, deferred_push_latency_settings_list}, @@ -1296,8 +1296,8 @@ static int menu_cbs_init_bind_deferred_push_compare_label( case MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_output_settings_list); break; - case MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST: - BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_input_settings_list); + case MENU_ENUM_LABEL_DEFERRED_MICROPHONE_SETTINGS_LIST: + BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_microphone_settings_list); break; case MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST: BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_audio_resampler_settings_list); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 8b01deabaf69..b4023d8bb472 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -456,8 +456,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST; case ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST; - case ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST: - return MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST; + case ACTION_OK_DL_MICROPHONE_SETTINGS_LIST: + return MENU_ENUM_LABEL_DEFERRED_MICROPHONE_SETTINGS_LIST; case ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST: return MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST; case ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: @@ -1637,7 +1637,7 @@ int generic_action_ok_displaylist_push(const char *path, case ACTION_OK_DL_AUDIO_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST: - case ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST: + case ACTION_OK_DL_MICROPHONE_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST: case ACTION_OK_DL_AUDIO_MIXER_SETTINGS_LIST: case ACTION_OK_DL_INPUT_HOTKEY_BINDS_LIST: @@ -5922,7 +5922,7 @@ DEFAULT_ACTION_OK_FUNC(action_ok_push_core_restore_backup_list, ACTION_OK_DL_COR DEFAULT_ACTION_OK_FUNC(action_ok_push_core_delete_backup_list, ACTION_OK_DL_CORE_DELETE_BACKUP_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_settings_list, ACTION_OK_DL_AUDIO_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_output_settings_list, ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST) -DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_input_settings_list, ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST) +DEFAULT_ACTION_OK_FUNC(action_ok_push_microphone_settings_list, ACTION_OK_DL_MICROPHONE_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_resampler_settings_list, ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST) DEFAULT_ACTION_OK_FUNC(action_ok_push_audio_synchronization_settings_list, ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST) #ifdef HAVE_AUDIOMIXER @@ -8310,7 +8310,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL, action_ok_close_submenu }, {MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_LIST, action_ok_push_manual_content_scan_list}, {MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS, action_ok_push_audio_output_settings_list}, - {MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, action_ok_push_audio_input_settings_list}, + {MENU_ENUM_LABEL_MICROPHONE_SETTINGS, action_ok_push_microphone_settings_list}, {MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS, action_ok_push_audio_resampler_settings_list}, {MENU_ENUM_LABEL_LATENCY_SETTINGS, action_ok_push_latency_settings_list}, {MENU_ENUM_LABEL_CORE_SETTINGS, action_ok_push_core_settings_list}, diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 7e8ab662a928..3fc7613ab7fa 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -341,7 +341,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_window_show_menubar, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_settings_list, MENU_ENUM_SUBLABEL_AUDIO_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_resampler_settings_list, MENU_ENUM_SUBLABEL_AUDIO_RESAMPLER_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_output_settings_list, MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_SETTINGS) -DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_settings_list, MENU_ENUM_SUBLABEL_AUDIO_INPUT_SETTINGS) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_settings_list, MENU_ENUM_SUBLABEL_MICROPHONE_SETTINGS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_synchronization_settings_list, MENU_ENUM_SUBLABEL_AUDIO_SYNCHRONIZATION_SETTINGS) #ifdef HAVE_AUDIOMIXER DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_mixer_settings_list, MENU_ENUM_SUBLABEL_AUDIO_MIXER_SETTINGS) @@ -489,7 +489,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_configurations_list_list, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_shared_context, MENU_ENUM_SUBLABEL_VIDEO_SHARED_CONTEXT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_driver_switch_enable, MENU_ENUM_SUBLABEL_DRIVER_SWITCH_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_latency, MENU_ENUM_SUBLABEL_AUDIO_LATENCY) -DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_latency, MENU_ENUM_SUBLABEL_AUDIO_INPUT_LATENCY) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_latency, MENU_ENUM_SUBLABEL_MICROPHONE_LATENCY) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_rate_control_delta, MENU_ENUM_SUBLABEL_AUDIO_RATE_CONTROL_DELTA) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_mute, MENU_ENUM_SUBLABEL_AUDIO_MUTE) #ifdef HAVE_AUDIOMIXER @@ -533,7 +533,6 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_always_reload_core_on_run_content, M DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_refresh_rate, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_refresh_rate_polled, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable, MENU_ENUM_SUBLABEL_AUDIO_ENABLE) -DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable_microphone, MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MICROPHONE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable_menu, MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MENU) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_sounds, MENU_ENUM_SUBLABEL_MENU_SOUNDS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_max_timing_skew, MENU_ENUM_SUBLABEL_AUDIO_MAX_TIMING_SKEW) @@ -772,9 +771,9 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_filter_supported_extensions, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_wallpaper, MENU_ENUM_SUBLABEL_MENU_WALLPAPER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_dynamic_wallpaper, MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_device, MENU_ENUM_SUBLABEL_AUDIO_DEVICE) -DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_device, MENU_ENUM_SUBLABEL_AUDIO_INPUT_DEVICE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_device, MENU_ENUM_SUBLABEL_MICROPHONE_DEVICE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_output_rate, MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE) -DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_input_rate, MENU_ENUM_SUBLABEL_AUDIO_INPUT_RATE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_rate, MENU_ENUM_SUBLABEL_MICROPHONE_INPUT_RATE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_dsp_plugin, MENU_ENUM_SUBLABEL_AUDIO_DSP_PLUGIN) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_dsp_plugin_remove, MENU_ENUM_SUBLABEL_AUDIO_DSP_PLUGIN_REMOVE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_wasapi_exclusive_mode, MENU_ENUM_SUBLABEL_AUDIO_WASAPI_EXCLUSIVE_MODE) @@ -3542,14 +3541,14 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_output_rate); break; - case MENU_ENUM_LABEL_AUDIO_INPUT_RATE: - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_rate); + case MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_rate); break; case MENU_ENUM_LABEL_AUDIO_DEVICE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_device); break; - case MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE: - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_device); + case MENU_ENUM_LABEL_MICROPHONE_DEVICE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_device); break; case MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_wasapi_exclusive_mode); @@ -4176,9 +4175,6 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable); break; - case MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE: - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable_microphone); - break; case MENU_ENUM_LABEL_AUDIO_ENABLE_MENU: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable_menu); break; @@ -4295,8 +4291,8 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_LATENCY: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_latency); break; - case MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY: - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_latency); + case MENU_ENUM_LABEL_MICROPHONE_LATENCY: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_latency); break; case MENU_ENUM_LABEL_DRIVER_SWITCH_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_driver_switch_enable); @@ -4620,8 +4616,8 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_output_settings_list); break; - case MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS: - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_input_settings_list); + case MENU_ENUM_LABEL_MICROPHONE_SETTINGS: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_settings_list); break; case MENU_ENUM_LABEL_AUDIO_MIXER_SETTINGS: #ifdef HAVE_AUDIOMIXER diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index 91fff237ca18..9d8d0b55c640 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -681,7 +681,7 @@ DEFAULT_TITLE_MACRO(action_get_updater_settings_list, MENU_ENUM_LABEL_ DEFAULT_TITLE_MACRO(action_get_audio_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_SETTINGS) DEFAULT_TITLE_MACRO(action_get_audio_resampler_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_RESAMPLER_SETTINGS) DEFAULT_TITLE_MACRO(action_get_audio_output_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_OUTPUT_SETTINGS) -DEFAULT_TITLE_MACRO(action_get_audio_input_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_SETTINGS) +DEFAULT_TITLE_MACRO(action_get_microphone_settings_list, MENU_ENUM_LABEL_VALUE_MICROPHONE_SETTINGS) DEFAULT_TITLE_MACRO(action_get_audio_synchronization_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_SYNCHRONIZATION_SETTINGS) #ifdef HAVE_AUDIOMIXER DEFAULT_TITLE_MACRO(action_get_audio_mixer_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_SETTINGS) @@ -1053,7 +1053,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, action_get_audio_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST, action_get_audio_resampler_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST, action_get_audio_output_settings_list}, - {MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, action_get_audio_input_settings_list}, + {MENU_ENUM_LABEL_DEFERRED_MICROPHONE_SETTINGS_LIST, action_get_microphone_settings_list}, {MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, action_get_audio_synchronization_settings_list}, #ifdef HAVE_AUDIOMIXER {MENU_ENUM_LABEL_DEFERRED_AUDIO_MIXER_SETTINGS_LIST, action_get_audio_mixer_settings_list}, diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 83f46d9f3a50..49b6ea324c74 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -10756,7 +10756,7 @@ static void materialui_list_insert( string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_HDR_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS)) || - string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS)) || + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MICROPHONE_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_SYNCHRONIZATION_SETTINGS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_AUDIO_MIXER_SETTINGS)) || diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index 1f6a57f5b2bf..c193cb238028 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -110,7 +110,7 @@ enum ACTION_OK_DL_CRT_SWITCHRES_SETTINGS_LIST, ACTION_OK_DL_AUDIO_SETTINGS_LIST, ACTION_OK_DL_AUDIO_OUTPUT_SETTINGS_LIST, - ACTION_OK_DL_AUDIO_INPUT_SETTINGS_LIST, + ACTION_OK_DL_MICROPHONE_SETTINGS_LIST, ACTION_OK_DL_AUDIO_RESAMPLER_SETTINGS_LIST, ACTION_OK_DL_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, ACTION_OK_DL_AUDIO_MIXER_SETTINGS_LIST, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index bd1af253990a..ee0a69f9a54b 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6634,10 +6634,6 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE, PARSE_ONLY_UINT, false) == 0) count++; - if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_AUDIO_INPUT_RATE, - PARSE_ONLY_UINT, false) == 0) - count++; break; case DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST: if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, @@ -6677,25 +6673,21 @@ unsigned menu_displaylist_build_list( PARSE_ONLY_UINT, false) == 0) count++; break; - case DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST: + case DISPLAYLIST_MICROPHONE_SETTINGS_LIST: if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, - PARSE_ONLY_BOOL, false) == 0) + MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE, + PARSE_ONLY_UINT, false) == 0) count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE, + MENU_ENUM_LABEL_MICROPHONE_DEVICE, PARSE_ONLY_STRING, false) == 0) count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_AUDIO_INPUT_RATE, - PARSE_ONLY_UINT, false) == 0) - count++; - if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, + MENU_ENUM_LABEL_MICROPHONE_LATENCY, PARSE_ONLY_UINT, false) == 0) count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_AUDIO_INPUT_BLOCK_FRAMES, + MENU_ENUM_LABEL_MICROPHONE_BLOCK_FRAMES, PARSE_ONLY_UINT, false) == 0) count++; break; @@ -6723,7 +6715,6 @@ unsigned menu_displaylist_build_list( #endif menu_displaylist_build_info_selective_t build_list[] = { {MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS, PARSE_ACTION, true }, - {MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_AUDIO_SYNCHRONIZATION_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_MIDI_SETTINGS, PARSE_ACTION, true }, @@ -9302,7 +9293,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_VIDEO_FRAME_DELAY, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO, PARSE_ONLY_BOOL, true }, {MENU_ENUM_LABEL_AUDIO_LATENCY, PARSE_ONLY_UINT, true }, - {MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, PARSE_ONLY_UINT, true }, + {MENU_ENUM_LABEL_MICROPHONE_LATENCY, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_INPUT_POLL_TYPE_BEHAVIOR, PARSE_ONLY_UINT, true }, {MENU_ENUM_LABEL_INPUT_BLOCK_TIMEOUT, PARSE_ONLY_UINT, true }, #ifdef HAVE_RUNAHEAD @@ -13293,7 +13284,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, case DISPLAYLIST_AUDIO_SETTINGS_LIST: case DISPLAYLIST_AUDIO_RESAMPLER_SETTINGS_LIST: case DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST: - case DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST: + case DISPLAYLIST_MICROPHONE_SETTINGS_LIST: case DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: case DISPLAYLIST_HELP_SCREEN_LIST: case DISPLAYLIST_INFORMATION_LIST: diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 27376d07e89a..6d41d9757619 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -185,7 +185,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_AUDIO_SETTINGS_LIST, DISPLAYLIST_AUDIO_RESAMPLER_SETTINGS_LIST, DISPLAYLIST_AUDIO_OUTPUT_SETTINGS_LIST, - DISPLAYLIST_AUDIO_INPUT_SETTINGS_LIST, + DISPLAYLIST_MICROPHONE_SETTINGS_LIST, DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, DISPLAYLIST_AUDIO_MIXER_SETTINGS_LIST, DISPLAYLIST_CORE_SETTINGS_LIST, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 99d2bd84b361..f696583196cb 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -8332,14 +8332,16 @@ static void general_write_handler(rarch_setting_t *setting) #endif break; case MENU_ENUM_LABEL_AUDIO_LATENCY: - case MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY: case MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE: - case MENU_ENUM_LABEL_AUDIO_INPUT_RATE: case MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE: case MENU_ENUM_LABEL_AUDIO_WASAPI_FLOAT_FORMAT: case MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH: rarch_cmd = CMD_EVENT_AUDIO_REINIT; break; + case MENU_ENUM_LABEL_MICROPHONE_LATENCY: + case MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE: + rarch_cmd = CMD_EVENT_MICROPHONE_REINIT; + break; case MENU_ENUM_LABEL_PAL60_ENABLE: { global_t *global = global_get_ptr(); @@ -10387,8 +10389,8 @@ static bool setting_append_list( CONFIG_ACTION( list, list_info, - MENU_ENUM_LABEL_AUDIO_INPUT_SETTINGS, - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_SETTINGS, + MENU_ENUM_LABEL_MICROPHONE_SETTINGS, + MENU_ENUM_LABEL_VALUE_MICROPHONE_SETTINGS, &group_info, &subgroup_info, parent_group); @@ -10435,7 +10437,7 @@ static bool setting_append_list( case SETTINGS_LIST_DRIVERS: { unsigned i, j = 0; - struct string_options_entry string_options_entries[12] = {0}; + struct string_options_entry string_options_entries[13] = {0}; START_GROUP(list, list_info, &group_info, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DRIVER_SETTINGS), parent_group); MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_IDX_PTR(list, list_info, MENU_ENUM_LABEL_DRIVER_SETTINGS); @@ -13580,9 +13582,9 @@ static bool setting_append_list( CONFIG_UINT( list, list_info, - &settings->uints.audio_input_latency, - MENU_ENUM_LABEL_AUDIO_INPUT_LATENCY, - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_LATENCY, + &settings->uints.microphone_latency, + MENU_ENUM_LABEL_MICROPHONE_LATENCY, + MENU_ENUM_LABEL_VALUE_MICROPHONE_LATENCY, g_defaults.settings_in_latency ? g_defaults.settings_in_latency : DEFAULT_IN_LATENCY, &group_info, @@ -13672,9 +13674,9 @@ static bool setting_append_list( CONFIG_UINT( list, list_info, - &settings->uints.audio_input_block_frames, - MENU_ENUM_LABEL_AUDIO_INPUT_BLOCK_FRAMES, - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_BLOCK_FRAMES, + &settings->uints.microphone_block_frames, + MENU_ENUM_LABEL_MICROPHONE_BLOCK_FRAMES, + MENU_ENUM_LABEL_VALUE_MICROPHONE_BLOCK_FRAMES, 0, &group_info, &subgroup_info, @@ -13688,27 +13690,6 @@ static bool setting_append_list( parent_group = msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS); - START_SUB_GROUP(list, list_info, "State", &group_info, &subgroup_info, parent_group); - - CONFIG_BOOL( - list, list_info, - &settings->bools.audio_enable_input, - MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE, - MENU_ENUM_LABEL_VALUE_AUDIO_ENABLE_MICROPHONE, - DEFAULT_AUDIO_ENABLE_INPUT, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, - &group_info, - &subgroup_info, - parent_group, - general_write_handler, - general_read_handler, - SD_FLAG_NONE); - - END_SUB_GROUP(list, list_info, parent_group); - - parent_group = msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS); - START_SUB_GROUP( list, list_info, @@ -13738,10 +13719,10 @@ static bool setting_append_list( CONFIG_STRING( list, list_info, - settings->arrays.audio_input_device, - sizeof(settings->arrays.audio_input_device), - MENU_ENUM_LABEL_AUDIO_INPUT_DEVICE, - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_DEVICE, + settings->arrays.microphone_device, + sizeof(settings->arrays.microphone_device), + MENU_ENUM_LABEL_MICROPHONE_DEVICE, + MENU_ENUM_LABEL_VALUE_MICROPHONE_DEVICE, "", &group_info, &subgroup_info, @@ -13772,9 +13753,9 @@ static bool setting_append_list( CONFIG_UINT( list, list_info, - &settings->uints.audio_input_sample_rate, - MENU_ENUM_LABEL_AUDIO_INPUT_RATE, - MENU_ENUM_LABEL_VALUE_AUDIO_INPUT_RATE, + &settings->uints.microphone_sample_rate, + MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE, + MENU_ENUM_LABEL_VALUE_MICROPHONE_INPUT_RATE, DEFAULT_INPUT_RATE, &group_info, &subgroup_info, diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index dc60c0eecd7c..bd5c433729d9 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -255,7 +255,7 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) const microphone_driver_t *mic_driver = mic_st->driver; void *driver_context = mic_st->driver_context; unsigned runloop_audio_latency = runloop_state_get_ptr()->audio_latency; - unsigned setting_audio_latency = settings->uints.audio_input_latency; + unsigned setting_audio_latency = settings->uints.microphone_latency; unsigned actual_sample_rate = 0; unsigned audio_latency = (runloop_audio_latency > setting_audio_latency) ? runloop_audio_latency : setting_audio_latency; @@ -274,10 +274,10 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) goto error; microphone->microphone_context = mic_driver->open_mic(driver_context, - *settings->arrays.audio_input_device ? settings->arrays.audio_input_device : NULL, - settings->uints.audio_input_sample_rate, + *settings->arrays.microphone_device ? settings->arrays.microphone_device : NULL, + settings->uints.microphone_sample_rate, audio_latency, - settings->uints.audio_input_block_frames, + settings->uints.microphone_block_frames, &actual_sample_rate); if (!microphone->microphone_context) @@ -288,10 +288,10 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) if (actual_sample_rate != 0) { RARCH_LOG("[Audio]: Requested microphone sample rate of %uHz, got %uHz. Updating settings with this value.\n", - settings->uints.audio_input_sample_rate, + settings->uints.microphone_sample_rate, actual_sample_rate ); - configuration_set_uint(settings, settings->uints.audio_input_sample_rate, actual_sample_rate); + configuration_set_uint(settings, settings->uints.microphone_sample_rate, actual_sample_rate); } RARCH_LOG("[Audio]: Initialized microphone\n", actual_sample_rate); diff --git a/msg_hash.h b/msg_hash.h index 21ad467d4438..22fe72bf3a1c 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1911,7 +1911,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_OUTPUT_SETTINGS_LIST, - MENU_ENUM_LABEL_DEFERRED_AUDIO_INPUT_SETTINGS_LIST, + MENU_ENUM_LABEL_DEFERRED_MICROPHONE_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_SYNCHRONIZATION_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_AUDIO_MIXER_SETTINGS_LIST, MENU_ENUM_LABEL_DEFERRED_CORE_SETTINGS_LIST, @@ -2047,19 +2047,15 @@ enum msg_hash_enums /* Audio */ MENU_LABEL(AUDIO_ENABLE), - MENU_LABEL(AUDIO_ENABLE_MICROPHONE), MENU_LABEL(AUDIO_ENABLE_MENU), MENU_LBL_H(AUDIO_MAX_TIMING_SKEW), MENU_LABEL(AUDIO_OUTPUT_RATE), - MENU_LABEL(AUDIO_INPUT_RATE), MENU_LBL_H(AUDIO_DEVICE), MENU_ENUM_LABEL_HELP_AUDIO_DEVICE_ALSA, MENU_ENUM_LABEL_HELP_AUDIO_DEVICE_OSS, MENU_ENUM_LABEL_HELP_AUDIO_DEVICE_JACK, MENU_ENUM_LABEL_HELP_AUDIO_DEVICE_RSOUND, - MENU_LBL_H(AUDIO_INPUT_DEVICE), MENU_LABEL(AUDIO_BLOCK_FRAMES), - MENU_LABEL(AUDIO_INPUT_BLOCK_FRAMES), MENU_LABEL(AUDIO_DSP_PLUGIN), MENU_LABEL(AUDIO_DSP_PLUGIN_REMOVE), MENU_LABEL(AUDIO_MUTE), @@ -2070,12 +2066,17 @@ enum msg_hash_enums MENU_LABEL(AUDIO_MIXER_VOLUME), MENU_LBL_H(AUDIO_RATE_CONTROL_DELTA), MENU_LABEL(AUDIO_LATENCY), - MENU_LABEL(AUDIO_INPUT_LATENCY), MENU_LABEL(AUDIO_RESAMPLER_QUALITY), MENU_LABEL(AUDIO_WASAPI_EXCLUSIVE_MODE), MENU_LABEL(AUDIO_WASAPI_FLOAT_FORMAT), MENU_LABEL(AUDIO_WASAPI_SH_BUFFER_LENGTH), + /* Microphone */ + MENU_LABEL(MICROPHONE_INPUT_RATE), + MENU_LABEL(MICROPHONE_LATENCY), + MENU_LBL_H(MICROPHONE_DEVICE), + MENU_LABEL(MICROPHONE_BLOCK_FRAMES), + MENU_LABEL(SAVE_STATE), MENU_LABEL(LOAD_STATE), MENU_LABEL(UNDO_LOAD_STATE), @@ -2904,7 +2905,7 @@ enum msg_hash_enums MENU_LABEL(AUDIO_SETTINGS), MENU_LABEL(AUDIO_RESAMPLER_SETTINGS), MENU_LABEL(AUDIO_OUTPUT_SETTINGS), - MENU_LABEL(AUDIO_INPUT_SETTINGS), + MENU_LABEL(MICROPHONE_SETTINGS), MENU_LABEL(AUDIO_SYNCHRONIZATION_SETTINGS), MENU_LABEL(AUDIO_MIXER_SETTINGS), MENU_LABEL(LATENCY_SETTINGS), From a1efbf028e541de526d759d5b2fed330d98fe10b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 13:21:17 -0500 Subject: [PATCH 133/309] Add DRIVER_MICROPHONE_MASK to DRIVERS_CMD_ALL --- retroarch.h | 1 + 1 file changed, 1 insertion(+) diff --git a/retroarch.h b/retroarch.h index d403a00168b1..4e2c6b233bb6 100644 --- a/retroarch.h +++ b/retroarch.h @@ -75,6 +75,7 @@ RETRO_BEGIN_DECLS #define DRIVERS_CMD_ALL \ ( DRIVER_AUDIO_MASK \ + | DRIVER_MICROPHONE_MASK \ | DRIVER_VIDEO_MASK \ | DRIVER_INPUT_MASK \ | DRIVER_CAMERA_MASK \ From 3b3968c7a93646b7e33719ce14de9d6355fbcefc Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 13:57:57 -0500 Subject: [PATCH 134/309] Rename audio_enable_input to microphone_enable --- config.def.h | 2 +- configuration.c | 2 +- configuration.h | 4 +++- microphone/microphone_driver.c | 6 +++--- runloop.c | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/config.def.h b/config.def.h index 7ef6634f7afb..10b837d49c54 100644 --- a/config.def.h +++ b/config.def.h @@ -978,7 +978,7 @@ /* Will enable audio or not. */ #define DEFAULT_AUDIO_ENABLE true -#define DEFAULT_AUDIO_ENABLE_INPUT true +#define DEFAULT_MICROPHONE_ENABLE true /* Enable menu audio sounds. */ #define DEFAULT_AUDIO_ENABLE_MENU false diff --git a/configuration.c b/configuration.c index c99c986ebd8c..2ec6d129c123 100644 --- a/configuration.c +++ b/configuration.c @@ -1786,7 +1786,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("keyboard_gamepad_enable", &settings->bools.input_keyboard_gamepad_enable, true, true, false); SETTING_BOOL("core_set_supports_no_game_enable", &settings->bools.set_supports_no_game_enable, true, true, false); SETTING_BOOL("audio_enable", &settings->bools.audio_enable, true, DEFAULT_AUDIO_ENABLE, false); - SETTING_BOOL("audio_enable_input", &settings->bools.audio_enable_input, true, DEFAULT_AUDIO_ENABLE_INPUT, false); + SETTING_BOOL("microphone_enable", &settings->bools.microphone_enable, true, DEFAULT_MICROPHONE_ENABLE, false); SETTING_BOOL("menu_enable_widgets", &settings->bools.menu_enable_widgets, true, DEFAULT_MENU_ENABLE_WIDGETS, false); SETTING_BOOL("menu_show_load_content_animation", &settings->bools.menu_show_load_content_animation, true, DEFAULT_MENU_SHOW_LOAD_CONTENT_ANIMATION, false); SETTING_BOOL("notification_show_autoconfig", &settings->bools.notification_show_autoconfig, true, DEFAULT_NOTIFICATION_SHOW_AUTOCONFIG, false); diff --git a/configuration.h b/configuration.h index b462f4c69896..780cad8e02b9 100644 --- a/configuration.h +++ b/configuration.h @@ -601,7 +601,6 @@ typedef struct settings /* Audio */ bool audio_enable; - bool audio_enable_input; bool audio_enable_menu; bool audio_enable_menu_ok; bool audio_enable_menu_cancel; @@ -614,6 +613,9 @@ typedef struct settings bool audio_wasapi_float_format; bool audio_fastforward_mute; + /* Microphone */ + bool microphone_enable; + /* Input */ bool input_remap_binds_enable; bool input_autodetect_enable; diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index bd5c433729d9..183407baa8b5 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -170,7 +170,7 @@ bool microphone_driver_init_internal(void *settings_data) { float *in_samples_buf = NULL; settings_t *settings = (settings_t*)settings_data; - bool audio_enable_microphone = settings->bools.audio_enable_input; + bool audio_enable_microphone = settings->bools.microphone_enable; float slowmotion_ratio = settings->floats.slowmotion_ratio; size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; int16_t *in_conv_buf = (int16_t*)memalign_alloc(64, insamples_max * sizeof(int16_t)); @@ -527,7 +527,7 @@ retro_microphone_t *microphone_driver_open_mic(void) return NULL; } - if (!settings->bools.audio_enable_input) + if (!settings->bools.microphone_enable) { /* Not checking mic_st->flags because they might not be set yet; * don't forget, the core can ask for a mic * before the audio driver is ready to create one. */ @@ -643,5 +643,5 @@ bool microphone_driver_deinit(void) microphone_driver_free_devices_list(); microphone_driver_close_microphones(); return microphone_driver_deinit_internal( - settings->bools.audio_enable_input); + settings->bools.microphone_enable); } \ No newline at end of file diff --git a/runloop.c b/runloop.c index 8d67cb6d910b..3a699f38da35 100644 --- a/runloop.c +++ b/runloop.c @@ -3347,7 +3347,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) return false; /* User didn't provide a pointer for a response, what can we do? */ - if (!settings->bools.audio_enable_input) + if (!settings->bools.microphone_enable) return false; if (mic_driver->init) @@ -3376,7 +3376,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) return false; /* User didn't provide a pointer for a response, what can we do? */ - *microphone_enabled = settings->bools.audio_enable_input; + *microphone_enabled = settings->bools.microphone_enable; return true; } default: From e1c0741c8056d18a23e2ccb5459ce3bd94cd80f1 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 13:58:08 -0500 Subject: [PATCH 135/309] Remove some labels from qt_options --- ui/drivers/qt/qt_options.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/drivers/qt/qt_options.cpp b/ui/drivers/qt/qt_options.cpp index 5f193c47cf2d..c560cfc1f52a 100644 --- a/ui/drivers/qt/qt_options.cpp +++ b/ui/drivers/qt/qt_options.cpp @@ -160,7 +160,6 @@ QWidget *AudioPage::widget() QGridLayout *volumeLayout = new QGridLayout(); outputGroup->add(MENU_ENUM_LABEL_AUDIO_ENABLE); - outputGroup->add(MENU_ENUM_LABEL_AUDIO_ENABLE_MICROPHONE); outputGroup->add(MENU_ENUM_LABEL_AUDIO_DRIVER); outputGroup->add(MENU_ENUM_LABEL_AUDIO_DEVICE); outputGroup->add(MENU_ENUM_LABEL_AUDIO_LATENCY); @@ -168,7 +167,6 @@ QWidget *AudioPage::widget() resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_RESAMPLER_DRIVER); resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_RESAMPLER_QUALITY); resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE); - resamplerGroup->add(MENU_ENUM_LABEL_AUDIO_INPUT_RATE); syncGroup->add(MENU_ENUM_LABEL_AUDIO_SYNC); syncGroup->add(MENU_ENUM_LABEL_AUDIO_MAX_TIMING_SKEW); From f227853ffe6cd62cf2791126ea536e8e97b83ca8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 13:58:32 -0500 Subject: [PATCH 136/309] Search for microphone_driver within find_driver_nonempty --- retroarch.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/retroarch.c b/retroarch.c index 350dd7bb0133..61bfeaab5fd3 100644 --- a/retroarch.c +++ b/retroarch.c @@ -428,6 +428,16 @@ static const void *find_driver_nonempty( return audio_drivers[i]; } } + else if (string_is_equal(label, "microphone_driver")) + { + if (microphone_drivers[i]) + { + const char *ident = microphone_drivers[i]->ident; + + strlcpy(s, ident, len); + return microphone_drivers[i]; + } + } else if (string_is_equal(label, "record_driver")) { if (record_drivers[i]) From 1a7a5ce29c254d2897ce14eb1f5c48d7bce87e9a Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 15:42:33 -0500 Subject: [PATCH 137/309] Clean up some mic driver code --- microphone/microphone_driver.c | 98 ++++++++++++++-------------------- 1 file changed, 41 insertions(+), 57 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 183407baa8b5..b02180dd2b44 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -168,55 +168,47 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone); bool microphone_driver_init_internal(void *settings_data) { - float *in_samples_buf = NULL; - settings_t *settings = (settings_t*)settings_data; - bool audio_enable_microphone = settings->bools.microphone_enable; - float slowmotion_ratio = settings->floats.slowmotion_ratio; - size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; - int16_t *in_conv_buf = (int16_t*)memalign_alloc(64, insamples_max * sizeof(int16_t)); - bool verbosity_enabled = verbosity_is_enabled(); + settings_t *settings = (settings_t*)settings_data; + float slowmotion_ratio = settings->floats.slowmotion_ratio; + bool verbosity_enabled = verbosity_is_enabled(); + size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; - convert_s16_to_float_init_simd(); - convert_float_to_s16_init_simd(); - - if (!in_conv_buf) - goto error; - - mic_driver_st.input_samples_conv_buf = in_conv_buf; - mic_driver_st.input_samples_conv_buf_length = insamples_max; - - if (!audio_enable_microphone) - { + if (!settings->bools.microphone_enable) + { /* If the user has mic support turned off... */ mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; return false; } + convert_s16_to_float_init_simd(); + convert_float_to_s16_init_simd(); + if (!(microphone_driver_find_driver(settings, - "microphone driver", verbosity_enabled))) + "microphone driver", verbosity_enabled))) { RARCH_ERR("[Microphone]: Failed to initialize microphone driver. Will continue without mic input.\n"); goto error; } - if (!mic_driver_st.driver || !mic_driver_st.driver->init) - { + mic_driver_st.input_samples_conv_buf_length = insamples_max; + mic_driver_st.input_samples_conv_buf = (int16_t*)memalign_alloc(64, mic_driver_st.input_samples_conv_buf_length); + if (!mic_driver_st.input_samples_conv_buf) goto error; - } + mic_driver_st.input_samples_buf_length = insamples_max * sizeof(float); + mic_driver_st.input_samples_buf = (float*)memalign_alloc(64, mic_driver_st.input_samples_buf_length); + if (!mic_driver_st.input_samples_buf) + goto error; - mic_driver_st.driver_context = mic_driver_st.driver->init(); - + if (!mic_driver_st.driver || !mic_driver_st.driver->init) + goto error; - if (!mic_driver_st.driver_context || !in_samples_buf) - { + mic_driver_st.driver_context = mic_driver_st.driver->init(); + if (!mic_driver_st.driver_context) goto error; - } RARCH_LOG("[Microphone]: Started synchronous microphone driver\n"); - mic_driver_st.input_samples_buf = (float*)in_samples_buf; - mic_driver_st.input_samples_buf_length = insamples_max * sizeof(float); - mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_CONTROL; + mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_CONTROL; if ( mic_driver_st.microphone.active && !mic_driver_st.microphone.microphone_context) @@ -232,7 +224,6 @@ bool microphone_driver_init_internal(void *settings_data) microphone_driver_close_mic(&mic_driver_st.microphone); } - } return true; @@ -287,18 +278,18 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) if (actual_sample_rate != 0) { - RARCH_LOG("[Audio]: Requested microphone sample rate of %uHz, got %uHz. Updating settings with this value.\n", + RARCH_LOG("[Microphone]: Requested microphone sample rate of %uHz, got %uHz. Updating settings with this value.\n", settings->uints.microphone_sample_rate, actual_sample_rate ); configuration_set_uint(settings, settings->uints.microphone_sample_rate, actual_sample_rate); } - RARCH_LOG("[Audio]: Initialized microphone\n", actual_sample_rate); + RARCH_LOG("[Microphone]: Initialized microphone\n", actual_sample_rate); return; error: mic_driver_microphone_handle_free(microphone); - RARCH_ERR("[Audio]: Driver attempted to initialize the microphone but failed\n"); + RARCH_ERR("[Microphone]: Driver attempted to initialize the microphone but failed\n"); } void microphone_driver_close_mic(retro_microphone_t *microphone) @@ -475,7 +466,7 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size return 0; /* Not an error */ } - runloop_flags = runloop_get_flags(); + runloop_flags = runloop_get_flags(); do { @@ -489,13 +480,13 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size /* If the game is running, the audio driver and mic are running, * and the input sample buffer is valid... */ audio_driver_flush_microphone_input(mic_st, - microphone, - config_get_ptr()->floats.slowmotion_ratio, - config_get_ptr()->bools.audio_fastforward_mute, - frames, - frames_to_read, - runloop_flags & RUNLOOP_FLAG_SLOWMOTION, - runloop_flags & RUNLOOP_FLAG_FASTMOTION); + microphone, + config_get_ptr()->floats.slowmotion_ratio, + config_get_ptr()->bools.audio_fastforward_mute, + frames, + frames_to_read, + runloop_flags & RUNLOOP_FLAG_SLOWMOTION, + runloop_flags & RUNLOOP_FLAG_FASTMOTION); frames_remaining -= frames_to_read; frames += frames_to_read << 1; } @@ -560,13 +551,13 @@ retro_microphone_t *microphone_driver_open_mic(void) mic_driver_open_mic_internal(&mic_st->microphone); if (mic_st->microphone.microphone_context) /* If the microphone was successfully initialized... */ - RARCH_LOG("[Audio]: Initialized the requested microphone successfully\n"); + RARCH_LOG("[Microphone]: Initialized the requested microphone successfully\n"); else goto error; } else - { /* If the audio driver isn't ready to create a microphone... */ - RARCH_LOG("[Audio]: Microphone requested before audio context was ready; deferring initialization\n"); + { /* If the driver isn't ready to create a microphone... */ + RARCH_LOG("[Microphone]: Microphone requested before audio context was ready; deferring initialization\n"); } return &mic_st->microphone; @@ -609,26 +600,20 @@ static bool microphone_driver_free_devices_list(void) return true; } -static bool microphone_driver_deinit_internal(bool microphone_enable) +static bool microphone_driver_deinit_internal(void) { microphone_driver_state_t *mic_st = &mic_driver_st; - if (mic_st->driver - && mic_st->driver->free) + if (mic_st->driver && mic_st->driver->free) { if (mic_st->driver_context) mic_st->driver->free(mic_st->driver_context); + mic_st->driver_context = NULL; } if (mic_st->input_samples_conv_buf) memalign_free(mic_st->input_samples_conv_buf); - mic_st->input_samples_conv_buf = NULL; - - if (!microphone_enable) - { - mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; - return false; - } + mic_st->input_samples_conv_buf = NULL; if (mic_st->input_samples_buf) memalign_free(mic_st->input_samples_buf); @@ -642,6 +627,5 @@ bool microphone_driver_deinit(void) settings_t *settings = config_get_ptr(); microphone_driver_free_devices_list(); microphone_driver_close_microphones(); - return microphone_driver_deinit_internal( - settings->bools.microphone_enable); + return microphone_driver_deinit_internal(); } \ No newline at end of file From 425cb8771566ee9fc045a666747f0a16aeb7d3aa Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 15:51:58 -0500 Subject: [PATCH 138/309] Pending mics now return silence --- microphone/microphone_driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index b02180dd2b44..128c0923dd35 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -463,7 +463,8 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size if (!(mic_st->driver_context && microphone->microphone_context)) { /* If the microphone isn't ready... */ - return 0; /* Not an error */ + memset(frames, 0, num_frames * sizeof(*frames)); + return num_frames; /* Not an error */ } runloop_flags = runloop_get_flags(); From 2fc37cd03e69b6b57758b7439e302a985889c693 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 16:12:31 -0500 Subject: [PATCH 139/309] Adjust some logging and comments --- microphone/microphone_driver.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 128c0923dd35..054d3cb52c76 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -176,6 +176,7 @@ bool microphone_driver_init_internal(void *settings_data) if (!settings->bools.microphone_enable) { /* If the user has mic support turned off... */ mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + RARCH_WARN("[Microphone]: Refused to initialize microphone driver because it's disabled in the settings\n"); return false; } @@ -206,14 +207,14 @@ bool microphone_driver_init_internal(void *settings_data) if (!mic_driver_st.driver_context) goto error; - RARCH_LOG("[Microphone]: Started synchronous microphone driver\n"); + RARCH_LOG("[Microphone]: Initialized microphone driver\n"); mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_CONTROL; if ( mic_driver_st.microphone.active && !mic_driver_st.microphone.microphone_context) { /* If the core requested a microphone before the driver was able to provide one...*/ - /* Now that the driver and driver context are ready, let's initialize the mid */ + /* Now that the driver and driver context are ready, let's initialize the mic */ mic_driver_open_mic_internal(&mic_driver_st.microphone); if (mic_driver_st.microphone.microphone_context) @@ -285,7 +286,7 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) configuration_set_uint(settings, settings->uints.microphone_sample_rate, actual_sample_rate); } - RARCH_LOG("[Microphone]: Initialized microphone\n", actual_sample_rate); + RARCH_LOG("[Microphone]: Initialized microphone\n"); return; error: mic_driver_microphone_handle_free(microphone); From 9f25c3271aacf687b7cf7a3900b11e5ee4b27589 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 16:31:26 -0500 Subject: [PATCH 140/309] Some cleanup in the microphone driver --- microphone/microphone_driver.c | 44 +++++++++++++++++----------------- microphone/microphone_driver.h | 15 ++++++++---- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 054d3cb52c76..c81680e54ff5 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -63,10 +63,9 @@ microphone_driver_state_t *microphone_state_get_ptr(void) return &mic_driver_st; } -unsigned mic_driver_get_sample_size(void) +unsigned mic_driver_get_sample_size(const retro_microphone_t *microphone) { - microphone_driver_state_t *mic_st = &mic_driver_st; - return (mic_st->flags & MICROPHONE_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t); + return (microphone->flags & MICROPHONE_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t); } /** @@ -209,7 +208,7 @@ bool microphone_driver_init_internal(void *settings_data) RARCH_LOG("[Microphone]: Initialized microphone driver\n"); - mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_CONTROL; + mic_driver_st.flags |= MICROPHONE_DRIVER_FLAG_ACTIVE; if ( mic_driver_st.microphone.active && !mic_driver_st.microphone.microphone_context) @@ -383,7 +382,7 @@ bool microphone_driver_get_mic_state(const retro_microphone_t *microphone) * * @see audio_driver_flush() */ -static void audio_driver_flush_microphone_input( +static void microphone_driver_flush( microphone_driver_state_t *mic_st, retro_microphone_t *microphone, float slowmotion_ratio, @@ -409,7 +408,7 @@ static void audio_driver_flush_microphone_input( microphone->microphone_context)) { void *buffer_source = NULL; - unsigned sample_size = mic_driver_get_sample_size(); + unsigned sample_size = mic_driver_get_sample_size(microphone); size_t bytes_to_read = MIN(mic_st->input_samples_buf_length, num_frames * sample_size); ssize_t bytes_read = mic_st->driver->read( mic_st->driver_context, @@ -450,7 +449,7 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size size_t frames_remaining = num_frames; microphone_driver_state_t *mic_st = &mic_driver_st; - if (mic_st->flags & MICROPHONE_DRIVER_FLAG_SUSPENDED + if (!(mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) || num_frames == 0 || !frames || !microphone @@ -476,19 +475,19 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size (frames_remaining > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)) ? (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1) : frames_remaining; - if (!( (runloop_flags & RUNLOOP_FLAG_PAUSED) - || !(mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) - || !(mic_st->input_samples_buf))) + if ((runloop_flags & RUNLOOP_FLAG_PAUSED) + && (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) + && mic_st->input_samples_buf) /* If the game is running, the audio driver and mic are running, * and the input sample buffer is valid... */ - audio_driver_flush_microphone_input(mic_st, - microphone, - config_get_ptr()->floats.slowmotion_ratio, - config_get_ptr()->bools.audio_fastforward_mute, - frames, - frames_to_read, - runloop_flags & RUNLOOP_FLAG_SLOWMOTION, - runloop_flags & RUNLOOP_FLAG_FASTMOTION); + microphone_driver_flush(mic_st, + microphone, + config_get_ptr()->floats.slowmotion_ratio, + config_get_ptr()->bools.audio_fastforward_mute, + frames, + frames_to_read, + runloop_flags & RUNLOOP_FLAG_SLOWMOTION, + runloop_flags & RUNLOOP_FLAG_FASTMOTION); frames_remaining -= frames_to_read; frames += frames_to_read << 1; } @@ -516,7 +515,7 @@ retro_microphone_t *microphone_driver_open_mic(void) if (!mic_driver) { - RARCH_ERR("[Microphone]: Failed to initialize microphone due to uninitialized driver\n"); + RARCH_ERR("[Microphone]: Failed to open microphone due to uninitialized driver\n"); return NULL; } @@ -524,13 +523,13 @@ retro_microphone_t *microphone_driver_open_mic(void) { /* Not checking mic_st->flags because they might not be set yet; * don't forget, the core can ask for a mic * before the audio driver is ready to create one. */ - RARCH_WARN("[Microphone]: Refused to initialize microphone because it's disabled in the settings\n"); + RARCH_WARN("[Microphone]: Refused to open microphone because it's disabled in the settings\n"); return NULL; } if (mic_st->microphone.active) { /* If the core has requested a second microphone... */ - RARCH_ERR("[Microphone]: Failed to initialize a second microphone, frontend only supports one at a time right now\n"); + RARCH_ERR("[Microphone]: Failed to open a second microphone, frontend only supports one at a time right now\n"); if (mic_st->microphone.microphone_context) /* If that mic is initialized... */ RARCH_ERR("[Microphone]: An initialized microphone exists\n"); @@ -553,7 +552,7 @@ retro_microphone_t *microphone_driver_open_mic(void) mic_driver_open_mic_internal(&mic_st->microphone); if (mic_st->microphone.microphone_context) /* If the microphone was successfully initialized... */ - RARCH_LOG("[Microphone]: Initialized the requested microphone successfully\n"); + RARCH_LOG("[Microphone]: Opened the requested microphone successfully\n"); else goto error; } @@ -620,6 +619,7 @@ static bool microphone_driver_deinit_internal(void) if (mic_st->input_samples_buf) memalign_free(mic_st->input_samples_buf); mic_st->input_samples_buf = NULL; + mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; return true; } diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index 356bae788af0..c71b0136eff0 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -27,11 +27,16 @@ enum microphone_driver_state_flags { + /** + * Indicates that the microphone driver is active. + * Microphones can be opened, closed, and read. + */ MICROPHONE_DRIVER_FLAG_ACTIVE = (1 << 0), - MICROPHONE_FLAG_USE_FLOAT = (1 << 1), - MICROPHONE_DRIVER_FLAG_SUSPENDED = (1 << 2), - MICROPHONE_FLAG_HARD_DISABLE = (1 << 3), - MICROPHONE_DRIVER_FLAG_CONTROL = (1 << 4), +}; + +enum microphone_state_flags +{ + MICROPHONE_FLAG_USE_FLOAT = (1 << 0) }; /** @@ -64,6 +69,8 @@ struct retro_microphone **/ size_t most_recent_copy_length; + enum microphone_state_flags flags; + /* May be enabled even before the driver is ready */ bool pending_enabled; From f1fe24cbe9c668f3d3e5e35c89baa5ed8f0d7ca1 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 16:43:00 -0500 Subject: [PATCH 141/309] Invert a flag check - Oops --- microphone/microphone_driver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index c81680e54ff5..90fc5ab21b20 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -475,9 +475,9 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size (frames_remaining > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)) ? (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1) : frames_remaining; - if ((runloop_flags & RUNLOOP_FLAG_PAUSED) - && (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) - && mic_st->input_samples_buf) + if (!(runloop_flags & RUNLOOP_FLAG_PAUSED) + && (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) + && mic_st->input_samples_buf) /* If the game is running, the audio driver and mic are running, * and the input sample buffer is valid... */ microphone_driver_flush(mic_st, From c1845063fd2b6ecb1ebcc584dd2c090a18e34164 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 17:03:23 -0500 Subject: [PATCH 142/309] Fix a log message --- microphone/microphone_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 90fc5ab21b20..56f1ea5d35a3 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -558,7 +558,7 @@ retro_microphone_t *microphone_driver_open_mic(void) } else { /* If the driver isn't ready to create a microphone... */ - RARCH_LOG("[Microphone]: Microphone requested before audio context was ready; deferring initialization\n"); + RARCH_LOG("[Microphone]: Microphone requested before driver context was ready; deferring initialization\n"); } return &mic_st->microphone; From 764f65c10b29ed3a5e19e4b58bf5c6e47329dda0 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 17:03:41 -0500 Subject: [PATCH 143/309] Fix the wrong flags being checked --- microphone/microphone_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 56f1ea5d35a3..8a8c009b2cbe 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -428,7 +428,7 @@ static void microphone_driver_flush( return; } - if (mic_st->flags & MICROPHONE_FLAG_USE_FLOAT) + if (microphone->flags & MICROPHONE_FLAG_USE_FLOAT) { convert_float_to_s16(mic_st->input_samples_conv_buf, mic_st->input_samples_buf, bytes_read / sample_size); buffer_source = mic_st->input_samples_conv_buf; From cb02d379a85c19d977de6ad5556285163a8438a6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 25 Jan 2023 20:30:24 -0500 Subject: [PATCH 144/309] Slight refactor of wasapi_init_device - Add a data_flow parameter - Declare it in a header - In preparation for WASAPI mic support --- audio/drivers/wasapi.c | 11 ++++++----- audio/drivers/wasapi.h | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 audio/drivers/wasapi.h diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index 5f47fd2dd8c5..e972874bb983 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -19,6 +19,7 @@ #include #include +#include "./wasapi.h" #include "../common/mmdevice_common.h" #include "../common/mmdevice_common_inline.h" @@ -40,7 +41,7 @@ typedef struct bool running; } wasapi_t; -static IMMDevice *wasapi_init_device(const char *id) +IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) { HRESULT hr; UINT32 dev_count, i; @@ -104,7 +105,7 @@ static IMMDevice *wasapi_init_device(const char *id) idx_found = 0; hr = _IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, - eRender, DEVICE_STATE_ACTIVE, &collection); + data_flow, DEVICE_STATE_ACTIVE, &collection); if (FAILED(hr)) goto error; @@ -127,7 +128,7 @@ static IMMDevice *wasapi_init_device(const char *id) else { hr = _IMMDeviceEnumerator_GetDefaultAudioEndpoint( - enumerator, eRender, eConsole, &device); + enumerator, data_flow, eConsole, &device); if (FAILED(hr)) goto error; } @@ -499,9 +500,9 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency, return NULL; w->exclusive = exclusive_mode; - w->device = wasapi_init_device(dev_id); + w->device = wasapi_init_device(dev_id, eRender); if (!w->device && dev_id) - w->device = wasapi_init_device(NULL); + w->device = wasapi_init_device(NULL, eRender); if (!w->device) goto error; diff --git a/audio/drivers/wasapi.h b/audio/drivers/wasapi.h new file mode 100644 index 000000000000..8e5c938068c0 --- /dev/null +++ b/audio/drivers/wasapi.h @@ -0,0 +1,24 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + + +#ifndef RETROARCH_WASAPI_H +#define RETROARCH_WASAPI_H + +#include "../common/mmdevice_common_inline.h" + +IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow); + +#endif /* RETROARCH_WASAPI_H */ \ No newline at end of file From d9e8a46298d11c625d84d1e39ff204836d7eaa2e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:16:43 -0500 Subject: [PATCH 145/309] Add some WASAPI macros for _IAudioCaptureClient --- audio/common/mmdevice_common_inline.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/audio/common/mmdevice_common_inline.h b/audio/common/mmdevice_common_inline.h index df0f219d6f5e..33395cb08354 100644 --- a/audio/common/mmdevice_common_inline.h +++ b/audio/common/mmdevice_common_inline.h @@ -67,6 +67,10 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 #define _IMMDevice_GetId(This,ppstrId) ((This)->GetId(ppstrId)) #define _IPropertyStore_GetValue(This,key,pv) ( (This)->GetValue(key,pv) ) #define _IMMDeviceCollection_GetCount(This,cProps) ( (This)->GetCount(cProps) ) +#define _IAudioCaptureClient_GetBuffer(This,ppData,pNumFramesToRead,pdwFlags,pu64DevicePosition,pu64QPCPosition) \ + ( (This) -> GetBuffer(ppData,pNumFramesToRead,pdwFlags,pu64DevicePosition,pu64QPCPosition) ) +#define _IAudioCaptureClient_ReleaseBuffer(This,NumFramesRead) \ + ( (This) -> ReleaseBuffer(NumFramesRead) ) #else #define _IMMDeviceCollection_Item(This,nDevice,ppdevice) (This)->lpVtbl->Item(This,nDevice,ppdevice) #define _IAudioClient_Start(This) ( (This)->lpVtbl -> Start(This) ) @@ -89,6 +93,10 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 #define _IMMDevice_GetId(This,ppstrId) (This)->lpVtbl->GetId(This,ppstrId) #define _IPropertyStore_GetValue(This,key,pv) ( (This)->lpVtbl -> GetValue(This,&(key),pv) ) #define _IMMDeviceCollection_GetCount(This,cProps) ( (This)->lpVtbl -> GetCount(This,cProps) ) +#define _IAudioCaptureClient_GetBuffer(This,ppData,pNumFramesToRead,pdwFlags,pu64DevicePosition,pu64QPCPosition) \ + ( (This)->lpVtbl -> GetBuffer(This,ppData,pNumFramesToRead,pdwFlags,pu64DevicePosition,pu64QPCPosition) ) +#define _IAudioCaptureClient_ReleaseBuffer(This,NumFramesRead) \ + ( (This)->lpVtbl -> ReleaseBuffer(This,NumFramesRead) ) #endif #ifdef __cplusplus From 6cc352eaf1131e7650b17fee042cbe2f3e022c40 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:18:15 -0500 Subject: [PATCH 146/309] Move some common WASAPI functions to audio/common/wasapi.c - They'll be used by the mic and the audio drivers --- Makefile.common | 3 +- audio/common/wasapi.c | 466 +++++++++++++++++++++++++++++ audio/{drivers => common}/wasapi.h | 15 +- audio/drivers/wasapi.c | 444 +-------------------------- 4 files changed, 481 insertions(+), 447 deletions(-) create mode 100644 audio/common/wasapi.c rename audio/{drivers => common}/wasapi.h (65%) diff --git a/Makefile.common b/Makefile.common index 22045fd7247e..b1d3e92b6e62 100644 --- a/Makefile.common +++ b/Makefile.common @@ -940,7 +940,8 @@ endif ifeq ($(HAVE_WASAPI), 1) HAVE_MMDEVAPI = 1 - OBJ += audio/drivers/wasapi.o + OBJ += audio/drivers/wasapi.o \ + audio/common/wasapi.o DEFINES += -DHAVE_WASAPI LIBS += -lole32 -lksuser endif diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c new file mode 100644 index 000000000000..d007c786ee9c --- /dev/null +++ b/audio/common/wasapi.c @@ -0,0 +1,466 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 Daniel De Matteis + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "wasapi.h" +#include +#include "microphone/microphone_driver.h" +#include "queues/fifo_queue.h" +#include "configuration.h" +#include "verbosity.h" +#include "string/stdstring.h" +#include "mmdevice_common.h" + +static unsigned wasapi_pref_rate(unsigned i) +{ + const unsigned r[] = { 48000, 44100, 96000, 192000, 32000 }; + + if (i >= sizeof(r) / sizeof(unsigned)) + return 0; + + return r[i]; +} + +static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, + bool float_fmt, unsigned rate) +{ + wf->Format.nChannels = 2; + wf->Format.nSamplesPerSec = rate; + + if (float_fmt) + { + wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf->Format.nAvgBytesPerSec = rate * 8; + wf->Format.nBlockAlign = 8; + wf->Format.wBitsPerSample = 32; + wf->Format.cbSize = sizeof(WORD) + sizeof(DWORD) + sizeof(GUID); + wf->Samples.wValidBitsPerSample = 32; + wf->dwChannelMask = KSAUDIO_SPEAKER_STEREO; + wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + else + { + wf->Format.wFormatTag = WAVE_FORMAT_PCM; + wf->Format.nAvgBytesPerSec = rate * 4; + wf->Format.nBlockAlign = 4; + wf->Format.wBitsPerSample = 16; + wf->Format.cbSize = 0; + } +} + +static IAudioClient *wasapi_init_client_ex(IMMDevice *device, + bool *float_fmt, unsigned *rate, unsigned latency) +{ + WAVEFORMATEXTENSIBLE wf; + int i, j; + IAudioClient *client = NULL; + bool float_fmt_res = *float_fmt; + unsigned rate_res = *rate; + REFERENCE_TIME minimum_period = 0; + REFERENCE_TIME buffer_duration = 0; + UINT32 buffer_length = 0; + HRESULT hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + return NULL; + + hr = _IAudioClient_GetDevicePeriod(client, NULL, &minimum_period); + if (FAILED(hr)) + goto error; + + /* buffer_duration is in 100ns units */ + buffer_duration = latency * 10000.0; + if (buffer_duration < minimum_period) + buffer_duration = minimum_period; + + /* once for float, once for pcm (requested first) */ + for (i = 0; i < 2; ++i) + { + rate_res = *rate; + if (i == 1) + float_fmt_res = !float_fmt_res; + + /* for requested rate (first) and all preferred rates */ + for (j = 0; rate_res; ++j) + { + RARCH_LOG("[WASAPI]: Initializing client (exclusive, %s, %uHz, %ums) ...\n", + float_fmt_res ? "float" : "pcm", rate_res, latency); + + wasapi_set_format(&wf, float_fmt_res, rate_res); +#ifdef __cplusplus + hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); +#else + hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); +#endif + if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) + { + hr = _IAudioClient_GetBufferSize(client, &buffer_length); + if (FAILED(hr)) + goto error; + + IFACE_RELEASE(client); + hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + return NULL; + + buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; +#ifdef __cplusplus + hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); +#else + hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); +#endif + } + if (hr == AUDCLNT_E_ALREADY_INITIALIZED) + { + IFACE_RELEASE(client); + hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + return NULL; + +#ifdef __cplusplus + hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); +#else + hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); +#endif + } + if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) + { + if (hr == AUDCLNT_E_DEVICE_IN_USE) + goto error; + + if (hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) + goto error; + + i = 2; /* break from outer loop too */ + break; + } + + RARCH_WARN("[WASAPI]: Unsupported format.\n"); + rate_res = wasapi_pref_rate(j); + if (rate_res == *rate) /* requested rate is allready tested */ + rate_res = wasapi_pref_rate(++j); /* skip it */ + } + } + + if (FAILED(hr)) + goto error; + + *float_fmt = float_fmt_res; + *rate = rate_res; + + return client; + +error: + IFACE_RELEASE(client); + + return NULL; +} + +static IAudioClient *wasapi_init_client_sh(IMMDevice *device, + bool *float_fmt, unsigned *rate, unsigned latency) +{ + WAVEFORMATEXTENSIBLE wf; + int i, j; + IAudioClient *client = NULL; + bool float_fmt_res = *float_fmt; + unsigned rate_res = *rate; + HRESULT hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + return NULL; + + /* once for float, once for pcm (requested first) */ + for (i = 0; i < 2; ++i) + { + rate_res = *rate; + if (i == 1) + float_fmt_res = !float_fmt_res; + + /* for requested rate (first) and all preferred rates */ + for (j = 0; rate_res; ++j) + { + RARCH_LOG("[WASAPI]: Initializing client (shared, %s, %uHz, %ums) ...\n", + float_fmt_res ? "float" : "pcm", rate_res, latency); + + wasapi_set_format(&wf, float_fmt_res, rate_res); +#ifdef __cplusplus + hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + 0, 0, (WAVEFORMATEX*)&wf, NULL); +#else + hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + 0, 0, (WAVEFORMATEX*)&wf, NULL); +#endif + + if (hr == AUDCLNT_E_ALREADY_INITIALIZED) + { + HRESULT hr; + IFACE_RELEASE(client); + hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + return NULL; + +#ifdef __cplusplus + hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + 0, 0, (WAVEFORMATEX*)&wf, NULL); +#else + hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + 0, 0, (WAVEFORMATEX*)&wf, NULL); +#endif + } + if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) + { + i = 2; /* break from outer loop too */ + break; + } + + RARCH_WARN("[WASAPI]: Unsupported format.\n"); + rate_res = wasapi_pref_rate(j); + if (rate_res == *rate) /* requested rate is allready tested */ + rate_res = wasapi_pref_rate(++j); /* skip it */ + } + } + + if (FAILED(hr)) + goto error; + + *float_fmt = float_fmt_res; + *rate = rate_res; + + return client; + +error: + IFACE_RELEASE(client); + + return NULL; +} + +IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) +{ + HRESULT hr; + UINT32 dev_count, i; + IMMDeviceEnumerator *enumerator = NULL; + IMMDevice *device = NULL; + IMMDeviceCollection *collection = NULL; + + if (id) + { + RARCH_LOG("[WASAPI]: Initializing device %s ...\n", id); + } + else + { + RARCH_LOG("[WASAPI]: Initializing default device.. \n"); + } + +#ifdef __cplusplus + hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + IID_IMMDeviceEnumerator, (void **)&enumerator); +#else + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + &IID_IMMDeviceEnumerator, (void **)&enumerator); +#endif + if (FAILED(hr)) + goto error; + + if (id) + { + int32_t idx_found = -1; + struct string_list *list = (struct string_list*)mmdevice_list_new(NULL); + + /* Search for device name first */ + if (list) + { + if (list->elems) + { + unsigned i; + for (i = 0; i < list->size; i++) + { + RARCH_LOG("[WASAPI]: %d : %s\n", i, list->elems[i].data); + if (string_is_equal(id, list->elems[i].data)) + { + idx_found = i; + break; + } + } + /* Index was not found yet based on name string, + * just assume id is a one-character number index. */ + + if (idx_found == -1 && isdigit(id[0])) + { + idx_found = strtoul(id, NULL, 0); + RARCH_LOG("[WASAPI]: Fallback, device index is a single number index instead: %d.\n", idx_found); + + } + } + string_list_free(list); + } + + if (idx_found == -1) + idx_found = 0; + + hr = _IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, + data_flow, DEVICE_STATE_ACTIVE, &collection); + if (FAILED(hr)) + goto error; + + hr = _IMMDeviceCollection_GetCount(collection, &dev_count); + if (FAILED(hr)) + goto error; + + for (i = 0; i < dev_count; ++i) + { + hr = _IMMDeviceCollection_Item(collection, i, &device); + if (FAILED(hr)) + goto error; + + if (i == idx_found) + break; + + IFACE_RELEASE(device); + } + } + else + { + hr = _IMMDeviceEnumerator_GetDefaultAudioEndpoint( + enumerator, data_flow, eConsole, &device); + if (FAILED(hr)) + goto error; + } + + if (!device) + goto error; + + IFACE_RELEASE(collection); + IFACE_RELEASE(enumerator); + + return device; + + error: + IFACE_RELEASE(collection); + IFACE_RELEASE(enumerator); + + if (id) + { + RARCH_WARN("[WASAPI]: Failed to initialize device.\n"); + } + else + { + RARCH_ERR("[WASAPI]: Failed to initialize device.\n"); + } + + return NULL; +} + +IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, + bool *float_fmt, unsigned *rate, unsigned latency) +{ + HRESULT hr; + IAudioClient *client; + double latency_res; + REFERENCE_TIME device_period = 0; + REFERENCE_TIME stream_latency = 0; + UINT32 buffer_length = 0; + + if (*exclusive) + { + client = wasapi_init_client_ex(device, float_fmt, rate, latency); + if (!client) + { + client = wasapi_init_client_sh(device, float_fmt, rate, latency); + if (client) + *exclusive = false; + } + } + else + { + client = wasapi_init_client_sh(device, float_fmt, rate, latency); + if (!client) + { + client = wasapi_init_client_ex(device, float_fmt, rate, latency); + if (client) + *exclusive = true; + } + } + + if (!client) + return NULL; + + /* next calls are allowed to fail (we losing info only) */ + + if (*exclusive) + hr = _IAudioClient_GetDevicePeriod(client, NULL, &device_period); + else + hr = _IAudioClient_GetDevicePeriod(client, &device_period, NULL); + + if (FAILED(hr)) + { + RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed with error 0x%.8X.\n", hr); + } + + if (!*exclusive) + { + hr = _IAudioClient_GetStreamLatency(client, &stream_latency); + if (FAILED(hr)) + { + RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed with error 0x%.8X.\n", hr); + } + } + + hr = _IAudioClient_GetBufferSize(client, &buffer_length); + if (FAILED(hr)) + { + RARCH_WARN("[WASAPI]: IAudioClient::GetBufferSize failed with error 0x%.8X.\n", hr); + } + + if (*exclusive) + latency_res = (double)buffer_length * 1000.0 / (*rate); + else + latency_res = (double)(stream_latency + device_period) / 10000.0; + + RARCH_LOG("[WASAPI]: Client initialized (%s, %s, %uHz, %.1fms).\n", + *exclusive ? "exclusive" : "shared", + *float_fmt ? "float" : "pcm", *rate, latency_res); + + RARCH_LOG("[WASAPI]: Client's buffer length is %u frames (%.1fms).\n", + buffer_length, (double)buffer_length * 1000.0 / (*rate)); + + RARCH_LOG("[WASAPI]: Device period is %.1fms (%lld frames).\n", + (double)device_period / 10000.0, device_period * (*rate) / 10000000); + + return client; +} \ No newline at end of file diff --git a/audio/drivers/wasapi.h b/audio/common/wasapi.h similarity index 65% rename from audio/drivers/wasapi.h rename to audio/common/wasapi.h index 8e5c938068c0..f1fae24dc8c5 100644 --- a/audio/drivers/wasapi.h +++ b/audio/common/wasapi.h @@ -1,4 +1,5 @@ /* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 Daniel De Matteis * Copyright (C) 2023 Jesse Talavera-Greenberg * * RetroArch is free software: you can redistribute it and/or modify it under the terms @@ -13,12 +14,20 @@ * If not, see . */ +/** + * Contains WASAPI-specific support functions that are used + * by the WASAPI audio and microphone drivers. + * + */ -#ifndef RETROARCH_WASAPI_H -#define RETROARCH_WASAPI_H +#ifndef RETROARCH_COMMON_WASAPI_H +#define RETROARCH_COMMON_WASAPI_H #include "../common/mmdevice_common_inline.h" +#include "boolean.h" IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow); +IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, + bool *float_fmt, unsigned *rate, unsigned latency); -#endif /* RETROARCH_WASAPI_H */ \ No newline at end of file +#endif /* RETROARCH_COMMON_WASAPI_H */ \ No newline at end of file diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index e972874bb983..38d517fbc19f 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -19,9 +19,9 @@ #include #include -#include "./wasapi.h" #include "../common/mmdevice_common.h" #include "../common/mmdevice_common_inline.h" +#include "../common/wasapi.h" #include "../audio_driver.h" #include "../../verbosity.h" @@ -41,448 +41,6 @@ typedef struct bool running; } wasapi_t; -IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) -{ - HRESULT hr; - UINT32 dev_count, i; - IMMDeviceEnumerator *enumerator = NULL; - IMMDevice *device = NULL; - IMMDeviceCollection *collection = NULL; - - if (id) - { - RARCH_LOG("[WASAPI]: Initializing device %s ...\n", id); - } - else - { - RARCH_LOG("[WASAPI]: Initializing default device.. \n"); - } - -#ifdef __cplusplus - hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - IID_IMMDeviceEnumerator, (void **)&enumerator); -#else - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, - &IID_IMMDeviceEnumerator, (void **)&enumerator); -#endif - if (FAILED(hr)) - goto error; - - if (id) - { - int32_t idx_found = -1; - struct string_list *list = (struct string_list*)mmdevice_list_new(NULL); - - /* Search for device name first */ - if (list) - { - if (list->elems) - { - unsigned i; - for (i = 0; i < list->size; i++) - { - RARCH_LOG("[WASAPI]: %d : %s\n", i, list->elems[i].data); - if (string_is_equal(id, list->elems[i].data)) - { - idx_found = i; - break; - } - } - /* Index was not found yet based on name string, - * just assume id is a one-character number index. */ - - if (idx_found == -1 && isdigit(id[0])) - { - idx_found = strtoul(id, NULL, 0); - RARCH_LOG("[WASAPI]: Fallback, device index is a single number index instead: %d.\n", idx_found); - - } - } - string_list_free(list); - } - - if (idx_found == -1) - idx_found = 0; - - hr = _IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, - data_flow, DEVICE_STATE_ACTIVE, &collection); - if (FAILED(hr)) - goto error; - - hr = _IMMDeviceCollection_GetCount(collection, &dev_count); - if (FAILED(hr)) - goto error; - - for (i = 0; i < dev_count; ++i) - { - hr = _IMMDeviceCollection_Item(collection, i, &device); - if (FAILED(hr)) - goto error; - - if (i == idx_found) - break; - - IFACE_RELEASE(device); - } - } - else - { - hr = _IMMDeviceEnumerator_GetDefaultAudioEndpoint( - enumerator, data_flow, eConsole, &device); - if (FAILED(hr)) - goto error; - } - - if (!device) - goto error; - - IFACE_RELEASE(collection); - IFACE_RELEASE(enumerator); - - return device; - -error: - IFACE_RELEASE(collection); - IFACE_RELEASE(enumerator); - - if (id) - { - RARCH_WARN("[WASAPI]: Failed to initialize device.\n"); - } - else - { - RARCH_ERR("[WASAPI]: Failed to initialize device.\n"); - } - - return NULL; -} - -static unsigned wasapi_pref_rate(unsigned i) -{ - const unsigned r[] = { 48000, 44100, 96000, 192000, 32000 }; - - if (i >= sizeof(r) / sizeof(unsigned)) - return 0; - - return r[i]; -} - -static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, - bool float_fmt, unsigned rate) -{ - wf->Format.nChannels = 2; - wf->Format.nSamplesPerSec = rate; - - if (float_fmt) - { - wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf->Format.nAvgBytesPerSec = rate * 8; - wf->Format.nBlockAlign = 8; - wf->Format.wBitsPerSample = 32; - wf->Format.cbSize = sizeof(WORD) + sizeof(DWORD) + sizeof(GUID); - wf->Samples.wValidBitsPerSample = 32; - wf->dwChannelMask = KSAUDIO_SPEAKER_STEREO; - wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } - else - { - wf->Format.wFormatTag = WAVE_FORMAT_PCM; - wf->Format.nAvgBytesPerSec = rate * 4; - wf->Format.nBlockAlign = 4; - wf->Format.wBitsPerSample = 16; - wf->Format.cbSize = 0; - } -} - -static IAudioClient *wasapi_init_client_sh(IMMDevice *device, - bool *float_fmt, unsigned *rate, unsigned latency) -{ - WAVEFORMATEXTENSIBLE wf; - int i, j; - IAudioClient *client = NULL; - bool float_fmt_res = *float_fmt; - unsigned rate_res = *rate; - HRESULT hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - return NULL; - - /* once for float, once for pcm (requested first) */ - for (i = 0; i < 2; ++i) - { - rate_res = *rate; - if (i == 1) - float_fmt_res = !float_fmt_res; - - /* for requested rate (first) and all preferred rates */ - for (j = 0; rate_res; ++j) - { - RARCH_LOG("[WASAPI]: Initializing client (shared, %s, %uHz, %ums) ...\n", - float_fmt_res ? "float" : "pcm", rate_res, latency); - - wasapi_set_format(&wf, float_fmt_res, rate_res); -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); -#endif - - if (hr == AUDCLNT_E_ALREADY_INITIALIZED) - { - HRESULT hr; - IFACE_RELEASE(client); - hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - return NULL; - -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); -#endif - } - if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) - { - i = 2; /* break from outer loop too */ - break; - } - - RARCH_WARN("[WASAPI]: Unsupported format.\n"); - rate_res = wasapi_pref_rate(j); - if (rate_res == *rate) /* requested rate is allready tested */ - rate_res = wasapi_pref_rate(++j); /* skip it */ - } - } - - if (FAILED(hr)) - goto error; - - *float_fmt = float_fmt_res; - *rate = rate_res; - - return client; - -error: - IFACE_RELEASE(client); - - return NULL; -} - -static IAudioClient *wasapi_init_client_ex(IMMDevice *device, - bool *float_fmt, unsigned *rate, unsigned latency) -{ - WAVEFORMATEXTENSIBLE wf; - int i, j; - IAudioClient *client = NULL; - bool float_fmt_res = *float_fmt; - unsigned rate_res = *rate; - REFERENCE_TIME minimum_period = 0; - REFERENCE_TIME buffer_duration = 0; - UINT32 buffer_length = 0; - HRESULT hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - return NULL; - - hr = _IAudioClient_GetDevicePeriod(client, NULL, &minimum_period); - if (FAILED(hr)) - goto error; - - /* buffer_duration is in 100ns units */ - buffer_duration = latency * 10000.0; - if (buffer_duration < minimum_period) - buffer_duration = minimum_period; - - /* once for float, once for pcm (requested first) */ - for (i = 0; i < 2; ++i) - { - rate_res = *rate; - if (i == 1) - float_fmt_res = !float_fmt_res; - - /* for requested rate (first) and all preferred rates */ - for (j = 0; rate_res; ++j) - { - RARCH_LOG("[WASAPI]: Initializing client (exclusive, %s, %uHz, %ums) ...\n", - float_fmt_res ? "float" : "pcm", rate_res, latency); - - wasapi_set_format(&wf, float_fmt_res, rate_res); -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#endif - if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) - { - hr = _IAudioClient_GetBufferSize(client, &buffer_length); - if (FAILED(hr)) - goto error; - - IFACE_RELEASE(client); - hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - return NULL; - - buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#endif - } - if (hr == AUDCLNT_E_ALREADY_INITIALIZED) - { - IFACE_RELEASE(client); - hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - return NULL; - -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#endif - } - if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) - { - if (hr == AUDCLNT_E_DEVICE_IN_USE) - goto error; - - if (hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) - goto error; - - i = 2; /* break from outer loop too */ - break; - } - - RARCH_WARN("[WASAPI]: Unsupported format.\n"); - rate_res = wasapi_pref_rate(j); - if (rate_res == *rate) /* requested rate is allready tested */ - rate_res = wasapi_pref_rate(++j); /* skip it */ - } - } - - if (FAILED(hr)) - goto error; - - *float_fmt = float_fmt_res; - *rate = rate_res; - - return client; - -error: - IFACE_RELEASE(client); - - return NULL; -} - -static IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, - bool *float_fmt, unsigned *rate, unsigned latency) -{ - HRESULT hr; - IAudioClient *client; - double latency_res; - REFERENCE_TIME device_period = 0; - REFERENCE_TIME stream_latency = 0; - UINT32 buffer_length = 0; - - if (*exclusive) - { - client = wasapi_init_client_ex(device, float_fmt, rate, latency); - if (!client) - { - client = wasapi_init_client_sh(device, float_fmt, rate, latency); - if (client) - *exclusive = false; - } - } - else - { - client = wasapi_init_client_sh(device, float_fmt, rate, latency); - if (!client) - { - client = wasapi_init_client_ex(device, float_fmt, rate, latency); - if (client) - *exclusive = true; - } - } - - if (!client) - return NULL; - - /* next calls are allowed to fail (we losing info only) */ - - if (*exclusive) - hr = _IAudioClient_GetDevicePeriod(client, NULL, &device_period); - else - hr = _IAudioClient_GetDevicePeriod(client, &device_period, NULL); - - if (FAILED(hr)) - { - RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed with error 0x%.8X.\n", hr); - } - - if (!*exclusive) - { - hr = _IAudioClient_GetStreamLatency(client, &stream_latency); - if (FAILED(hr)) - { - RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed with error 0x%.8X.\n", hr); - } - } - - hr = _IAudioClient_GetBufferSize(client, &buffer_length); - if (FAILED(hr)) - { - RARCH_WARN("[WASAPI]: IAudioClient::GetBufferSize failed with error 0x%.8X.\n", hr); - } - - if (*exclusive) - latency_res = (double)buffer_length * 1000.0 / (*rate); - else - latency_res = (double)(stream_latency + device_period) / 10000.0; - - RARCH_LOG("[WASAPI]: Client initialized (%s, %s, %uHz, %.1fms).\n", - *exclusive ? "exclusive" : "shared", - *float_fmt ? "float" : "pcm", *rate, latency_res); - - RARCH_LOG("[WASAPI]: Client's buffer length is %u frames (%.1fms).\n", - buffer_length, (double)buffer_length * 1000.0 / (*rate)); - - RARCH_LOG("[WASAPI]: Device period is %.1fms (%lld frames).\n", - (double)device_period / 10000.0, device_period * (*rate) / 10000000); - - return client; -} - static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency, unsigned u1, unsigned *u2) { From 04e25dc9a1354a2f41ed11ecc7bfbaa1535e940c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:20:17 -0500 Subject: [PATCH 147/309] Add wasapi_log_hr --- audio/common/wasapi.c | 14 ++++++++++++++ audio/common/wasapi.h | 1 + 2 files changed, 15 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index d007c786ee9c..39383c787581 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -23,6 +23,20 @@ #include "string/stdstring.h" #include "mmdevice_common.h" +static void wasapi_log_hr(HRESULT hr, char* buffer, size_t length) +{ + FormatMessage( + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_FROM_HMODULE, + NULL, + hr, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + buffer, + length - 1, + NULL); +} + static unsigned wasapi_pref_rate(unsigned i) { const unsigned r[] = { 48000, 44100, 96000, 192000, 32000 }; diff --git a/audio/common/wasapi.h b/audio/common/wasapi.h index f1fae24dc8c5..88ce61f3c8b5 100644 --- a/audio/common/wasapi.h +++ b/audio/common/wasapi.h @@ -26,6 +26,7 @@ #include "../common/mmdevice_common_inline.h" #include "boolean.h" +void wasapi_log_hr(HRESULT hr, char* buffer, size_t length); IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow); IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, bool *float_fmt, unsigned *rate, unsigned latency); From e187d9fa7e0a94d6f4ae8d341f6a080fea7a3919 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:39:53 -0500 Subject: [PATCH 148/309] Generalize mmdevice_list_new to look for capture devices, too --- audio/common/mmdevice_common.c | 14 ++++++++++++-- audio/common/mmdevice_common.h | 5 ++++- audio/drivers/wasapi.c | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/audio/common/mmdevice_common.c b/audio/common/mmdevice_common.c index 6a09aa583a19..23e67f5f195e 100644 --- a/audio/common/mmdevice_common.c +++ b/audio/common/mmdevice_common.c @@ -21,7 +21,17 @@ #include "mmdevice_common.h" #include "mmdevice_common_inline.h" -void *mmdevice_list_new(void *u) +void *mmdevice_list_new_render(void *u) +{ + return mmdevice_list_new(u, eRender); +} + +void *mmdevice_list_new_capture(void *u) +{ + return mmdevice_list_new(u, eCapture); +} + +void *mmdevice_list_new(void *u, EDataFlow data_flow) { HRESULT hr; UINT i; @@ -54,7 +64,7 @@ void *mmdevice_list_new(void *u) goto error; hr = _IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, - eRender, DEVICE_STATE_ACTIVE, &collection); + data_flow, DEVICE_STATE_ACTIVE, &collection); if (FAILED(hr)) goto error; diff --git a/audio/common/mmdevice_common.h b/audio/common/mmdevice_common.h index 647f36cd3856..d54a6c21d4d7 100644 --- a/audio/common/mmdevice_common.h +++ b/audio/common/mmdevice_common.h @@ -17,10 +17,13 @@ #define _MMDEVICE_COMMON_H #include +#include "mmdevice_common_inline.h" RETRO_BEGIN_DECLS -void *mmdevice_list_new(void *u); +void *mmdevice_list_new_render(void *u); +void *mmdevice_list_new_capture(void *u); +void *mmdevice_list_new(void *u, EDataFlow data_flow); RETRO_END_DECLS diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index 38d517fbc19f..88e6ad07d9c2 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -477,7 +477,7 @@ audio_driver_t audio_wasapi = { wasapi_free, wasapi_use_float, "wasapi", - mmdevice_list_new, + mmdevice_list_new_render, wasapi_device_list_free, wasapi_write_avail, wasapi_buffer_size From 1efc0b900c42744c7de2f88edfbc48f0ee2da252 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:43:36 -0500 Subject: [PATCH 149/309] Fix a function declaration --- audio/common/wasapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 39383c787581..e34f41078b97 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -23,7 +23,7 @@ #include "string/stdstring.h" #include "mmdevice_common.h" -static void wasapi_log_hr(HRESULT hr, char* buffer, size_t length) +void wasapi_log_hr(HRESULT hr, char* buffer, size_t length) { FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | From f9c2218fac4ce1694203a69904ce358acd54b61e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:44:23 -0500 Subject: [PATCH 150/309] Move driver-specific device_list_new functions into their respective files --- audio/common/mmdevice_common.c | 10 ---------- audio/common/wasapi.c | 2 +- audio/drivers/wasapi.c | 7 ++++++- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/audio/common/mmdevice_common.c b/audio/common/mmdevice_common.c index 23e67f5f195e..4f8ff9461d3a 100644 --- a/audio/common/mmdevice_common.c +++ b/audio/common/mmdevice_common.c @@ -21,16 +21,6 @@ #include "mmdevice_common.h" #include "mmdevice_common_inline.h" -void *mmdevice_list_new_render(void *u) -{ - return mmdevice_list_new(u, eRender); -} - -void *mmdevice_list_new_capture(void *u) -{ - return mmdevice_list_new(u, eCapture); -} - void *mmdevice_list_new(void *u, EDataFlow data_flow) { HRESULT hr; diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index e34f41078b97..c3fb4e9439f0 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -314,7 +314,7 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) if (id) { int32_t idx_found = -1; - struct string_list *list = (struct string_list*)mmdevice_list_new(NULL); + struct string_list *list = (struct string_list*)mmdevice_list_new(NULL, data_flow); /* Search for device name first */ if (list) diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index 88e6ad07d9c2..a5a156542172 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -467,6 +467,11 @@ static size_t wasapi_buffer_size(void *wh) return w->engine_buffer_size; } +static void *wasapi_device_list_new(void *u) +{ + return mmdevice_list_new(u, eRender); +} + audio_driver_t audio_wasapi = { wasapi_init, wasapi_write, @@ -477,7 +482,7 @@ audio_driver_t audio_wasapi = { wasapi_free, wasapi_use_float, "wasapi", - mmdevice_list_new_render, + wasapi_device_list_new, wasapi_device_list_free, wasapi_write_avail, wasapi_buffer_size From 7ab6f8ebfa7f81e5e7661b98ea7b38b923c4b343 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:45:29 -0500 Subject: [PATCH 151/309] Clean up some declarations --- audio/common/mmdevice_common.c | 2 +- audio/common/mmdevice_common.h | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/audio/common/mmdevice_common.c b/audio/common/mmdevice_common.c index 4f8ff9461d3a..1dc8d155fc05 100644 --- a/audio/common/mmdevice_common.c +++ b/audio/common/mmdevice_common.c @@ -21,7 +21,7 @@ #include "mmdevice_common.h" #include "mmdevice_common_inline.h" -void *mmdevice_list_new(void *u, EDataFlow data_flow) +void *mmdevice_list_new(const void *u, EDataFlow data_flow) { HRESULT hr; UINT i; diff --git a/audio/common/mmdevice_common.h b/audio/common/mmdevice_common.h index d54a6c21d4d7..b77d8805c25e 100644 --- a/audio/common/mmdevice_common.h +++ b/audio/common/mmdevice_common.h @@ -21,9 +21,7 @@ RETRO_BEGIN_DECLS -void *mmdevice_list_new_render(void *u); -void *mmdevice_list_new_capture(void *u); -void *mmdevice_list_new(void *u, EDataFlow data_flow); +void *mmdevice_list_new(const void *u, EDataFlow data_flow); RETRO_END_DECLS From c7890f30addc8e843dea90b2d0cabf7727da0ac0 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:45:43 -0500 Subject: [PATCH 152/309] First draft of wasapi microphone driver --- Makefile.common | 3 +- microphone/drivers/wasapi.c | 250 ++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 microphone/drivers/wasapi.c diff --git a/Makefile.common b/Makefile.common index b1d3e92b6e62..0e75917e19c1 100644 --- a/Makefile.common +++ b/Makefile.common @@ -941,7 +941,8 @@ endif ifeq ($(HAVE_WASAPI), 1) HAVE_MMDEVAPI = 1 OBJ += audio/drivers/wasapi.o \ - audio/common/wasapi.o + audio/common/wasapi.o \ + microphone/drivers/wasapi.o DEFINES += -DHAVE_WASAPI LIBS += -lole32 -lksuser endif diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c new file mode 100644 index 000000000000..bed21dc7d1b3 --- /dev/null +++ b/microphone/drivers/wasapi.c @@ -0,0 +1,250 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include "audio/common/wasapi.h" +#include "microphone/microphone_driver.h" +#include "queues/fifo_queue.h" +#include "configuration.h" +#include "verbosity.h" +#include "audio/common/mmdevice_common.h" + +typedef struct +{ + HANDLE write_event; + IMMDevice *device; + IAudioClient *client; + IAudioCaptureClient *capture; + fifo_buffer_t *buffer; /* NULL in unbuffered shared mode */ + size_t frame_size; /* 2 or 4 only */ + size_t engine_buffer_size; + bool exclusive; +} wasapi_microphone_handle_t; + +typedef struct wasapi_microphone +{ + wasapi_microphone_handle_t *microphone; + bool nonblock; + bool running; +} wasapi_microphone_t; + + +static void wasapi_microphone_close_mic(void *driver_context, void *microphone_context); + +static void *wasapi_microphone_init(void) +{ + settings_t *settings = config_get_ptr(); + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)calloc(1, sizeof(wasapi_microphone_t)); + + if (!wasapi) + { + RARCH_ERR("[WASAPI mic]: Failed to allocate microphone driver context\n"); + return NULL; + } + + wasapi->running = true; + wasapi->nonblock = !settings->bools.audio_sync; + RARCH_DBG("[WASAPI mic]: Initialized microphone driver context\n"); + + return wasapi; +} + + +static void wasapi_microphone_free(void *driver_context) +{ + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + + if (!wasapi) + return; + + if (wasapi->microphone) + { + wasapi_microphone_close_mic(wasapi, wasapi->microphone); + } + + free(wasapi); +} + +static void *wasapi_microphone_open_mic(void *driver_context, const char *dev_id, unsigned rate, unsigned latency, + unsigned u1, unsigned *u2) +{ + char error_message[256] = {0}; + settings_t *settings = config_get_ptr(); + HRESULT hr; + UINT32 frame_count = 0; + REFERENCE_TIME dev_period = 0; + BYTE *dest = NULL; + bool float_format = settings->bools.audio_wasapi_float_format; + bool exclusive_mode = settings->bools.audio_wasapi_exclusive_mode; + int sh_buffer_length = settings->ints.audio_wasapi_sh_buffer_length; + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + wasapi_microphone_handle_t *microphone = calloc(1, sizeof(wasapi_microphone_handle_t)); + + if (!microphone) + return NULL; + + microphone->exclusive = exclusive_mode; + microphone->device = wasapi_init_device(dev_id, eCapture); + if (!microphone->device && dev_id) + microphone->device = wasapi_init_device(NULL, eCapture); + if (!microphone->device) + goto error; + + microphone->client = wasapi_init_client(microphone->device, + µphone->exclusive, &float_format, &rate, latency); + if (!microphone->client) + goto error; + + hr = _IAudioClient_GetBufferSize(microphone->client, &frame_count); + if (FAILED(hr)) + goto error; + + microphone->frame_size = float_format ? 8 : 4; + microphone->engine_buffer_size = frame_count * microphone->frame_size; + + if (microphone->exclusive) + { + microphone->buffer = fifo_new(microphone->engine_buffer_size); + if (!microphone->buffer) + goto error; + + RARCH_LOG("[WASAPI]: Intermediate buffer length is %u frames (%.1fms).\n", + frame_count, (double)frame_count * 1000.0 / rate); + } + else if (sh_buffer_length) + { + if (sh_buffer_length < 0) + { + hr = _IAudioClient_GetDevicePeriod(microphone->client, &dev_period, NULL); + if (FAILED(hr)) + goto error; + + sh_buffer_length = dev_period * rate / 10000000; + } + + microphone->buffer = fifo_new(sh_buffer_length * microphone->frame_size); + if (!microphone->buffer) + goto error; + + RARCH_LOG("[WASAPI]: Intermediate buffer length is %u frames (%.1fms).\n", + sh_buffer_length, (double)sh_buffer_length * 1000.0 / rate); + } + else + { + RARCH_LOG("[WASAPI]: Intermediate buffer is off. \n"); + } + + microphone->write_event = CreateEventA(NULL, FALSE, FALSE, NULL); + if (!microphone->write_event) + goto error; + + hr = _IAudioClient_SetEventHandle(microphone->client, microphone->write_event); + if (FAILED(hr)) + goto error; + + hr = _IAudioClient_GetService(microphone->client, + IID_IAudioRenderClient, (void**)µphone->capture); + if (FAILED(hr)) + goto error; + + hr = _IAudioCaptureClient_GetBuffer(microphone->capture, frame_count, &dest); + if (FAILED(hr)) + goto error; + + hr = _IAudioCaptureClient_ReleaseBuffer( + microphone->capture, frame_count, + AUDCLNT_BUFFERFLAGS_SILENT); + if (FAILED(hr)) + goto error; + + hr = _IAudioClient_Start(microphone->client); + if (FAILED(hr)) + goto error; + + + return microphone; + +error: + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to open microphone: %s\n", error_message); + IFACE_RELEASE(microphone->capture); + IFACE_RELEASE(microphone->client); + IFACE_RELEASE(microphone->device); + if (microphone->write_event) + CloseHandle(microphone->write_event); + if (microphone->buffer) + fifo_free(microphone->buffer); + free(microphone); + + return NULL; +} + +static void wasapi_microphone_close_mic(void *driver_context, void *microphone_context) +{ + DWORD ir; + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t*)microphone_context; + HANDLE write_event; + + if (!wasapi || !microphone) + return; + + write_event = microphone->write_event; + + IFACE_RELEASE(microphone->capture); + if (microphone->client) + _IAudioClient_Stop(microphone->client); + IFACE_RELEASE(microphone->client); + IFACE_RELEASE(microphone->device); + if (microphone->buffer) + fifo_free(microphone->buffer); + free(microphone); + + ir = WaitForSingleObject(write_event, 20); + if (ir == WAIT_FAILED) + { + char error[256]; + wasapi_log_hr(HRESULT_FROM_WIN32(GetLastError()), error, sizeof(error)); + RARCH_ERR("[WASAPI mic]: WaitForSingleObject failed with error %d.\n", GetLastError()); + } + + /* If event isn't signaled log and leak */ + if (!(ir == WAIT_OBJECT_0)) + return; + + CloseHandle(write_event); +} + +static struct string_list *wasapi_microphone_device_list_new(const void *u) +{ + return mmdevice_list_new(u, eCapture); +} + +microphone_driver_t microphone_wasapi = { + wasapi_microphone_init, + wasapi_microphone_free, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "wasapi", + wasapi_microphone_device_list_new, + NULL, + wasapi_microphone_open_mic, + wasapi_microphone_close_mic +}; \ No newline at end of file From aebdf2ded757cf145bbd8514d3c28d1d546f8e42 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:47:16 -0500 Subject: [PATCH 153/309] Add wasapi_microphone_device_list_free --- microphone/drivers/wasapi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index bed21dc7d1b3..e55ecf357e65 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -232,6 +232,14 @@ static struct string_list *wasapi_microphone_device_list_new(const void *u) return mmdevice_list_new(u, eCapture); } +static void wasapi_microphone_device_list_free(const void *driver_context, struct string_list *devices) +{ + struct string_list *sl = (struct string_list*)devices; + + if (sl) + string_list_free(sl); +} + microphone_driver_t microphone_wasapi = { wasapi_microphone_init, wasapi_microphone_free, @@ -244,7 +252,7 @@ microphone_driver_t microphone_wasapi = { NULL, "wasapi", wasapi_microphone_device_list_new, - NULL, + wasapi_microphone_device_list_free, wasapi_microphone_open_mic, wasapi_microphone_close_mic }; \ No newline at end of file From fefb2b15047d1270160b2b142fafc9e81d197a1c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 12:47:31 -0500 Subject: [PATCH 154/309] Change function parameter names to be consistent with microphone_driver --- microphone/drivers/wasapi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index e55ecf357e65..c0034acde9dc 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -227,9 +227,9 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c CloseHandle(write_event); } -static struct string_list *wasapi_microphone_device_list_new(const void *u) +static struct string_list *wasapi_microphone_device_list_new(const void *driver_context) { - return mmdevice_list_new(u, eCapture); + return mmdevice_list_new(driver_context, eCapture); } static void wasapi_microphone_device_list_free(const void *driver_context, struct string_list *devices) From 24372217f50373f7b7e71d5498bf74a12145b4a4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 26 Jan 2023 13:07:00 -0500 Subject: [PATCH 155/309] Partially implement wasapi_microphone_read - Mostly copied from the audio driver so far - It doesn't compile yet - But it'll be beautiful when I'm done with it --- microphone/drivers/wasapi.c | 243 +++++++++++++++++++++++++++++++++++- 1 file changed, 242 insertions(+), 1 deletion(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index c0034acde9dc..d081bbdb2ea4 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -77,6 +77,247 @@ static void wasapi_microphone_free(void *driver_context) free(wasapi); } +static bool wasapi_microphone_flush( + wasapi_microphone_t *wasapi, + wasapi_microphone_handle_t *microphone, + const void * buffer, + size_t buffer_size) +{ + BYTE *dest = NULL; + UINT32 frame_count = buffer_size / microphone->frame_size; + + if (FAILED(_IAudioCaptureClient_GetBuffer( + microphone->capture, frame_count, &dest))) + return false; + + memcpy(dest, buffer, buffer_size); + if (FAILED(_IAudioCaptureClient_ReleaseBuffer( + microphone->renderer, frame_count, + 0))) + return false; + + return true; +} + +static bool wasapi_microphone_flush_buffer( + wasapi_microphone_t *wasapi, + wasapi_microphone_handle_t *microphone, + size_t buffer_size) +{ + BYTE *dest = NULL; + UINT32 frame_count = buffer_size / microphone->frame_size; + if (FAILED(_IAudioCaptureClient_GetBuffer( + microphone->renderer, frame_count, &dest))) + return false; + + fifo_read(microphone->buffer, dest, buffer_size); + if (FAILED(_IAudioCaptureClient_ReleaseBuffer( + microphone->renderer, frame_count, + 0))) + return false; + + return true; +} + +/** + * + * @param wasapi + * @param microphone + * @param buffer + * @param buffer_size + * @return The number of bytes that were read + */ +static ssize_t wasapi_microphone_read_sh_buffer( + wasapi_microphone_t *wasapi, + wasapi_microphone_handle_t *microphone, + const void *buffer, + size_t buffer_size) +{ + ssize_t written = -1; + UINT32 padding = 0; + size_t write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + + if (!write_avail) + { + size_t read_avail = 0; + if (WaitForSingleObject(microphone->write_event, INFINITE) != WAIT_OBJECT_0) + return -1; + + if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + return -1; + + read_avail = FIFO_READ_AVAIL(microphone->buffer); + write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; + written = read_avail < write_avail ? read_avail : write_avail; + if (written) + if (!wasapi_microphone_flush_buffer(microphone, written)) + return -1; + } + + write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + written = buffer_size < write_avail ? buffer_size : write_avail; + if (written) + fifo_write(microphone->buffer, buffer, written); + + return written; +} + +static ssize_t wasapi_microphone_read_sh( + wasapi_microphone_t *wasapi, + wasapi_microphone_handle_t *microphone, + const void *buffer, + size_t buffer_size) +{ + size_t write_avail = 0; + ssize_t written = -1; + UINT32 padding = 0; + + if (WaitForSingleObject(microphone->write_event, INFINITE) != WAIT_OBJECT_0) + return -1; + + if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + return -1; + + write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; + if (!write_avail) + return 0; + + written = buffer_size < write_avail ? buffer_size : write_avail; + if (written) + if (!wasapi_microphone_flush(microphone, buffer, written)) + return -1; + + return written; +} + +static ssize_t wasapi_microphone_read_sh_nonblock( + wasapi_microphone_t *wasapi, + wasapi_microphone_handle_t *microphone, + const void *buffer, + size_t buffer_size) +{ + size_t write_avail = 0; + ssize_t written = -1; + UINT32 padding = 0; + + if (microphone->buffer) + { + write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + if (!write_avail) + { + size_t read_avail = 0; + if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + return -1; + + read_avail = FIFO_READ_AVAIL(microphone->buffer); + write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; + written = read_avail < write_avail ? read_avail : write_avail; + if (written) + if (!wasapi_microphone_flush_buffer(microphone, written)) + return -1; + } + + write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + written = buffer_size < write_avail ? buffer_size : write_avail; + if (written) + fifo_write(microphone->buffer, buffer, written); + } + else + { + if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + return -1; + + if (!(write_avail = microphone->engine_buffer_size - padding * microphone->frame_size)) + return 0; + + written = buffer_size < write_avail ? buffer_size : write_avail; + if (written) + if (!wasapi_microphone_flush(microphone, buffer, written)) + return -1; + } + + return written; +} + +static ssize_t wasapi_microphone_read_ex( + wasapi_microphone_t *wasapi, + wasapi_microphone_handle_t *microphone, + const void * buffer, + size_t buffer_size, + DWORD ms) +{ + ssize_t written = 0; + size_t write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + + if (!write_avail) + { + if (WaitForSingleObject(microphone->write_event, ms) != WAIT_OBJECT_0) + return 0; + + if (!wasapi_microphone_flush_buffer(microphone, microphone->engine_buffer_size)) + return -1; + + write_avail = microphone->engine_buffer_size; + } + + written = buffer_size < write_avail ? buffer_size : write_avail; + fifo_write(microphone->buffer, buffer, written); + + return written; +} + +static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, void *buffer, size_t buffer_size) +{ + size_t bytes_read = 0; + wasapi_microphone_t *wasapi = (wasapi_microphone_t *)driver_context; + wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t*)mic_context; + + if (!wasapi || !microphone || !buffer) + return -1; + + if (wasapi->nonblock) + { + if (microphone->exclusive) + return wasapi_microphone_read_ex(wasapi, microphone, buffer, buffer_size, 0); + return wasapi_microphone_read_sh_nonblock(wasapi, microphone, buffer, buffer_size); + } + + if (microphone->exclusive) + { + ssize_t read; + for (read = -1; bytes_read < buffer_size; bytes_read += read) + { + read = wasapi_microphone_read_ex(wasapi, microphone, (char*)buffer + bytes_read, buffer_size - bytes_read, INFINITE); + if (read == -1) + return -1; + } + } + else + { + ssize_t read; + if (microphone->buffer) + { + for (read = -1; bytes_read < buffer_size; bytes_read += read) + { + read = wasapi_microphone_read_sh_buffer(wasapi, microphone, (char*)buffer + bytes_read, buffer_size - bytes_read); + if (read == -1) + return -1; + } + } + else + { + for (read = -1; bytes_read < buffer_size; bytes_read += read) + { + read = wasapi_microphone_read_sh(microphone, (char*)buffer + bytes_read, buffer_size - bytes_read); + if (read == -1) + return -1; + } + } + } + + return bytes_read; +} + static void *wasapi_microphone_open_mic(void *driver_context, const char *dev_id, unsigned rate, unsigned latency, unsigned u1, unsigned *u2) { @@ -243,7 +484,7 @@ static void wasapi_microphone_device_list_free(const void *driver_context, struc microphone_driver_t microphone_wasapi = { wasapi_microphone_init, wasapi_microphone_free, - NULL, + wasapi_microphone_read, NULL, NULL, NULL, From 147babe47853a13935c10ec104e178747df37b94 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 27 Jan 2023 07:41:12 -0500 Subject: [PATCH 156/309] Refactor the mic driver's functions - Rename get_mic_active to mic_alive - Split set_mic_active into start_mic and stop_mic - Refactor the SDL mic driver accordingly --- microphone/drivers/sdl_microphone.c | 21 +++++++--- microphone/microphone_driver.c | 25 ++++++++--- microphone/microphone_driver.h | 65 ++++++++++++----------------- 3 files changed, 61 insertions(+), 50 deletions(-) diff --git a/microphone/drivers/sdl_microphone.c b/microphone/drivers/sdl_microphone.c index 6641d66d0061..cf4a7e5ddf66 100644 --- a/microphone/drivers/sdl_microphone.c +++ b/microphone/drivers/sdl_microphone.c @@ -289,7 +289,7 @@ static void sdl_microphone_close_mic(void *data, void *microphone_context) } } -static bool sdl_microphone_get_mic_active(const void *data, const void *microphone_context) +static bool sdl_microphone_mic_alive(const void *data, const void *microphone_context) { const sdl_microphone_t *sdl = (const sdl_microphone_t*)data; const sdl_microphone_handle_t *microphone = (const sdl_microphone_handle_t*)microphone_context; @@ -321,6 +321,16 @@ static bool sdl_microphone_set_mic_active(void *data, void *microphone_context, return true; } +static bool sdl_microphone_start_mic(void *data, void *microphone_context) +{ + return sdl_microphone_set_mic_active(data, microphone_context, true); +} + +static bool sdl_microphone_stop_mic(void *data, void *microphone_context) +{ + return sdl_microphone_set_mic_active(data, microphone_context, false); +} + static void sdl_microphone_set_nonblock_state(void *data, bool state) { sdl_microphone_t *sdl = (sdl_microphone_t*)data; @@ -439,15 +449,16 @@ microphone_driver_t microphone_sdl = { sdl_microphone_init, sdl_microphone_free, sdl_microphone_read, - sdl_microphone_stop, sdl_microphone_start, + sdl_microphone_stop, sdl_microphone_alive, - sdl_microphone_set_mic_active, - sdl_microphone_get_mic_active, sdl_microphone_set_nonblock_state, "sdl2", NULL, NULL, sdl_microphone_open_mic, - sdl_microphone_close_mic + sdl_microphone_close_mic, + sdl_microphone_mic_alive, + sdl_microphone_start_mic, + sdl_microphone_stop_mic }; \ No newline at end of file diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 8a8c009b2cbe..bd92b2ac776d 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -313,12 +313,25 @@ bool microphone_driver_set_mic_state(retro_microphone_t *microphone, bool state) const microphone_driver_t *mic_driver = mic_st->driver; void *driver_context = mic_st->driver_context; - if (!microphone || !microphone->active || !mic_driver || !mic_driver->set_mic_active) + if (!microphone || !microphone->active || !mic_driver || !mic_driver->start_mic || !mic_driver->stop_mic || !mic_driver->mic_alive) return false; if (driver_context && microphone->microphone_context) { /* If the driver is initialized... */ - bool success = mic_driver->set_mic_active(driver_context, microphone->microphone_context, state); + bool success; + if (state) + { /* If we want to enable this mic... */ + success = mic_driver->mic_alive(driver_context, microphone->microphone_context) + || mic_driver->start_mic(driver_context, microphone->microphone_context); + + /* Enable the mic, or do nothing and report success if it's already enabled. */ + } + else + { /* If we want to pause this mic... */ + success = !mic_driver->mic_alive(driver_context, microphone->microphone_context) + || mic_driver->stop_mic(driver_context, microphone->microphone_context); + /* Disable the mic, or do nothing and report success if it's already disabled. */ + } if (success) RARCH_DBG("[Microphone]: Set initialized mic state to %s\n", @@ -346,12 +359,12 @@ bool microphone_driver_get_mic_state(const retro_microphone_t *microphone) const microphone_driver_t *mic_driver = mic_st->driver; void *driver_context = mic_st->driver_context; - if (!microphone || !microphone->active || !mic_driver || !mic_driver->get_mic_active) + if (!microphone || !microphone->active || !mic_driver || !mic_driver->mic_alive) return false; if (driver_context && microphone->microphone_context) { /* If the driver is initialized... */ - return mic_driver->get_mic_active(driver_context, microphone->microphone_context); + return mic_driver->mic_alive(driver_context, microphone->microphone_context); } else { /* The driver's not ready yet, @@ -402,8 +415,8 @@ static void microphone_driver_flush( microphone->sample_buffer_length && /* ...with a non-empty sample buffer... */ mic_st->driver->read && /* ...and valid function pointers... */ - mic_st->driver->get_mic_active && - mic_st->driver->get_mic_active( /* ...and it's enabled... */ + mic_st->driver->mic_alive && + mic_st->driver->mic_alive( /* ...and it's enabled... */ mic_st->driver_context, microphone->microphone_context)) { diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index c71b0136eff0..07dcfa90bc53 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -150,6 +150,13 @@ typedef struct microphone_driver */ ssize_t (*read)(void *driver_context, void *mic_context, void *buffer, size_t buffer_size); + /** + * Resumes the driver from the paused state. + * Microphones will resume activity \em if they were already active + * before the driver was stopped. + **/ + bool (*start)(void *data, bool is_shutdown); + /** * Temporarily pauses all microphones. * The return value of \c get_mic_state will not be updated, @@ -162,13 +169,6 @@ typedef struct microphone_driver **/ bool (*stop)(void *driver_context); - /** - * Resumes the driver from the paused state. - * Microphones will resume activity \em if they were already active - * before the driver was stopped. - **/ - bool (*start)(void *data, bool is_shutdown); - /** * Queries whether the driver is active. * This only queries the overall driver state, @@ -181,38 +181,6 @@ typedef struct microphone_driver */ bool (*alive)(void *driver_context); - /** - * Sets the active state of the provided microphone. - * Microphones must be active in order to read samples from them. - * Microphones are inactive when created, - * so cores will need to activate them explicitly with this function. - * - * @param[in] driver_context Pointer to the driver context. - * Will be the value that was returned by \c ::init(). - * @param[in] mic_context Pointer to a particular microphone's context. - * Will be a value that was returned by \c ::open_mic(). - * @param[in] active \c true if the provided microphone should be activated, - * false if it should be paused. - * @return \c true if the active state was updated, - * \c false if there was an error, - */ - bool (*set_mic_active)(void *driver_context, void *mic_context, bool active); - - /** - * Returns the active state of the provided microphone. - * Note that this describes the user-facing state of the microphone; - * this function can still return \c true if the mic driver is paused - * or muted. - * - * @param[in] driver_context Pointer to the driver context. - * Will be the value that was returned by \c ::init(). - * @param[in] mic_context Pointer to a particular microphone's context. - * Will be a value that was returned by \c ::open_mic(). - * @return \c true if the provided microphone is active, - * \c false if not or if there was an error. - */ - bool (*get_mic_active)(const void *driver_context, const void *mic_context); - /** * Sets the nonblocking state of all microphones. * Primarily used for fast-forwarding. @@ -317,6 +285,25 @@ typedef struct microphone_driver * while \p microphone_context will not. */ void (*close_mic)(void *driver_context, void *microphone_context); + + /** + * Returns the active state of the provided microphone. + * Note that this describes the user-facing state of the microphone; + * this function can still return \c true if the mic driver is paused + * or muted. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] mic_context Pointer to a particular microphone's context. + * Will be a value that was returned by \c ::open_mic(). + * @return \c true if the provided microphone is active, + * \c false if not or if there was an error. + */ + bool (*mic_alive)(const void *driver_context, const void *mic_context); + + bool (*start_mic)(void *driver_context, void *microphone_context); + + bool (*stop_mic)(void *driver_context, void *microphone_context); } microphone_driver_t; typedef struct From 30294cfd610bd2d064b3bc05c13899389ebdb765 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 27 Jan 2023 07:42:15 -0500 Subject: [PATCH 157/309] Edit some WASAPI functions for logging and clarity --- audio/common/wasapi.c | 86 ++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index c3fb4e9439f0..601df175a9e7 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -37,6 +37,21 @@ void wasapi_log_hr(HRESULT hr, char* buffer, size_t length) NULL); } +static const char* wasapi_data_flow_name(EDataFlow data_flow) +{ + switch (data_flow) + { + case eCapture: + return "eCapture"; + case eRender: + return "eRender"; + case eAll: + return "eAll"; + default: + return ""; + } +} + static unsigned wasapi_pref_rate(unsigned i) { const unsigned r[] = { 48000, 44100, 96000, 192000, 32000 }; @@ -291,14 +306,16 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) IMMDeviceEnumerator *enumerator = NULL; IMMDevice *device = NULL; IMMDeviceCollection *collection = NULL; + const char *data_flow_name = wasapi_data_flow_name(data_flow); + char error_message[256] = {0}; if (id) { - RARCH_LOG("[WASAPI]: Initializing device %s ...\n", id); + RARCH_LOG("[WASAPI]: Initializing %s device \"%s\" ...\n", data_flow_name, id); } else { - RARCH_LOG("[WASAPI]: Initializing default device.. \n"); + RARCH_LOG("[WASAPI]: Initializing default %s device.. \n", data_flow_name); } #ifdef __cplusplus @@ -309,40 +326,45 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) &IID_IMMDeviceEnumerator, (void **)&enumerator); #endif if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to create device enumerator: %s\n", error_message); goto error; + } if (id) - { + { /* If a specific device was requested... */ int32_t idx_found = -1; struct string_list *list = (struct string_list*)mmdevice_list_new(NULL, data_flow); - /* Search for device name first */ - if (list) + if (!list) { - if (list->elems) + RARCH_ERR("[WASAPI]: Failed to allocate %s device list\n", data_flow_name); + goto error; + } + + if (list->elems) + { /* If any devices were found... */ + unsigned d; + for (d = 0; d < list->size; d++) { - unsigned i; - for (i = 0; i < list->size; i++) + RARCH_LOG("[WASAPI]: %u : %s\n", d, list->elems[d].data); + if (string_is_equal(id, list->elems[d].data)) { - RARCH_LOG("[WASAPI]: %d : %s\n", i, list->elems[i].data); - if (string_is_equal(id, list->elems[i].data)) - { - idx_found = i; - break; - } + idx_found = d; + break; } - /* Index was not found yet based on name string, - * just assume id is a one-character number index. */ - - if (idx_found == -1 && isdigit(id[0])) - { - idx_found = strtoul(id, NULL, 0); - RARCH_LOG("[WASAPI]: Fallback, device index is a single number index instead: %d.\n", idx_found); + } + /* Index was not found yet based on name string, + * just assume id is a one-character number index. */ - } + if (idx_found == -1 && isdigit(id[0])) + { + idx_found = strtoul(id, NULL, 0); + RARCH_LOG("[WASAPI]: Fallback, %s device index is a single number index instead: %u.\n", data_flow_name, idx_found); } - string_list_free(list); } + string_list_free(list); if (idx_found == -1) idx_found = 0; @@ -350,17 +372,26 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) hr = _IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, data_flow, DEVICE_STATE_ACTIVE, &collection); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); goto error; + } hr = _IMMDeviceCollection_GetCount(collection, &dev_count); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); goto error; + } for (i = 0; i < dev_count; ++i) { hr = _IMMDeviceCollection_Item(collection, i, &device); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); goto error; + } if (i == idx_found) break; @@ -373,7 +404,10 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) hr = _IMMDeviceEnumerator_GetDefaultAudioEndpoint( enumerator, data_flow, eConsole, &device); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); goto error; + } } if (!device) @@ -384,17 +418,17 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) return device; - error: +error: IFACE_RELEASE(collection); IFACE_RELEASE(enumerator); if (id) { - RARCH_WARN("[WASAPI]: Failed to initialize device.\n"); + RARCH_WARN("[WASAPI]: Failed to initialize %s device \"%s\"\n", data_flow_name, id); } else { - RARCH_ERR("[WASAPI]: Failed to initialize device.\n"); + RARCH_ERR("[WASAPI]: Failed to initialize default %s device\n", data_flow_name); } return NULL; From 0788f5667b49dc27ab5a0a4d52f6bd012af4ec5f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 27 Jan 2023 08:31:11 -0500 Subject: [PATCH 158/309] Implement more of the WASAPI mic driver --- microphone/drivers/wasapi.c | 224 +++++++++++++++++++++++++++++++----- 1 file changed, 197 insertions(+), 27 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index d081bbdb2ea4..8c3cfa10de92 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -25,12 +25,14 @@ typedef struct { HANDLE write_event; IMMDevice *device; + LPWSTR device_id; IAudioClient *client; IAudioCaptureClient *capture; fifo_buffer_t *buffer; /* NULL in unbuffered shared mode */ size_t frame_size; /* 2 or 4 only */ size_t engine_buffer_size; bool exclusive; + bool running; } wasapi_microphone_handle_t; typedef struct wasapi_microphone @@ -61,7 +63,6 @@ static void *wasapi_microphone_init(void) return wasapi; } - static void wasapi_microphone_free(void *driver_context) { wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; @@ -165,7 +166,7 @@ static ssize_t wasapi_microphone_read_sh_buffer( static ssize_t wasapi_microphone_read_sh( wasapi_microphone_t *wasapi, wasapi_microphone_handle_t *microphone, - const void *buffer, + void *buffer, size_t buffer_size) { size_t write_avail = 0; @@ -193,7 +194,7 @@ static ssize_t wasapi_microphone_read_sh( static ssize_t wasapi_microphone_read_sh_nonblock( wasapi_microphone_t *wasapi, wasapi_microphone_handle_t *microphone, - const void *buffer, + void *buffer, size_t buffer_size) { size_t write_avail = 0; @@ -242,7 +243,7 @@ static ssize_t wasapi_microphone_read_sh_nonblock( static ssize_t wasapi_microphone_read_ex( wasapi_microphone_t *wasapi, wasapi_microphone_handle_t *microphone, - const void * buffer, + void * buffer, size_t buffer_size, DWORD ms) { @@ -318,12 +319,66 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v return bytes_read; } -static void *wasapi_microphone_open_mic(void *driver_context, const char *dev_id, unsigned rate, unsigned latency, - unsigned u1, unsigned *u2) +static bool wasapi_microphone_mic_alive(const void *driver_context, const void *mic_context); +static bool wasapi_microphone_start_mic(void *driver_context, void *microphone_context); +static bool wasapi_microphone_start(void *driver_context, bool is_shutdown) +{ + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + + if (!wasapi) + return false; + + if (wasapi->microphone && wasapi_microphone_mic_alive(wasapi, wasapi->microphone)) + { /* If we have a microphone that was active at the time the driver stopped... */ + bool result = wasapi_microphone_start_mic(wasapi, wasapi->microphone); + } + + wasapi->running = true; + + return true; +} + +static bool wasapi_microphone_stop_mic(void *driver_context, void *microphone_context); +static bool wasapi_microphone_stop(void *driver_context) +{ + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + + if (!wasapi) + return false; + + if (wasapi->microphone && wasapi_microphone_mic_alive(wasapi, wasapi->microphone)) + { /* If we have a microphone that we need to pause... */ + + } + + wasapi->running = false; + + return true; +} + +static bool wasapi_microphone_alive(void *driver_context) +{ + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + + return wasapi->running; +} + +static void wasapi_microphone_set_nonblock_state(void *driver_context, bool nonblock) +{ + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + + RARCH_LOG("[WASAPI mic]: Sync %s.\n", nonblock ? "off" : "on"); + + wasapi->nonblock = nonblock; +} + +static void *wasapi_microphone_open_mic(void *driver_context, const char *device, unsigned rate, + unsigned latency, unsigned block_frames, unsigned *new_rate) { char error_message[256] = {0}; settings_t *settings = config_get_ptr(); HRESULT hr; + DWORD flags = 0; UINT32 frame_count = 0; REFERENCE_TIME dev_period = 0; BYTE *dest = NULL; @@ -337,31 +392,57 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *dev_id return NULL; microphone->exclusive = exclusive_mode; - microphone->device = wasapi_init_device(dev_id, eCapture); - if (!microphone->device && dev_id) + microphone->device = wasapi_init_device(device, eCapture); + if (device && !microphone->device) + { /* If we requested a particular capture device, but couldn't open it... */ + RARCH_WARN("[WASAPI]: Failed to open requested capture device \"%s\", attempting to open default device\n", device); microphone->device = wasapi_init_device(NULL, eCapture); + } + if (!microphone->device) + { + RARCH_ERR("[WASAPI]: Failed to open capture device\n"); goto error; + } + + hr = _IMMDevice_GetId(microphone->device, µphone->device_id); + if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to get ID of capture device: %s\n", error_message); + goto error; + } microphone->client = wasapi_init_client(microphone->device, µphone->exclusive, &float_format, &rate, latency); if (!microphone->client) + { + RARCH_ERR("[WASAPI]: Failed to open client for capture device \"%ls\"\n", microphone->device_id); goto error; + } hr = _IAudioClient_GetBufferSize(microphone->client, &frame_count); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to get buffer size of IAudioClient for capture device \"%ls\": %s\n", + microphone->device_id, error_message); goto error; + } - microphone->frame_size = float_format ? 8 : 4; + microphone->frame_size = float_format ? sizeof(float) : sizeof(int16_t); microphone->engine_buffer_size = frame_count * microphone->frame_size; if (microphone->exclusive) { microphone->buffer = fifo_new(microphone->engine_buffer_size); if (!microphone->buffer) + { + RARCH_ERR("[WASAPI]: Failed to initialize FIFO queue for capture device.\n"); goto error; + } - RARCH_LOG("[WASAPI]: Intermediate buffer length is %u frames (%.1fms).\n", + RARCH_LOG("[WASAPI]: Intermediate capture buffer length is %u frames (%.1fms).\n", frame_count, (double)frame_count * 1000.0 / rate); } else if (sh_buffer_length) @@ -389,31 +470,47 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *dev_id microphone->write_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!microphone->write_event) + { + RARCH_ERR("[WASAPI]: Failed to allocate capture device's event handle\n"); goto error; + } hr = _IAudioClient_SetEventHandle(microphone->client, microphone->write_event); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to set capture device's event handle: %s\n", error_message); goto error; + } hr = _IAudioClient_GetService(microphone->client, - IID_IAudioRenderClient, (void**)µphone->capture); + IID_IAudioCaptureClient, (void**)µphone->capture); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to get capture device's IAudioCaptureClient service: %s\n", error_message); goto error; + } - hr = _IAudioCaptureClient_GetBuffer(microphone->capture, frame_count, &dest); + /* Get and release the buffer, just to ensure that we can. */ + hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &dest, &frame_count, &flags, NULL, NULL); if (FAILED(hr)) + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to get capture client buffer: %s\n", error_message); goto error; + } - hr = _IAudioCaptureClient_ReleaseBuffer( - microphone->capture, frame_count, - AUDCLNT_BUFFERFLAGS_SILENT); + hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, 0); if (FAILED(hr)) - goto error; + { + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to release capture client buffer: %s\n", error_message); - hr = _IAudioClient_Start(microphone->client); - if (FAILED(hr)) goto error; + } + wasapi->microphone = microphone; return microphone; @@ -427,6 +524,8 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *dev_id CloseHandle(microphone->write_event); if (microphone->buffer) fifo_free(microphone->buffer); + if (microphone->device_id) + CoTaskMemFree(microphone->device_id); free(microphone); return NULL; @@ -451,6 +550,8 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c IFACE_RELEASE(microphone->device); if (microphone->buffer) fifo_free(microphone->buffer); + if (microphone->device_id) + CoTaskMemFree(microphone->device_id); free(microphone); ir = WaitForSingleObject(write_event, 20); @@ -458,16 +559,84 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c { char error[256]; wasapi_log_hr(HRESULT_FROM_WIN32(GetLastError()), error, sizeof(error)); - RARCH_ERR("[WASAPI mic]: WaitForSingleObject failed with error %d.\n", GetLastError()); + RARCH_ERR("[WASAPI mic]: WaitForSingleObject failed with error %d: %s\n", GetLastError(), error); } /* If event isn't signaled log and leak */ - if (!(ir == WAIT_OBJECT_0)) + if (ir != WAIT_OBJECT_0) return; CloseHandle(write_event); } +static bool wasapi_microphone_start_mic(void *driver_context, void *microphone_context) +{ + wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t*)microphone_context; + + if (!wasapi || !microphone) + return false; + + if (wasapi_microphone_alive(wasapi)) + { /* If the microphone should be active... */ + HRESULT hr = _IAudioClient_Start(microphone->client); + + if (SUCCEEDED(hr) || hr == AUDCLNT_E_NOT_STOPPED) + { /* Starting an active microphone is not an error */ + + microphone->running = true; + } + else + { + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI mic]: Failed to start capture device \"%ls\"'s IAudioClient: %s\n", microphone->device_id, error); + microphone->running = false; + } + } + else + { + microphone->running = true; + /* The microphone will resume next time the driver itself is resumed */ + } + + + return microphone->running; +} + +static bool wasapi_microphone_stop_mic(void *driver_context, void *microphone_context) +{ + wasapi_microphone_t *w = (wasapi_microphone_t*)driver_context; + wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t*)microphone_context; + HRESULT hr; + + if (!w || !microphone) + return false; + + hr = _IAudioClient_Stop(microphone->client); + if (FAILED(hr)) + { + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI mic]: Failed to stop capture device \"%ls\"'s IAudioClient: %s\n", microphone->device_id, error); + return false; + } + + RARCH_LOG("[WASAPI mic]: Stopped capture device \"%ls\"\n", microphone->device_id); + + microphone->running = false; + + return true; +} + +static bool wasapi_microphone_mic_alive(const void *driver_context, const void *mic_context) +{ + wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t *)mic_context; + (void)driver_context; + + return microphone && microphone->running; +} + static struct string_list *wasapi_microphone_device_list_new(const void *driver_context) { return mmdevice_list_new(driver_context, eCapture); @@ -485,15 +654,16 @@ microphone_driver_t microphone_wasapi = { wasapi_microphone_init, wasapi_microphone_free, wasapi_microphone_read, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, + wasapi_microphone_start, + wasapi_microphone_stop, + wasapi_microphone_alive, + wasapi_microphone_set_nonblock_state, "wasapi", wasapi_microphone_device_list_new, wasapi_microphone_device_list_free, wasapi_microphone_open_mic, - wasapi_microphone_close_mic + wasapi_microphone_close_mic, + wasapi_microphone_mic_alive, + wasapi_microphone_start_mic, + wasapi_microphone_stop_mic }; \ No newline at end of file From d183eb1c115d85cbb5d95ed1f74c2c167ab22397 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 27 Jan 2023 08:44:56 -0500 Subject: [PATCH 159/309] Rename write_event to read_event --- microphone/drivers/wasapi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 8c3cfa10de92..17d3d42bd4e8 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -23,7 +23,7 @@ typedef struct { - HANDLE write_event; + HANDLE read_event; IMMDevice *device; LPWSTR device_id; IAudioClient *client; @@ -141,7 +141,7 @@ static ssize_t wasapi_microphone_read_sh_buffer( if (!write_avail) { size_t read_avail = 0; - if (WaitForSingleObject(microphone->write_event, INFINITE) != WAIT_OBJECT_0) + if (WaitForSingleObject(microphone->read_event, INFINITE) != WAIT_OBJECT_0) return -1; if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) @@ -173,7 +173,7 @@ static ssize_t wasapi_microphone_read_sh( ssize_t written = -1; UINT32 padding = 0; - if (WaitForSingleObject(microphone->write_event, INFINITE) != WAIT_OBJECT_0) + if (WaitForSingleObject(microphone->read_event, INFINITE) != WAIT_OBJECT_0) return -1; if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) @@ -252,7 +252,7 @@ static ssize_t wasapi_microphone_read_ex( if (!write_avail) { - if (WaitForSingleObject(microphone->write_event, ms) != WAIT_OBJECT_0) + if (WaitForSingleObject(microphone->read_event, ms) != WAIT_OBJECT_0) return 0; if (!wasapi_microphone_flush_buffer(microphone, microphone->engine_buffer_size)) @@ -468,14 +468,14 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device RARCH_LOG("[WASAPI]: Intermediate buffer is off. \n"); } - microphone->write_event = CreateEventA(NULL, FALSE, FALSE, NULL); - if (!microphone->write_event) + microphone->read_event = CreateEventA(NULL, FALSE, FALSE, NULL); + if (!microphone->read_event) { RARCH_ERR("[WASAPI]: Failed to allocate capture device's event handle\n"); goto error; } - hr = _IAudioClient_SetEventHandle(microphone->client, microphone->write_event); + hr = _IAudioClient_SetEventHandle(microphone->client, microphone->read_event); if (FAILED(hr)) { wasapi_log_hr(hr, error_message, sizeof(error_message)); @@ -520,8 +520,8 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device IFACE_RELEASE(microphone->capture); IFACE_RELEASE(microphone->client); IFACE_RELEASE(microphone->device); - if (microphone->write_event) - CloseHandle(microphone->write_event); + if (microphone->read_event) + CloseHandle(microphone->read_event); if (microphone->buffer) fifo_free(microphone->buffer); if (microphone->device_id) @@ -541,7 +541,7 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c if (!wasapi || !microphone) return; - write_event = microphone->write_event; + write_event = microphone->read_event; IFACE_RELEASE(microphone->capture); if (microphone->client) From dedc6cd9ed95b216fb423724cfa7921876fecde6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 27 Jan 2023 08:45:25 -0500 Subject: [PATCH 160/309] Pass the WASAPI driver context to the various read functions --- microphone/drivers/wasapi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 17d3d42bd4e8..30773f6ce8e8 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -151,7 +151,7 @@ static ssize_t wasapi_microphone_read_sh_buffer( write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; written = read_avail < write_avail ? read_avail : write_avail; if (written) - if (!wasapi_microphone_flush_buffer(microphone, written)) + if (!wasapi_microphone_flush_buffer(wasapi, microphone, written)) return -1; } @@ -185,7 +185,7 @@ static ssize_t wasapi_microphone_read_sh( written = buffer_size < write_avail ? buffer_size : write_avail; if (written) - if (!wasapi_microphone_flush(microphone, buffer, written)) + if (!wasapi_microphone_flush(wasapi, microphone, buffer, written)) return -1; return written; @@ -214,7 +214,7 @@ static ssize_t wasapi_microphone_read_sh_nonblock( write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; written = read_avail < write_avail ? read_avail : write_avail; if (written) - if (!wasapi_microphone_flush_buffer(microphone, written)) + if (!wasapi_microphone_flush_buffer(wasapi, microphone, written)) return -1; } @@ -233,7 +233,7 @@ static ssize_t wasapi_microphone_read_sh_nonblock( written = buffer_size < write_avail ? buffer_size : write_avail; if (written) - if (!wasapi_microphone_flush(microphone, buffer, written)) + if (!wasapi_microphone_flush(wasapi, microphone, buffer, written)) return -1; } @@ -255,7 +255,7 @@ static ssize_t wasapi_microphone_read_ex( if (WaitForSingleObject(microphone->read_event, ms) != WAIT_OBJECT_0) return 0; - if (!wasapi_microphone_flush_buffer(microphone, microphone->engine_buffer_size)) + if (!wasapi_microphone_flush_buffer(wasapi, microphone, microphone->engine_buffer_size)) return -1; write_avail = microphone->engine_buffer_size; @@ -309,7 +309,7 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v { for (read = -1; bytes_read < buffer_size; bytes_read += read) { - read = wasapi_microphone_read_sh(microphone, (char*)buffer + bytes_read, buffer_size - bytes_read); + read = wasapi_microphone_read_sh(wasapi, microphone, (char*)buffer + bytes_read, buffer_size - bytes_read); if (read == -1) return -1; } From ef221bed839edeea8f4f7883d6c6ddc98c10d7ae Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 28 Jan 2023 12:37:27 -0500 Subject: [PATCH 161/309] Mostly implement the read function for the WASAPI mic driver --- microphone/drivers/wasapi.c | 367 +++++++++++++++++++++++++----------- 1 file changed, 258 insertions(+), 109 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 30773f6ce8e8..50f48d7f7ece 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -28,8 +28,23 @@ typedef struct LPWSTR device_id; IAudioClient *client; IAudioCaptureClient *capture; - fifo_buffer_t *buffer; /* NULL in unbuffered shared mode */ - size_t frame_size; /* 2 or 4 only */ + + /** + * The buffer in which samples from the microphone will be read and stored + * until the core fetches them. + * Will be \c NULL if the mic is opened in shared mode + * and a shared-buffer length is not given. + * If \c NULL, then samples are copied directly from the + * platform's underlying buffer to the driver. + */ + fifo_buffer_t *buffer; + + /** + * The size of an audio frame, in bytes. + * Mic input is in one channel with either 16-bit ints or 32-bit floats, + * so this will be 2 or 4. + */ + size_t frame_size; size_t engine_buffer_size; bool exclusive; bool running; @@ -78,49 +93,119 @@ static void wasapi_microphone_free(void *driver_context) free(wasapi); } -static bool wasapi_microphone_flush( - wasapi_microphone_t *wasapi, +/** + * Flushes microphone's most recent input to the provided buffer. + * @param microphone Pointer to the microphone context. + * @param buffer The buffer in which incoming samples will be stored. + * @param buffer_size The length of \c buffer, in bytes. + * @return True if the read was successful + */ +static bool wasapi_microphone_fetch_buffer( wasapi_microphone_handle_t *microphone, - const void * buffer, + void *buffer, size_t buffer_size) { - BYTE *dest = NULL; - UINT32 frame_count = buffer_size / microphone->frame_size; + BYTE *mic_input = NULL; + UINT32 frame_count = 0; + DWORD buffer_status_flags = 0; + HRESULT hr; - if (FAILED(_IAudioCaptureClient_GetBuffer( - microphone->capture, frame_count, &dest))) + hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frame_count, &buffer_status_flags, NULL, NULL); + if (FAILED(hr)) + { + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); return false; + } - memcpy(dest, buffer, buffer_size); - if (FAILED(_IAudioCaptureClient_ReleaseBuffer( - microphone->renderer, frame_count, - 0))) + memcpy(buffer, mic_input, MIN(buffer_size, frame_count)); + hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, buffer_status_flags); + if (FAILED(hr)) + { + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); return false; + } return true; } -static bool wasapi_microphone_flush_buffer( - wasapi_microphone_t *wasapi, +/** + * Flushes microphone's most recent input to the provided context's FIFO queue. + * @param microphone Pointer to the microphone context. + * @param buffer The buffer in which incoming samples will be stored. + * @param buffer_size The length of \c buffer, in bytes. + * @return True if the read was successful + */ +static bool wasapi_microphone_fetch_fifo( wasapi_microphone_handle_t *microphone, size_t buffer_size) { - BYTE *dest = NULL; - UINT32 frame_count = buffer_size / microphone->frame_size; - if (FAILED(_IAudioCaptureClient_GetBuffer( - microphone->renderer, frame_count, &dest))) + BYTE *mic_input = NULL; + UINT32 frame_count = 0; + DWORD buffer_status_flags = 0; + HRESULT hr; + + hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frame_count, &buffer_status_flags, NULL, NULL); + if (FAILED(hr)) + { + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); return false; + } - fifo_read(microphone->buffer, dest, buffer_size); - if (FAILED(_IAudioCaptureClient_ReleaseBuffer( - microphone->renderer, frame_count, - 0))) + fifo_write(microphone->buffer, mic_input, MIN(buffer_size, frame_count)); + hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, buffer_status_flags); + if (FAILED(hr)) + { + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); return false; + } return true; } /** + * Blocks until the provided microphone's capture event is signalled. + * + * @param microphone The microphone to wait on. + * @param timeout The amount of time to wait, in milliseconds. + * @return true if the event was signalled, + * false if it timed out or there was an error. + */ +static bool wasapi_microphone_wait_for_capture_event(wasapi_microphone_handle_t *microphone, DWORD timeout) +{ + switch (WaitForSingleObject(microphone->read_event, timeout)) + { /*...then let's wait for the mic to tell us that samples are ready. */ + case WAIT_OBJECT_0: + /* Okay, there's data available. */ + return true; + case WAIT_TIMEOUT: + /* Time out; there's nothing here for us. */ + RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: Timeout after %ums\n", microphone->device_id, timeout); + return false; + case WAIT_FAILED: + { + HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); + char error_message[256]; + wasapi_log_hr(hr, error_message, sizeof(error_message)); + RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: %s\n", microphone->device_id, error_message); + return false; + } + default: + RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: Unknown error\n", microphone->device_id); + return false; + } +} + +/** + * Reads samples from a shared-mode microphone, + * fetching new ones directly from the device if necessary. * * @param wasapi * @param microphone @@ -128,143 +213,206 @@ static bool wasapi_microphone_flush_buffer( * @param buffer_size * @return The number of bytes that were read */ -static ssize_t wasapi_microphone_read_sh_buffer( - wasapi_microphone_t *wasapi, +static ssize_t wasapi_microphone_read_shared_buffered( wasapi_microphone_handle_t *microphone, - const void *buffer, + void *buffer, size_t buffer_size) { - ssize_t written = -1; - UINT32 padding = 0; - size_t write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + ssize_t bytes_to_read = -1; + UINT32 available_frames = 0; + size_t read_avail = FIFO_READ_AVAIL(microphone->buffer); - if (!write_avail) - { - size_t read_avail = 0; - if (WaitForSingleObject(microphone->read_event, INFINITE) != WAIT_OBJECT_0) + if (!read_avail) + { /* If the incoming sample buffer is empty... */ + size_t write_avail = 0; + HRESULT hr; + + if (!wasapi_microphone_wait_for_capture_event(microphone, INFINITE)) return -1; - if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); + if (FAILED(hr)) + { /* Get the number of frames that the mic has for us. */ + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); return -1; + } - read_avail = FIFO_READ_AVAIL(microphone->buffer); - write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; - written = read_avail < write_avail ? read_avail : write_avail; - if (written) - if (!wasapi_microphone_flush_buffer(wasapi, microphone, written)) + write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + read_avail = available_frames * microphone->frame_size; + bytes_to_read = MIN(write_avail, read_avail); + + if (bytes_to_read > 0) + if (!wasapi_microphone_fetch_fifo(microphone, bytes_to_read)) return -1; } - write_avail = FIFO_WRITE_AVAIL(microphone->buffer); - written = buffer_size < write_avail ? buffer_size : write_avail; - if (written) - fifo_write(microphone->buffer, buffer, written); + read_avail = FIFO_WRITE_AVAIL(microphone->buffer); + bytes_to_read = MIN(buffer_size, read_avail); + if (bytes_to_read) + fifo_read(microphone->buffer, buffer, bytes_to_read); - return written; + return bytes_to_read; } -static ssize_t wasapi_microphone_read_sh( - wasapi_microphone_t *wasapi, - wasapi_microphone_handle_t *microphone, - void *buffer, - size_t buffer_size) +/** + * Reads incoming samples from a shared-mode microphone, + * without buffering any. + * @param microphone + * @param buffer + * @param buffer_size + * @return + */ +static ssize_t wasapi_microphone_read_shared_unbuffered( + wasapi_microphone_handle_t *microphone, + void *buffer, + size_t buffer_size) { - size_t write_avail = 0; - ssize_t written = -1; - UINT32 padding = 0; + size_t read_avail = 0; + ssize_t bytes_to_read = -1; + UINT32 available_frames = 0; + HRESULT hr; - if (WaitForSingleObject(microphone->read_event, INFINITE) != WAIT_OBJECT_0) + if (!wasapi_microphone_wait_for_capture_event(microphone, INFINITE)) return -1; - if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); + if (FAILED(hr)) + { /* Get the number of frames that the mic has for us. */ + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); return -1; + } - write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; - if (!write_avail) + read_avail = microphone->engine_buffer_size - available_frames * microphone->frame_size; + if (!read_avail) return 0; - written = buffer_size < write_avail ? buffer_size : write_avail; - if (written) - if (!wasapi_microphone_flush(wasapi, microphone, buffer, written)) + bytes_to_read = MIN(buffer_size, read_avail); + if (bytes_to_read) + if (!wasapi_microphone_fetch_buffer(microphone, buffer, bytes_to_read)) return -1; - return written; + return bytes_to_read; } -static ssize_t wasapi_microphone_read_sh_nonblock( - wasapi_microphone_t *wasapi, +/** + * Reads from a shared-mode microphone into the provided buffer + * Handles both buffered and unbuffered microphone input, + * depending on the presence of microphone::buffer. + * @param microphone + * @param buffer + * @param buffer_size + * @return The number of bytes read, + * or -1 if there was an error. + */ +static ssize_t wasapi_microphone_read_shared_nonblock( wasapi_microphone_handle_t *microphone, void *buffer, size_t buffer_size) { - size_t write_avail = 0; - ssize_t written = -1; - UINT32 padding = 0; + size_t read_avail = 0; + ssize_t bytes_to_read = -1; + UINT32 available_frames = 0; if (microphone->buffer) - { - write_avail = FIFO_WRITE_AVAIL(microphone->buffer); - if (!write_avail) - { - size_t read_avail = 0; - if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + { /* If we're buffering mic input... */ + read_avail = FIFO_READ_AVAIL(microphone->buffer); + if (!read_avail) + { /* If the incoming sample queue is empty... */ + size_t write_avail = 0; + HRESULT hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); + if (FAILED(hr)) + { /* Get the number of frames that the mic has for us. */ + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); return -1; + } - read_avail = FIFO_READ_AVAIL(microphone->buffer); - write_avail = microphone->engine_buffer_size - padding * microphone->frame_size; - written = read_avail < write_avail ? read_avail : write_avail; - if (written) - if (!wasapi_microphone_flush_buffer(wasapi, microphone, written)) + write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + read_avail = microphone->engine_buffer_size - available_frames * microphone->frame_size; + bytes_to_read = MIN(write_avail, read_avail); + if (bytes_to_read) + if (!wasapi_microphone_fetch_fifo(microphone, bytes_to_read)) return -1; } - write_avail = FIFO_WRITE_AVAIL(microphone->buffer); - written = buffer_size < write_avail ? buffer_size : write_avail; - if (written) - fifo_write(microphone->buffer, buffer, written); + read_avail = FIFO_READ_AVAIL(microphone->buffer); + bytes_to_read = MIN(buffer_size, read_avail); + if (bytes_to_read) + fifo_write(microphone->buffer, buffer, bytes_to_read); } else { - if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &padding))) + HRESULT hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); + if (FAILED(hr)) + { /* Get the number of frames that the mic has for us. */ + char error[256]; + wasapi_log_hr(hr, error, sizeof(error)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); return -1; + } - if (!(write_avail = microphone->engine_buffer_size - padding * microphone->frame_size)) + read_avail = microphone->engine_buffer_size - available_frames * microphone->frame_size; + if (!read_avail) return 0; - written = buffer_size < write_avail ? buffer_size : write_avail; - if (written) - if (!wasapi_microphone_flush(wasapi, microphone, buffer, written)) + bytes_to_read = MIN(buffer_size, read_avail); + if (bytes_to_read) + if (!wasapi_microphone_fetch_buffer(microphone, buffer, bytes_to_read)) return -1; } - return written; + return bytes_to_read; } -static ssize_t wasapi_microphone_read_ex( - wasapi_microphone_t *wasapi, +/** + * Reads samples from an exclusive-mode microphone, + * fetching more from it if necessary. + * + * @param microphone Pointer to the context of the microphone + * from which samples will be read. + * @param buffer The buffer in which the fetched samples will be stored. + * @param buffer_size The size of buffer, in bytes. + * @param timeout Timeout for new samples, in milliseconds. + * 0 means that this function won't wait for new samples, + * \c INFINITE means that this function will wait indefinitely. + * @return + */ +static ssize_t wasapi_microphone_read_exclusive( wasapi_microphone_handle_t *microphone, void * buffer, size_t buffer_size, - DWORD ms) + DWORD timeout) { - ssize_t written = 0; - size_t write_avail = FIFO_WRITE_AVAIL(microphone->buffer); + ssize_t bytes_read = 0; + size_t bytes_available = FIFO_READ_AVAIL(microphone->buffer); - if (!write_avail) - { - if (WaitForSingleObject(microphone->read_event, ms) != WAIT_OBJECT_0) - return 0; + if (!bytes_available) + { /* If the incoming sample queue is empty... */ + if (!wasapi_microphone_wait_for_capture_event(microphone, timeout)) + { /* If we couldn't wait for the microphone to signal a capture event... */ + return -1; + } - if (!wasapi_microphone_flush_buffer(wasapi, microphone, microphone->engine_buffer_size)) + if (!wasapi_microphone_fetch_fifo(microphone, microphone->engine_buffer_size)) + { /* If we couldn't fetch samples from the microphone... */ return -1; + } - write_avail = microphone->engine_buffer_size; + bytes_available = microphone->engine_buffer_size; } - written = buffer_size < write_avail ? buffer_size : write_avail; - fifo_write(microphone->buffer, buffer, written); + /* Now that we have samples available, let's give them to the core */ - return written; + bytes_read = MIN(buffer_size, bytes_available); + fifo_read(microphone->buffer, buffer, bytes_read); + /* Read data from the sample queue and store it in the provided buffer */ + + return bytes_read; } static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, void *buffer, size_t buffer_size) @@ -277,10 +425,10 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v return -1; if (wasapi->nonblock) - { + { /* If microphones shouldn't block... */ if (microphone->exclusive) - return wasapi_microphone_read_ex(wasapi, microphone, buffer, buffer_size, 0); - return wasapi_microphone_read_sh_nonblock(wasapi, microphone, buffer, buffer_size); + return wasapi_microphone_read_exclusive(microphone, buffer, buffer_size, 0); + return wasapi_microphone_read_shared_nonblock(microphone, buffer, buffer_size); } if (microphone->exclusive) @@ -288,7 +436,8 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v ssize_t read; for (read = -1; bytes_read < buffer_size; bytes_read += read) { - read = wasapi_microphone_read_ex(wasapi, microphone, (char*)buffer + bytes_read, buffer_size - bytes_read, INFINITE); + read = wasapi_microphone_read_exclusive(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read, + INFINITE); if (read == -1) return -1; } @@ -300,7 +449,7 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v { for (read = -1; bytes_read < buffer_size; bytes_read += read) { - read = wasapi_microphone_read_sh_buffer(wasapi, microphone, (char*)buffer + bytes_read, buffer_size - bytes_read); + read = wasapi_microphone_read_shared_buffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read); if (read == -1) return -1; } @@ -309,7 +458,7 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v { for (read = -1; bytes_read < buffer_size; bytes_read += read) { - read = wasapi_microphone_read_sh(wasapi, microphone, (char*)buffer + bytes_read, buffer_size - bytes_read); + read = wasapi_microphone_read_shared_unbuffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read); if (read == -1) return -1; } @@ -434,7 +583,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device microphone->engine_buffer_size = frame_count * microphone->frame_size; if (microphone->exclusive) - { + { /* If this mic should be used *exclusively* by RetroArch... */ microphone->buffer = fifo_new(microphone->engine_buffer_size); if (!microphone->buffer) { @@ -460,7 +609,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device if (!microphone->buffer) goto error; - RARCH_LOG("[WASAPI]: Intermediate buffer length is %u frames (%.1fms).\n", + RARCH_LOG("[WASAPI]: Intermediate capture buffer length is %u frames (%.1fms).\n", sh_buffer_length, (double)sh_buffer_length * 1000.0 / rate); } else @@ -468,7 +617,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device RARCH_LOG("[WASAPI]: Intermediate buffer is off. \n"); } - microphone->read_event = CreateEventA(NULL, FALSE, FALSE, NULL); + microphone->read_event = CreateEventA(NULL, FALSE, FALSE, "Microphone Read"); if (!microphone->read_event) { RARCH_ERR("[WASAPI]: Failed to allocate capture device's event handle\n"); From 39f5ad54c76c63342c6d7f9402629057d1405c98 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 28 Jan 2023 17:29:47 -0500 Subject: [PATCH 162/309] Fix a crash in microphone_driver - Forgot to move the position of the name of null_driver --- microphone/microphone_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index bd92b2ac776d..f9672a07815c 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -34,9 +34,9 @@ microphone_driver_t microphone_null = { NULL, NULL, NULL, + "null", NULL, NULL, - "null", NULL, NULL }; From 6d8cfde56a655f2a4b2ec5593fe89c755eefe8c3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 28 Jan 2023 22:00:32 -0500 Subject: [PATCH 163/309] Reduce some logging in wasapi common functions - Only log the chosen audio client format, not all attempted ones --- audio/common/wasapi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 601df175a9e7..2e75f61ac11b 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -125,9 +125,6 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, /* for requested rate (first) and all preferred rates */ for (j = 0; rate_res; ++j) { - RARCH_LOG("[WASAPI]: Initializing client (exclusive, %s, %uHz, %ums) ...\n", - float_fmt_res ? "float" : "pcm", rate_res, latency); - wasapi_set_format(&wf, float_fmt_res, rate_res); #ifdef __cplusplus hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, @@ -206,6 +203,9 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, *float_fmt = float_fmt_res; *rate = rate_res; + RARCH_LOG("[WASAPI]: Initialized exclusive %s client at %uHz, latency %ums\n", + float_fmt_res ? "float" : "pcm", rate_res, latency); + return client; error: @@ -238,9 +238,6 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, /* for requested rate (first) and all preferred rates */ for (j = 0; rate_res; ++j) { - RARCH_LOG("[WASAPI]: Initializing client (shared, %s, %uHz, %ums) ...\n", - float_fmt_res ? "float" : "pcm", rate_res, latency); - wasapi_set_format(&wf, float_fmt_res, rate_res); #ifdef __cplusplus hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, @@ -291,6 +288,9 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, *float_fmt = float_fmt_res; *rate = rate_res; + RARCH_LOG("[WASAPI]: Initialized shared %s client at %uHz, latency %ums\n", + float_fmt_res ? "float" : "pcm", rate_res, latency); + return client; error: From e9da792a0c59229f97db2207ccacd744a5e893ce Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 17:15:37 -0500 Subject: [PATCH 164/309] Add some macro wrappers for IAudioClient methods --- audio/common/mmdevice_common_inline.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/audio/common/mmdevice_common_inline.h b/audio/common/mmdevice_common_inline.h index 33395cb08354..5875317584d6 100644 --- a/audio/common/mmdevice_common_inline.h +++ b/audio/common/mmdevice_common_inline.h @@ -60,6 +60,10 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 #define _IAudioClient_GetBufferSize(This,pNumBufferFrames) ( (This)->GetBufferSize(pNumBufferFrames) ) #define _IAudioClient_GetStreamLatency(This,phnsLatency) ( (This)->GetStreamLatency(phnsLatency) ) #define _IAudioClient_GetDevicePeriod(This,phnsDefaultDevicePeriod,phnsMinimumDevicePeriod) ( (This)->GetDevicePeriod(phnsDefaultDevicePeriod,phnsMinimumDevicePeriod) ) +#define _IAudioClient_Initialize(This,ShareMode,StreamFlags,hnsBufferDuration,hnsPeriodicity,pFormat,AudioSessionGuid) \ + ( (This)->Initialize(ShareMode,StreamFlags,hnsBufferDuration,hnsPeriodicity,pFormat,AudioSessionGuid)) +#define _IAudioClient_IsFormatSupported(This,ShareMode,pFormat,ppClosestMatch) \ + ( (This)->IsFormatSupported(ShareMode,pFormat,ppClosestMatch)) #define _IMMDevice_Activate(This,iid,dwClsCtx,pActivationParams,ppv) ((This)->Activate(iid,(dwClsCtx),pActivationParams,ppv)) #define _IMMDeviceEnumerator_EnumAudioEndpoints(This,dataFlow,dwStateMask,ppDevices) (This)->EnumAudioEndpoints(dataFlow,dwStateMask,ppDevices) #define _IMMDeviceEnumerator_GetDefaultAudioEndpoint(This,dataFlow,role,ppEndpoint) (This)->GetDefaultAudioEndpoint(dataFlow,role,ppEndpoint) @@ -86,6 +90,10 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 #define _IAudioClient_GetBufferSize(This,pNumBufferFrames) ( (This)->lpVtbl -> GetBufferSize(This,pNumBufferFrames) ) #define _IAudioClient_GetStreamLatency(This,phnsLatency) ( (This)->lpVtbl -> GetStreamLatency(This,phnsLatency) ) #define _IAudioClient_GetDevicePeriod(This,phnsDefaultDevicePeriod,phnsMinimumDevicePeriod) ( (This)->lpVtbl -> GetDevicePeriod(This,phnsDefaultDevicePeriod,phnsMinimumDevicePeriod) ) +#define _IAudioClient_Initialize(This,ShareMode,StreamFlags,hnsBufferDuration,hnsPeriodicity,pFormat,AudioSessionGuid) \ + ( (This)->lpVtbl->Initialize(This,ShareMode,StreamFlags,hnsBufferDuration,hnsPeriodicity,pFormat,AudioSessionGuid)) +#define _IAudioClient_IsFormatSupported(This,ShareMode,pFormat,ppClosestMatch) \ + ( (This)->lpVtbl->IsFormatSupported(This,ShareMode,pFormat,ppClosestMatch)) #define _IMMDevice_Activate(This,iid,dwClsCtx,pActivationParams,ppv) ((This)->lpVtbl->Activate(This,&(iid),dwClsCtx,pActivationParams,ppv)) #define _IMMDeviceEnumerator_EnumAudioEndpoints(This,dataFlow,dwStateMask,ppDevices) (This)->lpVtbl->EnumAudioEndpoints(This,dataFlow,dwStateMask,ppDevices) #define _IMMDeviceEnumerator_GetDefaultAudioEndpoint(This,dataFlow,role,ppEndpoint) (This)->lpVtbl->GetDefaultAudioEndpoint(This,dataFlow,role,ppEndpoint) From 50439e39f1ed2b7434eb80b2e3d3db0124058a33 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 17:17:14 -0500 Subject: [PATCH 165/309] Update mic driver configuration - Make the mic driver configurable in the menu - Add config items for WASAPI-related options similar to the audio driver --- configuration.c | 3 + configuration.h | 3 + intl/msg_hash_lbl.h | 16 +++ intl/msg_hash_us.h | 38 +++++- menu/cbs/menu_cbs_sublabel.c | 16 +++ menu/menu_displaylist.c | 25 +++- menu/menu_setting.c | 218 +++++++++++++++++++++++++++++++++ microphone/drivers/wasapi.c | 6 +- microphone/microphone_driver.c | 9 ++ microphone/microphone_driver.h | 2 + msg_hash.h | 4 + 11 files changed, 332 insertions(+), 8 deletions(-) diff --git a/configuration.c b/configuration.c index 2ec6d129c123..a59e1eb9568a 100644 --- a/configuration.c +++ b/configuration.c @@ -2070,7 +2070,9 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("audio_rate_control", &settings->bools.audio_rate_control, true, DEFAULT_RATE_CONTROL, false); #ifdef HAVE_WASAPI SETTING_BOOL("audio_wasapi_exclusive_mode", &settings->bools.audio_wasapi_exclusive_mode, true, DEFAULT_WASAPI_EXCLUSIVE_MODE, false); + SETTING_BOOL("microphone_wasapi_exclusive_mode", &settings->bools.microphone_wasapi_exclusive_mode, true, DEFAULT_WASAPI_EXCLUSIVE_MODE, false); SETTING_BOOL("audio_wasapi_float_format", &settings->bools.audio_wasapi_float_format, true, DEFAULT_WASAPI_FLOAT_FORMAT, false); + SETTING_BOOL("microphone_wasapi_float_format", &settings->bools.microphone_wasapi_float_format, true, DEFAULT_WASAPI_FLOAT_FORMAT, false); #endif SETTING_BOOL("savestates_in_content_dir", &settings->bools.savestates_in_content_dir, true, DEFAULT_SAVESTATES_IN_CONTENT_DIR, false); @@ -2499,6 +2501,7 @@ static struct config_int_setting *populate_settings_int( #endif #ifdef HAVE_WASAPI SETTING_INT("audio_wasapi_sh_buffer_length", &settings->ints.audio_wasapi_sh_buffer_length, true, DEFAULT_WASAPI_SH_BUFFER_LENGTH, false); + SETTING_INT("microphone_wasapi_sh_buffer_length", &settings->ints.microphone_wasapi_sh_buffer_length, true, DEFAULT_WASAPI_SH_BUFFER_LENGTH, false); #endif SETTING_INT("crt_switch_center_adjust", &settings->ints.crt_switch_center_adjust, false, DEFAULT_CRT_SWITCH_CENTER_ADJUST, false); SETTING_INT("crt_switch_porch_adjust", &settings->ints.crt_switch_porch_adjust, false, DEFAULT_CRT_SWITCH_PORCH_ADJUST, false); diff --git a/configuration.h b/configuration.h index 780cad8e02b9..d2942929666b 100644 --- a/configuration.h +++ b/configuration.h @@ -105,6 +105,7 @@ typedef struct settings int location_update_interval_distance; int state_slot; int audio_wasapi_sh_buffer_length; + int microphone_wasapi_sh_buffer_length; int crt_switch_center_adjust; int crt_switch_porch_adjust; #ifdef HAVE_VULKAN @@ -615,6 +616,8 @@ typedef struct settings /* Microphone */ bool microphone_enable; + bool microphone_wasapi_exclusive_mode; + bool microphone_wasapi_float_format; /* Input */ bool input_remap_binds_enable; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 71e6792363dd..357e9cd88f4d 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -130,6 +130,10 @@ MSG_HASH( MENU_ENUM_LABEL_AUDIO_ENABLE, "audio_enable" ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_ENABLE, + "microphone_enable" + ) MSG_HASH( MENU_ENUM_LABEL_AUDIO_FILTER_DIR, "audio_filter_dir" @@ -222,6 +226,18 @@ MSG_HASH( MENU_ENUM_LABEL_MICROPHONE_BLOCK_FRAMES, "microphone_block_frames" ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_WASAPI_EXCLUSIVE_MODE, + "microphone_wasapi_exclusive_mode" + ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_WASAPI_FLOAT_FORMAT, + "microphone_wasapi_float_format" + ) +MSG_HASH( + MENU_ENUM_LABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, + "microphone_wasapi_sh_buffer_length" + ) MSG_HASH( MENU_ENUM_LABEL_AUTOSAVE_INTERVAL, "autosave_interval" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index ee657886d843..8c87138372d3 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -2554,17 +2554,25 @@ MSG_HASH( ) /* Settings > Audio > Input */ +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MICROPHONE_ENABLE, + "Microphone" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MICROPHONE_ENABLE, + "Enable audio input." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_MICROPHONE_DEVICE, "Device" ) MSG_HASH( MENU_ENUM_SUBLABEL_MICROPHONE_DEVICE, - "Override the default input device the audio driver uses. This is driver dependent." + "Override the default input device the microphone driver uses. This is driver dependent." ) MSG_HASH( MENU_ENUM_LABEL_HELP_MICROPHONE_DEVICE, - "Override the default input device the audio driver uses. This is driver dependent." + "Override the default input device the microphone driver uses. This is driver dependent." ) MSG_HASH( MENU_ENUM_LABEL_VALUE_MICROPHONE_INPUT_RATE, @@ -2580,7 +2588,31 @@ MSG_HASH( ) MSG_HASH( MENU_ENUM_SUBLABEL_MICROPHONE_LATENCY, - "Desired audio input latency in milliseconds. Might not be honored if the audio driver can't provide given latency." + "Desired audio input latency in milliseconds. Might not be honored if the microphone driver can't provide given latency." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MICROPHONE_WASAPI_EXCLUSIVE_MODE, + "WASAPI Exclusive Mode" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MICROPHONE_WASAPI_EXCLUSIVE_MODE, + "Allow the WASAPI driver to take exclusive control of the microphone device. If disabled, it will use shared mode instead." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MICROPHONE_WASAPI_FLOAT_FORMAT, + "WASAPI Float Format" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MICROPHONE_WASAPI_FLOAT_FORMAT, + "Use float format for the WASAPI driver, if supported by your audio device." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, + "WASAPI Shared Buffer Length" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, + "The intermediate buffer length (in frames) when using the WASAPI driver in shared mode." ) /* Settings > Audio > Resampler */ diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 3fc7613ab7fa..f3b4a8555f6d 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -533,6 +533,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_always_reload_core_on_run_content, M DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_refresh_rate, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_refresh_rate_polled, MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_POLLED) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable, MENU_ENUM_SUBLABEL_AUDIO_ENABLE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_enable, MENU_ENUM_SUBLABEL_MICROPHONE_ENABLE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_enable_menu, MENU_ENUM_SUBLABEL_AUDIO_ENABLE_MENU) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_sounds, MENU_ENUM_SUBLABEL_MENU_SOUNDS) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_max_timing_skew, MENU_ENUM_SUBLABEL_AUDIO_MAX_TIMING_SKEW) @@ -779,6 +780,9 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_dsp_plugin_remove, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_wasapi_exclusive_mode, MENU_ENUM_SUBLABEL_AUDIO_WASAPI_EXCLUSIVE_MODE) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_wasapi_float_format, MENU_ENUM_SUBLABEL_AUDIO_WASAPI_FLOAT_FORMAT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_audio_wasapi_sh_buffer_length, MENU_ENUM_SUBLABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_wasapi_exclusive_mode, MENU_ENUM_SUBLABEL_MICROPHONE_WASAPI_EXCLUSIVE_MODE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_wasapi_float_format, MENU_ENUM_SUBLABEL_MICROPHONE_WASAPI_FLOAT_FORMAT) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_microphone_wasapi_sh_buffer_length, MENU_ENUM_SUBLABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_overlay_opacity, MENU_ENUM_SUBLABEL_OVERLAY_OPACITY) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_overlay_scale_landscape, MENU_ENUM_SUBLABEL_OVERLAY_SCALE_LANDSCAPE) @@ -3559,6 +3563,15 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_wasapi_sh_buffer_length); break; + case MENU_ENUM_LABEL_MICROPHONE_WASAPI_EXCLUSIVE_MODE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_wasapi_exclusive_mode); + break; + case MENU_ENUM_LABEL_MICROPHONE_WASAPI_FLOAT_FORMAT: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_wasapi_float_format); + break; + case MENU_ENUM_LABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_wasapi_sh_buffer_length); + break; case MENU_ENUM_LABEL_MENU_WALLPAPER: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_wallpaper); break; @@ -4175,6 +4188,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_AUDIO_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable); break; + case MENU_ENUM_LABEL_MICROPHONE_ENABLE: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_microphone_enable); + break; case MENU_ENUM_LABEL_AUDIO_ENABLE_MENU: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_enable_menu); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index ee0a69f9a54b..86c5df6658c4 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6675,13 +6675,21 @@ unsigned menu_displaylist_build_list( break; case DISPLAYLIST_MICROPHONE_SETTINGS_LIST: if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, - MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE, - PARSE_ONLY_UINT, false) == 0) + MENU_ENUM_LABEL_MICROPHONE_ENABLE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_MICROPHONE_DRIVER, + PARSE_ONLY_STRING_OPTIONS, false) == 0) count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_MICROPHONE_DEVICE, PARSE_ONLY_STRING, false) == 0) count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE, + PARSE_ONLY_UINT, false) == 0) + count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_MICROPHONE_LATENCY, PARSE_ONLY_UINT, false) == 0) @@ -6690,6 +6698,18 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_MICROPHONE_BLOCK_FRAMES, PARSE_ONLY_UINT, false) == 0) count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_MICROPHONE_WASAPI_EXCLUSIVE_MODE, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_MICROPHONE_WASAPI_FLOAT_FORMAT, + PARSE_ONLY_BOOL, false) == 0) + count++; + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, + PARSE_ONLY_INT, false) == 0) + count++; break; case DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, @@ -6715,6 +6735,7 @@ unsigned menu_displaylist_build_list( #endif menu_displaylist_build_info_selective_t build_list[] = { {MENU_ENUM_LABEL_AUDIO_OUTPUT_SETTINGS, PARSE_ACTION, true }, + {MENU_ENUM_LABEL_MICROPHONE_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_AUDIO_RESAMPLER_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_AUDIO_SYNCHRONIZATION_SETTINGS, PARSE_ACTION, true }, {MENU_ENUM_LABEL_MIDI_SETTINGS, PARSE_ACTION, true }, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index f696583196cb..c8e04f1fa2b7 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -86,6 +86,7 @@ #include "../dynamic.h" #include "../list_special.h" #include "../audio/audio_driver.h" +#include "../microphone/microphone_driver.h" #ifdef HAVE_BLUETOOTH #include "../bluetooth/bluetooth_driver.h" #endif @@ -284,6 +285,7 @@ enum settings_list_type SETTINGS_LIST_VIDEO, SETTINGS_LIST_CRT_SWITCHRES, SETTINGS_LIST_AUDIO, + SETTINGS_LIST_MICROPHONE, SETTINGS_LIST_INPUT, SETTINGS_LIST_INPUT_TURBO_FIRE, SETTINGS_LIST_INPUT_HOTKEY, @@ -5857,6 +5859,32 @@ static int setting_string_action_left_audio_device( return 0; } + +static int setting_string_action_left_microphone_device( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + int microphone_device_index; + struct string_list *ptr = NULL; + + if (!microphone_driver_get_devices_list((void**)&ptr)) + return -1; + + if (!ptr) + return -1; + + /* Get index in the string list */ + microphone_device_index = string_list_find_elem( + ptr, setting->value.target.string) - 1; + microphone_device_index--; + + /* Reset index if needed */ + if (microphone_device_index < 0) + microphone_device_index = (int)(ptr->size - 1); + + strlcpy(setting->value.target.string, ptr->elems[microphone_device_index].data, setting->size); + + return 0; +} #endif static int setting_string_action_left_driver( @@ -6140,6 +6168,32 @@ static int setting_string_action_right_audio_device( return 0; } + +static int setting_string_action_right_microphone_device( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + int microphone_device_index; + struct string_list *ptr = NULL; + + if (!microphone_driver_get_devices_list((void**)&ptr)) + return -1; + + if (!ptr) + return -1; + + /* Get index in the string list */ + microphone_device_index = string_list_find_elem(ptr,setting->value.target.string) -1; + microphone_device_index++; + + /* Reset index if needed */ + if (microphone_device_index == (signed)ptr->size) + microphone_device_index = 0; + + strlcpy(setting->value.target.string, + ptr->elems[microphone_device_index].data, setting->size); + + return 0; +} #endif static int setting_string_action_right_driver( @@ -13835,6 +13889,169 @@ static bool setting_append_list( } #endif + END_SUB_GROUP(list, list_info, parent_group); + END_GROUP(list, list_info, parent_group); + break; + case SETTINGS_LIST_MICROPHONE: + START_GROUP(list, list_info, &group_info, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MICROPHONE_SETTINGS), parent_group); + MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_IDX_PTR(list, list_info, MENU_ENUM_LABEL_MICROPHONE_SETTINGS); + + parent_group = msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS); + + START_SUB_GROUP(list, list_info, "State", &group_info, &subgroup_info, parent_group); + + CONFIG_BOOL( + list, list_info, + &settings->bools.microphone_enable, + MENU_ENUM_LABEL_MICROPHONE_ENABLE, + MENU_ENUM_LABEL_VALUE_MICROPHONE_ENABLE, + DEFAULT_AUDIO_ENABLE, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + + END_SUB_GROUP(list, list_info, parent_group); + + parent_group = msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS); + + CONFIG_UINT( + list, list_info, + &settings->uints.microphone_latency, + MENU_ENUM_LABEL_MICROPHONE_LATENCY, + MENU_ENUM_LABEL_VALUE_MICROPHONE_LATENCY, + g_defaults.settings_in_latency ? + g_defaults.settings_in_latency : DEFAULT_IN_LATENCY, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 0, 512, 1.0, true, true); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED); + +#ifdef RARCH_MOBILE + CONFIG_UINT( + list, list_info, + &settings->uints.microphone_block_frames, + MENU_ENUM_LABEL_MICROPHONE_BLOCK_FRAMES, + MENU_ENUM_LABEL_VALUE_MICROPHONE_BLOCK_FRAMES, + 0, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); +#endif + + END_SUB_GROUP(list, list_info, parent_group); + + parent_group = msg_hash_to_str(MENU_ENUM_LABEL_SETTINGS); + + START_SUB_GROUP( + list, + list_info, + "Miscellaneous", + &group_info, + &subgroup_info, + parent_group); + +#if !defined(RARCH_CONSOLE) + CONFIG_STRING( + list, list_info, + settings->arrays.microphone_device, + sizeof(settings->arrays.microphone_device), + MENU_ENUM_LABEL_MICROPHONE_DEVICE, + MENU_ENUM_LABEL_VALUE_MICROPHONE_DEVICE, + "", + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT; + (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; + (*list)[list_info->index - 1].action_left = &setting_string_action_left_microphone_device; + (*list)[list_info->index - 1].action_right = &setting_string_action_right_microphone_device; +#endif + + CONFIG_UINT( + list, list_info, + &settings->uints.microphone_sample_rate, + MENU_ENUM_LABEL_MICROPHONE_INPUT_RATE, + MENU_ENUM_LABEL_VALUE_MICROPHONE_INPUT_RATE, + DEFAULT_INPUT_RATE, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint_special; + menu_settings_list_current_add_range(list, list_info, 1000, 192000, 100.0, true, true); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); + +#ifdef HAVE_WASAPI + if (string_is_equal(settings->arrays.microphone_driver, "wasapi")) + { + CONFIG_BOOL( + list, list_info, + &settings->bools.microphone_wasapi_exclusive_mode, + MENU_ENUM_LABEL_MICROPHONE_WASAPI_EXCLUSIVE_MODE, + MENU_ENUM_LABEL_VALUE_MICROPHONE_WASAPI_EXCLUSIVE_MODE, + DEFAULT_WASAPI_EXCLUSIVE_MODE, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + + CONFIG_BOOL( + list, list_info, + &settings->bools.microphone_wasapi_float_format, + MENU_ENUM_LABEL_MICROPHONE_WASAPI_FLOAT_FORMAT, + MENU_ENUM_LABEL_VALUE_MICROPHONE_WASAPI_FLOAT_FORMAT, + DEFAULT_WASAPI_FLOAT_FORMAT, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE + ); + + CONFIG_INT( + list, list_info, + &settings->ints.microphone_wasapi_sh_buffer_length, + MENU_ENUM_LABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, + MENU_ENUM_LABEL_VALUE_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, + DEFAULT_WASAPI_SH_BUFFER_LENGTH, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, -16.0f, 0.0f, 16.0f, true, false); + SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_int_audio_wasapi_sh_buffer_length; + } +#endif + END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); break; @@ -22551,6 +22768,7 @@ static rarch_setting_t *menu_setting_new_internal(rarch_setting_info_t *list_inf SETTINGS_LIST_VIDEO, SETTINGS_LIST_CRT_SWITCHRES, SETTINGS_LIST_AUDIO, + SETTINGS_LIST_MICROPHONE, SETTINGS_LIST_INPUT, SETTINGS_LIST_INPUT_TURBO_FIRE, SETTINGS_LIST_INPUT_HOTKEY, diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 50f48d7f7ece..f06babcb6a39 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -531,9 +531,9 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device UINT32 frame_count = 0; REFERENCE_TIME dev_period = 0; BYTE *dest = NULL; - bool float_format = settings->bools.audio_wasapi_float_format; - bool exclusive_mode = settings->bools.audio_wasapi_exclusive_mode; - int sh_buffer_length = settings->ints.audio_wasapi_sh_buffer_length; + bool float_format = settings->bools.microphone_wasapi_float_format; + bool exclusive_mode = settings->bools.microphone_wasapi_exclusive_mode; + int sh_buffer_length = settings->ints.microphone_wasapi_sh_buffer_length; wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; wasapi_microphone_handle_t *microphone = calloc(1, sizeof(wasapi_microphone_handle_t)); diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index f9672a07815c..036d45442313 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -643,4 +643,13 @@ bool microphone_driver_deinit(void) microphone_driver_free_devices_list(); microphone_driver_close_microphones(); return microphone_driver_deinit_internal(); +} + +bool microphone_driver_get_devices_list(void **data) +{ + struct string_list**ptr = (struct string_list**)data; + if (!ptr) + return false; + *ptr = mic_driver_st.devices_list; + return true; } \ No newline at end of file diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index 07dcfa90bc53..4dfa81e9a0af 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -468,4 +468,6 @@ bool microphone_driver_find_driver( const char *prefix, bool verbosity_enabled); +bool microphone_driver_get_devices_list(void **ptr); + #endif /* RETROARCH_MICROPHONE_DRIVER_H */ \ No newline at end of file diff --git a/msg_hash.h b/msg_hash.h index 22fe72bf3a1c..4075ce18caf4 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2072,10 +2072,14 @@ enum msg_hash_enums MENU_LABEL(AUDIO_WASAPI_SH_BUFFER_LENGTH), /* Microphone */ + MENU_LABEL(MICROPHONE_ENABLE), MENU_LABEL(MICROPHONE_INPUT_RATE), MENU_LABEL(MICROPHONE_LATENCY), MENU_LBL_H(MICROPHONE_DEVICE), MENU_LABEL(MICROPHONE_BLOCK_FRAMES), + MENU_LABEL(MICROPHONE_WASAPI_EXCLUSIVE_MODE), + MENU_LABEL(MICROPHONE_WASAPI_FLOAT_FORMAT), + MENU_LABEL(MICROPHONE_WASAPI_SH_BUFFER_LENGTH), MENU_LABEL(SAVE_STATE), MENU_LABEL(LOAD_STATE), From 26f2d4a380c411377857158c1fdf336828d352db Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 17:26:44 -0500 Subject: [PATCH 166/309] Fix a menu entry scrolling through audio devices instead of mic devices --- menu/menu_setting.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index c8e04f1fa2b7..a52c2aa29d90 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -13786,8 +13786,8 @@ static bool setting_append_list( SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT; (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; - (*list)[list_info->index - 1].action_left = &setting_string_action_left_audio_device; - (*list)[list_info->index - 1].action_right = &setting_string_action_right_audio_device; + (*list)[list_info->index - 1].action_left = &setting_string_action_left_microphone_device; + (*list)[list_info->index - 1].action_right = &setting_string_action_right_microphone_device; #endif CONFIG_UINT( From da855f3a7846ba89b4e6c9c36e0689bbfdea3a24 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 17:31:25 -0500 Subject: [PATCH 167/309] Add some utility functions --- audio/common/wasapi.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 2e75f61ac11b..af755f3f258c 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -37,6 +37,66 @@ void wasapi_log_hr(HRESULT hr, char* buffer, size_t length) NULL); } +static const char *hresult_name(HRESULT hr) +{ + switch (hr) + { + case E_NOINTERFACE: + return "E_NOINTERFACE"; + case E_POINTER: + return "E_POINTER"; + case E_OUTOFMEMORY: + return "E_OUTOFMEMORY"; + case E_INVALIDARG: + return "E_INVALIDARG"; + case AUDCLNT_E_DEVICE_INVALIDATED: + return "AUDCLNT_E_DEVICE_INVALIDATED"; + case AUDCLNT_E_ALREADY_INITIALIZED: + return "AUDCLNT_E_ALREADY_INITIALIZED"; + case AUDCLNT_E_WRONG_ENDPOINT_TYPE: + return "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; + case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: + return "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; + case AUDCLNT_E_BUFFER_SIZE_ERROR: + return "AUDCLNT_E_BUFFER_SIZE_ERROR"; + case AUDCLNT_E_CPUUSAGE_EXCEEDED: + return "AUDCLNT_E_CPUUSAGE_EXCEEDED"; + case AUDCLNT_E_DEVICE_IN_USE: + return "AUDCLNT_E_DEVICE_IN_USE"; + case AUDCLNT_E_ENDPOINT_CREATE_FAILED: + return "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; + case AUDCLNT_E_INVALID_DEVICE_PERIOD: + return "AUDCLNT_E_INVALID_DEVICE_PERIOD"; + case AUDCLNT_E_UNSUPPORTED_FORMAT: + return "AUDCLNT_E_UNSUPPORTED_FORMAT"; + case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: + return "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; + case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: + return "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; + case AUDCLNT_E_SERVICE_NOT_RUNNING: + return "AUDCLNT_E_SERVICE_NOT_RUNNING"; + default: + return ""; + } +} + +static const char* wasapi_last_error(DWORD error) +{ + static char error_message[256]; + + FormatMessage( + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + error_message, + sizeof(error_message) - 1, + NULL); + + return error_message; +} + static const char* wasapi_data_flow_name(EDataFlow data_flow) { switch (data_flow) From 35b1e1d749e79d1acb2e0f1f9a072a3fc5ecdb5d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 17:33:05 -0500 Subject: [PATCH 168/309] Expose the new utility functions in wasapi.h --- audio/common/wasapi.c | 4 ++-- audio/common/wasapi.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index af755f3f258c..c01448b9027c 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -37,7 +37,7 @@ void wasapi_log_hr(HRESULT hr, char* buffer, size_t length) NULL); } -static const char *hresult_name(HRESULT hr) +const char *hresult_name(HRESULT hr) { switch (hr) { @@ -80,7 +80,7 @@ static const char *hresult_name(HRESULT hr) } } -static const char* wasapi_last_error(DWORD error) +const char* wasapi_error(DWORD error) { static char error_message[256]; diff --git a/audio/common/wasapi.h b/audio/common/wasapi.h index 88ce61f3c8b5..ee5d2dbf23f0 100644 --- a/audio/common/wasapi.h +++ b/audio/common/wasapi.h @@ -26,6 +26,8 @@ #include "../common/mmdevice_common_inline.h" #include "boolean.h" +const char *hresult_name(HRESULT hr); +const char* wasapi_error(DWORD error); void wasapi_log_hr(HRESULT hr, char* buffer, size_t length); IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow); IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, From 74a2e9c7ca556cf9a0a2c1bfdbfe7f0674a3b9da Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 18:26:49 -0500 Subject: [PATCH 169/309] Add extra logging in the WASAPI common functions --- audio/common/wasapi.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index c01448b9027c..5ed3b46ceb34 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -163,12 +163,21 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, HRESULT hr = _IMMDevice_Activate(device, IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + { + RARCH_ERR("[WASAPI]: IMMDevice::Activate failed (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); return NULL; + } hr = _IAudioClient_GetDevicePeriod(client, NULL, &minimum_period); if (FAILED(hr)) + { + RARCH_ERR("[WASAPI]: Failed to get device period of exclusive-mode client (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); goto error; + } /* buffer_duration is in 100ns units */ buffer_duration = latency * 10000.0; @@ -197,16 +206,25 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, #endif if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { + RARCH_WARN("[WASAPI] Unaligned buffer size: %s", wasapi_error(HRESULT_CODE(hr))); hr = _IAudioClient_GetBufferSize(client, &buffer_length); if (FAILED(hr)) + { + RARCH_ERR("[WASAPI] Failed to get buffer size of client (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); goto error; + } IFACE_RELEASE(client); hr = _IMMDevice_Activate(device, IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client); if (FAILED(hr)) + { + RARCH_ERR("[WASAPI] IMMDevice::Activate failed (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); return NULL; + } buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; #ifdef __cplusplus @@ -226,7 +244,11 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client); if (FAILED(hr)) + { + RARCH_ERR("[WASAPI] IMMDevice::Activate failed (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); return NULL; + } #ifdef __cplusplus hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, @@ -258,7 +280,11 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, } if (FAILED(hr)) + { + RARCH_ERR("[WASAPI]: Failed to create exclusive-mode client with %s: %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); goto error; + } *float_fmt = float_fmt_res; *rate = rate_res; @@ -285,8 +311,12 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, HRESULT hr = _IMMDevice_Activate(device, IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + { /* If we couldn't create the IAudioClient... */ + RARCH_ERR("[WASAPI]: Failed to create %s IAudioClient with %s: %s", hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); return NULL; + } /* once for float, once for pcm (requested first) */ for (i = 0; i < 2; ++i) @@ -343,7 +373,11 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, } if (FAILED(hr)) + { + RARCH_ERR("[WASAPI]: IAudioClient::Initialize failed with %s: %s", hresult_name(hr), + wasapi_error(HRESULT_CODE(hr))); goto error; + } *float_fmt = float_fmt_res; *rate = rate_res; @@ -504,6 +538,10 @@ IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, REFERENCE_TIME stream_latency = 0; UINT32 buffer_length = 0; + RARCH_DBG("[WASAPI]: Requesting %s %s client (rate=%uHz, latency=%ums).\n", + *exclusive ? "exclusive" : "shared", + *float_fmt ? "float" : "pcm", *rate, latency); + if (*exclusive) { client = wasapi_init_client_ex(device, float_fmt, rate, latency); From 7c6418359d4a1be39d0f7f250bc9274ddab35447 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 18:27:08 -0500 Subject: [PATCH 170/309] Add sharemode_name --- audio/common/wasapi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 5ed3b46ceb34..d29181f78163 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -80,6 +80,19 @@ const char *hresult_name(HRESULT hr) } } +const char *sharemode_name(AUDCLNT_SHAREMODE mode) +{ + switch (mode) + { + case AUDCLNT_SHAREMODE_SHARED: + return "shared"; + case AUDCLNT_SHAREMODE_EXCLUSIVE: + return "exclusive"; + default: + return ""; + } +} + const char* wasapi_error(DWORD error) { static char error_message[256]; From cc2c91f674a50756bf636f6dcd3d03d166e474ad Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 18:28:16 -0500 Subject: [PATCH 171/309] Use _IAudioClient_Initialize macro in some places --- audio/common/wasapi.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index d29181f78163..90447b97b942 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -208,15 +208,9 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, for (j = 0; rate_res; ++j) { wasapi_set_format(&wf, float_fmt_res, rate_res); -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#endif if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { RARCH_WARN("[WASAPI] Unaligned buffer size: %s", wasapi_error(HRESULT_CODE(hr))); @@ -240,15 +234,9 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, } buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#endif } if (hr == AUDCLNT_E_ALREADY_INITIALIZED) { @@ -263,15 +251,9 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, return NULL; } -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); -#endif } if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) { From 86b6fcfebf366b3eec95dec1f5e679dd5789a118 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 18:36:12 -0500 Subject: [PATCH 172/309] Pass channels to wasapi_init_client - Remember, mics are in mono --- audio/common/wasapi.c | 14 +++++++------- audio/common/wasapi.h | 2 +- audio/drivers/wasapi.c | 2 +- microphone/drivers/wasapi.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 90447b97b942..d81d9937346b 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -136,9 +136,9 @@ static unsigned wasapi_pref_rate(unsigned i) } static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, - bool float_fmt, unsigned rate) + bool float_fmt, unsigned rate, unsigned channels) { - wf->Format.nChannels = 2; + wf->Format.nChannels = channels; wf->Format.nSamplesPerSec = rate; if (float_fmt) @@ -149,7 +149,7 @@ static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, wf->Format.wBitsPerSample = 32; wf->Format.cbSize = sizeof(WORD) + sizeof(DWORD) + sizeof(GUID); wf->Samples.wValidBitsPerSample = 32; - wf->dwChannelMask = KSAUDIO_SPEAKER_STEREO; + wf->dwChannelMask = channels == 1 ? KSAUDIO_SPEAKER_MONO : KSAUDIO_SPEAKER_STEREO; wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else @@ -163,7 +163,7 @@ static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, } static IAudioClient *wasapi_init_client_ex(IMMDevice *device, - bool *float_fmt, unsigned *rate, unsigned latency) + bool *float_fmt, unsigned *rate, unsigned latency, unsigned channels) { WAVEFORMATEXTENSIBLE wf; int i, j; @@ -207,7 +207,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, /* for requested rate (first) and all preferred rates */ for (j = 0; rate_res; ++j) { - wasapi_set_format(&wf, float_fmt_res, rate_res); + wasapi_set_format(&wf, float_fmt_res, rate_res, channels); hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); @@ -296,7 +296,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, } static IAudioClient *wasapi_init_client_sh(IMMDevice *device, - bool *float_fmt, unsigned *rate, unsigned latency) + bool *float_fmt, unsigned *rate, unsigned latency, unsigned channels) { WAVEFORMATEXTENSIBLE wf; int i, j; @@ -323,7 +323,7 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, /* for requested rate (first) and all preferred rates */ for (j = 0; rate_res; ++j) { - wasapi_set_format(&wf, float_fmt_res, rate_res); + wasapi_set_format(&wf, float_fmt_res, rate_res, channels); #ifdef __cplusplus hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, diff --git a/audio/common/wasapi.h b/audio/common/wasapi.h index ee5d2dbf23f0..8ceb2d4ed3fe 100644 --- a/audio/common/wasapi.h +++ b/audio/common/wasapi.h @@ -31,6 +31,6 @@ const char* wasapi_error(DWORD error); void wasapi_log_hr(HRESULT hr, char* buffer, size_t length); IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow); IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, - bool *float_fmt, unsigned *rate, unsigned latency); + bool *float_fmt, unsigned *rate, unsigned latency, unsigned channels); #endif /* RETROARCH_COMMON_WASAPI_H */ \ No newline at end of file diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index a5a156542172..1412f0096843 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -65,7 +65,7 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency, goto error; w->client = wasapi_init_client(w->device, - &w->exclusive, &float_format, &rate, latency); + &w->exclusive, &float_format, &rate, latency, 2); if (!w->client) goto error; diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index f06babcb6a39..8851755e9556 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -563,7 +563,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device } microphone->client = wasapi_init_client(microphone->device, - µphone->exclusive, &float_format, &rate, latency); + µphone->exclusive, &float_format, &rate, latency, 1); if (!microphone->client) { RARCH_ERR("[WASAPI]: Failed to open client for capture device \"%ls\"\n", microphone->device_id); From d28e9d3de8564631b0634a1687ebc97cff06d441 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 18:37:45 -0500 Subject: [PATCH 173/309] Use _IAudioClient_Initialize macro some more --- audio/common/wasapi.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index d81d9937346b..abb95933c173 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -324,15 +324,9 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, for (j = 0; rate_res; ++j) { wasapi_set_format(&wf, float_fmt_res, rate_res, channels); -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, 0, 0, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); -#endif if (hr == AUDCLNT_E_ALREADY_INITIALIZED) { @@ -344,15 +338,9 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, if (FAILED(hr)) return NULL; -#ifdef __cplusplus - hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, 0, 0, (WAVEFORMATEX*)&wf, NULL); -#else - hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); -#endif } if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) { From ed8d53dad3a5245eafd6e963d757077c8355d659 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 18:38:00 -0500 Subject: [PATCH 174/309] Forgot to pass channels in some places --- audio/common/wasapi.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index abb95933c173..8dad523fd225 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -512,7 +512,7 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow) } IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, - bool *float_fmt, unsigned *rate, unsigned latency) + bool *float_fmt, unsigned *rate, unsigned latency, unsigned channels) { HRESULT hr; IAudioClient *client; @@ -527,20 +527,22 @@ IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, if (*exclusive) { - client = wasapi_init_client_ex(device, float_fmt, rate, latency); + client = wasapi_init_client_ex(device, float_fmt, rate, latency, channels); if (!client) { - client = wasapi_init_client_sh(device, float_fmt, rate, latency); + RARCH_WARN("[WASAPI] Failed to initialize exclusive client, attempting shared client.\n"); + client = wasapi_init_client_sh(device, float_fmt, rate, latency, channels); if (client) *exclusive = false; } } else { - client = wasapi_init_client_sh(device, float_fmt, rate, latency); + client = wasapi_init_client_sh(device, float_fmt, rate, latency, channels); if (!client) { - client = wasapi_init_client_ex(device, float_fmt, rate, latency); + RARCH_WARN("[WASAPI] Failed to initialize shared client, attempting exclusive client.\n"); + client = wasapi_init_client_ex(device, float_fmt, rate, latency, channels); if (client) *exclusive = true; } From f353395a62e4d87f025c0a599267fdf595c747b9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 19:32:52 -0500 Subject: [PATCH 175/309] Add some utility functions --- audio/common/wasapi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 8dad523fd225..489613eebadf 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -80,6 +80,29 @@ const char *hresult_name(HRESULT hr) } } +const char *wave_subtype_name(const GUID *guid) +{ + if (IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + return "KSDATAFORMAT_SUBTYPE_IEEE_FLOAT"; + } + + return ""; +} + +const char *wave_format_name(const WAVEFORMATEXTENSIBLE *format) +{ + switch (format->Format.wFormatTag) + { + case WAVE_FORMAT_PCM: + return "WAVE_FORMAT_PCM"; + case WAVE_FORMAT_EXTENSIBLE: + return wave_subtype_name(&format->SubFormat); + default: + return ""; + } +} + const char *sharemode_name(AUDCLNT_SHAREMODE mode) { switch (mode) From 2432fa5dd67d13fc86a9c27574116f3ced8310c8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 19:33:01 -0500 Subject: [PATCH 176/309] Forgot an #include --- audio/common/wasapi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 489613eebadf..feefe260ba93 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -16,6 +16,7 @@ #include "wasapi.h" #include +#include #include "microphone/microphone_driver.h" #include "queues/fifo_queue.h" #include "configuration.h" From 06d63101bf228f6d7d47c965e6e0a351b51298d7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 19:33:45 -0500 Subject: [PATCH 177/309] Add wasapi_select_device_format --- audio/common/wasapi.c | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index feefe260ba93..3895102aae54 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -159,6 +159,54 @@ static unsigned wasapi_pref_rate(unsigned i) return r[i]; } +/** + * Selects a device format + * @param[in,out] format The place where the chosen format will be written, + * as well as the first format checked. + * @param client TODO + * @param mode todo + * @return true if successful, false if there was an error or a suitable format wasn't found + */ +static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClient *client, AUDCLNT_SHAREMODE mode) +{ + static const unsigned preferred_rates[] = { 48000, 44100, 96000, 192000, 32000 }; + WAVEFORMATEXTENSIBLE *suggested_format = NULL; + bool result = false; + HRESULT hr = _IAudioClient_IsFormatSupported(client, mode, + (const WAVEFORMATEX *) format, (WAVEFORMATEX **) &suggested_format); + /* The Windows docs say that casting these arguments to WAVEFORMATEX* is okay. */ + switch (hr) + { + case S_OK: + /* The requested format is okay without any changes */ + result = true; + break; + case S_FALSE: + /* The requested format is unsupported, but Windows has suggested a similar one. */ + // TODO: Check that the suggested format meets RetroArch's requirements + *format = *suggested_format; + result = true; + break; + case AUDCLNT_E_UNSUPPORTED_FORMAT: + /* The requested format is unsupported, and Windows was unable to suggest another. + * Usually happens with exclusive mode. */ + // TODO: Try to pick a format manually + default: + /* Something else went wrong. */ + RARCH_ERR("[WASAPI]: Failed to select client format (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + result = false; + break; + } + + if (suggested_format) + { /* IAudioClient::IsFormatSupported allocates a format object */ + CoTaskMemFree(suggested_format); + } + + return result; +} + static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, bool float_fmt, unsigned rate, unsigned channels) { From 1a043445d0527ab911add03295bb9f4e9603ebac Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 30 Jan 2023 19:34:37 -0500 Subject: [PATCH 178/309] Simplify the format selection logic in wasapi_init_client_sh --- audio/common/wasapi.c | 70 +++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 3895102aae54..208a422efda5 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -385,46 +385,38 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, return NULL; } - /* once for float, once for pcm (requested first) */ - for (i = 0; i < 2; ++i) - { - rate_res = *rate; - if (i == 1) - float_fmt_res = !float_fmt_res; + wasapi_set_format(&wf, float_fmt_res, rate_res, channels); - /* for requested rate (first) and all preferred rates */ - for (j = 0; rate_res; ++j) - { - wasapi_set_format(&wf, float_fmt_res, rate_res, channels); - hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); + RARCH_LOG("[WASAPI]: Requesting %u-channel client with %s samples at %uHz\n", + wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); - if (hr == AUDCLNT_E_ALREADY_INITIALIZED) - { - HRESULT hr; - IFACE_RELEASE(client); - hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - return NULL; + if (wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_SHARED)) + { + RARCH_LOG("[WASAPI]: Got %u-channel client with %s samples at %uHz\n", + wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); + } + else + { + RARCH_ERR("[WASAPI]: Failed to select a suitable device format\n"); + goto error; + } - hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - 0, 0, (WAVEFORMATEX*)&wf, NULL); - } - if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) - { - i = 2; /* break from outer loop too */ - break; - } + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + 0, 0, (WAVEFORMATEX*)&wf, NULL); - RARCH_WARN("[WASAPI]: Unsupported format.\n"); - rate_res = wasapi_pref_rate(j); - if (rate_res == *rate) /* requested rate is allready tested */ - rate_res = wasapi_pref_rate(++j); /* skip it */ - } + if (hr == AUDCLNT_E_ALREADY_INITIALIZED) + { + IFACE_RELEASE(client); + hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + return NULL; + + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + 0, 0, (WAVEFORMATEX*)&wf, NULL); } if (FAILED(hr)) @@ -434,11 +426,11 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, goto error; } - *float_fmt = float_fmt_res; - *rate = rate_res; + *float_fmt = IsEqualGUID(&wf.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT); + *rate = wf.Format.nSamplesPerSec; RARCH_LOG("[WASAPI]: Initialized shared %s client at %uHz, latency %ums\n", - float_fmt_res ? "float" : "pcm", rate_res, latency); + wave_format_name(&wf), *rate, latency); return client; From e11b5fce2cac040da510fdec6f141e6fdca35a02 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 12:55:13 -0500 Subject: [PATCH 179/309] Unset the microphone in wasapi_microphone_close_mic - Ought to prevent a potential segfault --- microphone/drivers/wasapi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 8851755e9556..b2ca5e6c26fc 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -703,6 +703,8 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c CoTaskMemFree(microphone->device_id); free(microphone); + wasapi->microphone = NULL; + ir = WaitForSingleObject(write_event, 20); if (ir == WAIT_FAILED) { From 9303cf1859dbb3ff02f98d5dcf6209bceb8142ae Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 12:56:16 -0500 Subject: [PATCH 180/309] Simplify some logging --- microphone/drivers/wasapi.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index b2ca5e6c26fc..c273ff22006d 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -113,9 +113,9 @@ static bool wasapi_microphone_fetch_buffer( hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frame_count, &buffer_status_flags, NULL, NULL); if (FAILED(hr)) { - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer (%s)\n", + microphone->device_id, + hresult_name(hr)); return false; } @@ -123,9 +123,9 @@ static bool wasapi_microphone_fetch_buffer( hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, buffer_status_flags); if (FAILED(hr)) { - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer (%s)\n", + microphone->device_id, + hresult_name(hr)); return false; } @@ -151,9 +151,9 @@ static bool wasapi_microphone_fetch_fifo( hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frame_count, &buffer_status_flags, NULL, NULL); if (FAILED(hr)) { - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", + microphone->device_id, + hresult_name(hr)); return false; } @@ -161,9 +161,9 @@ static bool wasapi_microphone_fetch_fifo( hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, buffer_status_flags); if (FAILED(hr)) { - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer: %s\n", + microphone->device_id, + hresult_name(hr)); return false; } @@ -198,7 +198,7 @@ static bool wasapi_microphone_wait_for_capture_event(wasapi_microphone_handle_t return false; } default: - RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: Unknown error\n", microphone->device_id); + RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: %s\n", microphone->device_id, wasapi_error(GetLastError())); return false; } } @@ -739,9 +739,8 @@ static bool wasapi_microphone_start_mic(void *driver_context, void *microphone_c } else { - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI mic]: Failed to start capture device \"%ls\"'s IAudioClient: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI mic]: Failed to start capture device \"%ls\"'s IAudioClient: %s\n", + microphone->device_id, hresult_name(hr)); microphone->running = false; } } @@ -767,9 +766,8 @@ static bool wasapi_microphone_stop_mic(void *driver_context, void *microphone_co hr = _IAudioClient_Stop(microphone->client); if (FAILED(hr)) { - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI mic]: Failed to stop capture device \"%ls\"'s IAudioClient: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI mic]: Failed to stop capture device \"%ls\"'s IAudioClient: %s\n", + microphone->device_id, hresult_name(hr)); return false; } From 7e8295613c253af81d58144516b68821605cc8e3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 12:58:37 -0500 Subject: [PATCH 181/309] Fix incorrect value being passed to _IAudioCaptureClient_ReleaseBuffer --- microphone/drivers/wasapi.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index c273ff22006d..e1315a852b2d 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -107,6 +107,7 @@ static bool wasapi_microphone_fetch_buffer( { BYTE *mic_input = NULL; UINT32 frame_count = 0; + UINT32 byte_count = 0; DWORD buffer_status_flags = 0; HRESULT hr; @@ -118,9 +119,10 @@ static bool wasapi_microphone_fetch_buffer( hresult_name(hr)); return false; } + byte_count = frame_count * microphone->frame_size; memcpy(buffer, mic_input, MIN(buffer_size, frame_count)); - hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, buffer_status_flags); + hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, byte_count); if (FAILED(hr)) { RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer (%s)\n", @@ -145,6 +147,7 @@ static bool wasapi_microphone_fetch_fifo( { BYTE *mic_input = NULL; UINT32 frame_count = 0; + UINT32 byte_count = 0; DWORD buffer_status_flags = 0; HRESULT hr; @@ -156,9 +159,12 @@ static bool wasapi_microphone_fetch_fifo( hresult_name(hr)); return false; } + byte_count = frame_count * microphone->frame_size; + // TODO: As written, this can drop frames + // TODO: Determine if the queue can fit the available frames. If not, don't read them - fifo_write(microphone->buffer, mic_input, MIN(buffer_size, frame_count)); - hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, buffer_status_flags); + fifo_write(microphone->buffer, mic_input, MIN(buffer_size, byte_count)); + hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, frame_count); if (FAILED(hr)) { RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer: %s\n", From 945d2b386a803686999e35618d1b27e16a4fda8a Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 12:58:53 -0500 Subject: [PATCH 182/309] Remove some unneeded logging --- microphone/drivers/wasapi.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index e1315a852b2d..53b3b7bf2f3a 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -195,14 +195,6 @@ static bool wasapi_microphone_wait_for_capture_event(wasapi_microphone_handle_t /* Time out; there's nothing here for us. */ RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: Timeout after %ums\n", microphone->device_id, timeout); return false; - case WAIT_FAILED: - { - HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); - char error_message[256]; - wasapi_log_hr(hr, error_message, sizeof(error_message)); - RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: %s\n", microphone->device_id, error_message); - return false; - } default: RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: %s\n", microphone->device_id, wasapi_error(GetLastError())); return false; @@ -670,8 +662,6 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device return microphone; error: - wasapi_log_hr(hr, error_message, sizeof(error_message)); - RARCH_ERR("[WASAPI]: Failed to open microphone: %s\n", error_message); IFACE_RELEASE(microphone->capture); IFACE_RELEASE(microphone->client); IFACE_RELEASE(microphone->device); From c0cb9f2d7e40a598243dc1ac25add815ca7093fd Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 13:02:09 -0500 Subject: [PATCH 183/309] Add some values to hresult_name --- audio/common/wasapi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 208a422efda5..8d617195bbe7 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -76,6 +76,18 @@ const char *hresult_name(HRESULT hr) return "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; case AUDCLNT_E_SERVICE_NOT_RUNNING: return "AUDCLNT_E_SERVICE_NOT_RUNNING"; + case AUDCLNT_E_INVALID_SIZE: + return "AUDCLNT_E_INVALID_SIZE"; + case AUDCLNT_E_OUT_OF_ORDER: + return "AUDCLNT_E_OUT_OF_ORDER"; + case AUDCLNT_S_BUFFER_EMPTY: + return "AUDCLNT_S_BUFFER_EMPTY"; + case S_OK: + return "S_OK"; + case AUDCLNT_E_BUFFER_ERROR: + return "AUDCLNT_E_BUFFER_ERROR"; + case AUDCLNT_E_BUFFER_OPERATION_PENDING: + return "AUDCLNT_E_BUFFER_OPERATION_PENDING"; default: return ""; } From 01ab254bae9b500bdef9e5068a4eb9474610a2d3 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 13:04:08 -0500 Subject: [PATCH 184/309] Polish up wasapi_select_device_format - Test for formats manually when Windows can't - Add some debug logging - Check for channels --- audio/common/wasapi.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 8d617195bbe7..2b6ec2da214e 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -171,6 +171,8 @@ static unsigned wasapi_pref_rate(unsigned i) return r[i]; } +static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, + bool float_fmt, unsigned rate, unsigned channels); /** * Selects a device format * @param[in,out] format The place where the chosen format will be written, @@ -179,9 +181,10 @@ static unsigned wasapi_pref_rate(unsigned i) * @param mode todo * @return true if successful, false if there was an error or a suitable format wasn't found */ -static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClient *client, AUDCLNT_SHAREMODE mode) +static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClient *client, AUDCLNT_SHAREMODE mode, unsigned channels) { static const unsigned preferred_rates[] = { 48000, 44100, 96000, 192000, 32000 }; + const bool preferred_formats[] = {format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, format->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE}; WAVEFORMATEXTENSIBLE *suggested_format = NULL; bool result = false; HRESULT hr = _IAudioClient_IsFormatSupported(client, mode, @@ -191,26 +194,49 @@ static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClie { case S_OK: /* The requested format is okay without any changes */ + RARCH_DBG("[WASAPI]: Desired format (%s, %u-channel, %uHz) can be used as-is.\n", + wave_format_name(format), format->Format.nChannels, format->Format.nSamplesPerSec); result = true; break; case S_FALSE: /* The requested format is unsupported, but Windows has suggested a similar one. */ // TODO: Check that the suggested format meets RetroArch's requirements + RARCH_DBG("[WASAPI]: Windows suggests a format of (%s, %u-channel, %uHz).\n", + wave_format_name(suggested_format), suggested_format->Format.nChannels, suggested_format->Format.nSamplesPerSec); *format = *suggested_format; result = true; break; case AUDCLNT_E_UNSUPPORTED_FORMAT: - /* The requested format is unsupported, and Windows was unable to suggest another. - * Usually happens with exclusive mode. */ - // TODO: Try to pick a format manually + { /* The requested format is unsupported, and Windows was unable to suggest another. + * Usually happens with exclusive mode. */ + int i, j; + WAVEFORMATEXTENSIBLE possible_format; + HRESULT format_check_hr; + RARCH_WARN("[WASAPI]: Requested format not supported, and Windows could not suggest one. RetroArch will do so.\n"); + for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) + { + for (j = 0; j < ARRAY_SIZE(preferred_rates); ++j) + { + wasapi_set_format(&possible_format, preferred_formats[i], preferred_rates[j], channels); + format_check_hr = _IAudioClient_IsFormatSupported(client, mode, (const WAVEFORMATEX *) &possible_format, NULL); + if (SUCCEEDED(format_check_hr)) + { + *format = possible_format; + result = true; + RARCH_DBG("[WASAPI]: RetroArch suggests a format of (%s, %u-channel, %uHz).\n", + wave_format_name(format), format->Format.nChannels, format->Format.nSamplesPerSec); + goto done; + } + } + } + } default: /* Something else went wrong. */ - RARCH_ERR("[WASAPI]: Failed to select client format (%s): %s", - hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + RARCH_ERR("[WASAPI]: Failed to select client format: %s\n", hresult_name(hr)); result = false; break; } - +done: if (suggested_format) { /* IAudioClient::IsFormatSupported allocates a format object */ CoTaskMemFree(suggested_format); From 3cd1057fe919b3ebea557f562acd5828b2584fab Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 13:04:46 -0500 Subject: [PATCH 185/309] Compute the fields of WAVEFORMATEXTENSIBLE correctly - As per the doc's stated requirements --- audio/common/wasapi.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 2b6ec2da214e..028c8e85bf94 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -248,26 +248,27 @@ static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClie static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, bool float_fmt, unsigned rate, unsigned channels) { - wf->Format.nChannels = channels; - wf->Format.nSamplesPerSec = rate; + WORD wBitsPerSample = float_fmt ? 32 : 16; + WORD nBlockAlign = (channels * wBitsPerSample) / 8; + DWORD nAvgBytesPerSec = rate * nBlockAlign; + + wf->Format.nChannels = channels; + wf->Format.nSamplesPerSec = rate; + wf->Format.nAvgBytesPerSec = nAvgBytesPerSec; + wf->Format.nBlockAlign = nBlockAlign; + wf->Format.wBitsPerSample = wBitsPerSample; if (float_fmt) { wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf->Format.nAvgBytesPerSec = rate * 8; - wf->Format.nBlockAlign = 8; - wf->Format.wBitsPerSample = 32; wf->Format.cbSize = sizeof(WORD) + sizeof(DWORD) + sizeof(GUID); - wf->Samples.wValidBitsPerSample = 32; + wf->Samples.wValidBitsPerSample = wBitsPerSample; wf->dwChannelMask = channels == 1 ? KSAUDIO_SPEAKER_MONO : KSAUDIO_SPEAKER_STEREO; wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else { wf->Format.wFormatTag = WAVE_FORMAT_PCM; - wf->Format.nAvgBytesPerSec = rate * 4; - wf->Format.nBlockAlign = 4; - wf->Format.wBitsPerSample = 16; wf->Format.cbSize = 0; } } From a0bc2d2077a46f6cb881f038e5936fbaf26f83e2 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 13:05:43 -0500 Subject: [PATCH 186/309] Simplify logic for WASAPI client creation --- audio/common/wasapi.c | 140 ++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 72 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 028c8e85bf94..2b93279b2283 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -308,81 +308,77 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, if (buffer_duration < minimum_period) buffer_duration = minimum_period; - /* once for float, once for pcm (requested first) */ - for (i = 0; i < 2; ++i) + wasapi_set_format(&wf, float_fmt_res, rate_res, channels); + RARCH_LOG("[WASAPI]: Requesting format: %u-bit %u-channel client with %s samples at %uHz\n", + wf.Format.wBitsPerSample, + wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); + + if (wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_EXCLUSIVE, channels)) + { + RARCH_LOG("[WASAPI]: Using format: %u-bit %u-channel client with %s samples at %uHz\n", + wf.Format.wBitsPerSample, + wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); + } + else { - rate_res = *rate; - if (i == 1) - float_fmt_res = !float_fmt_res; + RARCH_ERR("[WASAPI]: Failed to select a suitable device format\n"); + goto error; + } - /* for requested rate (first) and all preferred rates */ - for (j = 0; rate_res; ++j) + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); + if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) + { + RARCH_WARN("[WASAPI] Unaligned buffer size: %s", wasapi_error(HRESULT_CODE(hr))); + hr = _IAudioClient_GetBufferSize(client, &buffer_length); + if (FAILED(hr)) { - wasapi_set_format(&wf, float_fmt_res, rate_res, channels); - hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); - if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) - { - RARCH_WARN("[WASAPI] Unaligned buffer size: %s", wasapi_error(HRESULT_CODE(hr))); - hr = _IAudioClient_GetBufferSize(client, &buffer_length); - if (FAILED(hr)) - { - RARCH_ERR("[WASAPI] Failed to get buffer size of client (%s): %s", - hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); - goto error; - } - - IFACE_RELEASE(client); - hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - { - RARCH_ERR("[WASAPI] IMMDevice::Activate failed (%s): %s", - hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); - return NULL; - } - - buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; - hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); - } - if (hr == AUDCLNT_E_ALREADY_INITIALIZED) - { - IFACE_RELEASE(client); - hr = _IMMDevice_Activate(device, - IID_IAudioClient, - CLSCTX_ALL, NULL, (void**)&client); - if (FAILED(hr)) - { - RARCH_ERR("[WASAPI] IMMDevice::Activate failed (%s): %s", - hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); - return NULL; - } + RARCH_ERR("[WASAPI] Failed to get buffer size of client (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + goto error; + } - hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); - } - if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) - { - if (hr == AUDCLNT_E_DEVICE_IN_USE) - goto error; + IFACE_RELEASE(client); + hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + { + RARCH_ERR("[WASAPI] IMMDevice::Activate failed (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + return NULL; + } - if (hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) - goto error; + buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); + } + if (hr == AUDCLNT_E_ALREADY_INITIALIZED) + { + IFACE_RELEASE(client); + hr = _IMMDevice_Activate(device, + IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + if (FAILED(hr)) + { + RARCH_ERR("[WASAPI] IMMDevice::Activate failed (%s): %s", + hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + return NULL; + } - i = 2; /* break from outer loop too */ - break; - } + hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); + } + if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT) + { + if (hr == AUDCLNT_E_DEVICE_IN_USE) + goto error; - RARCH_WARN("[WASAPI]: Unsupported format.\n"); - rate_res = wasapi_pref_rate(j); - if (rate_res == *rate) /* requested rate is allready tested */ - rate_res = wasapi_pref_rate(++j); /* skip it */ - } + if (hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) + goto error; } if (FAILED(hr)) @@ -392,11 +388,11 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, goto error; } - *float_fmt = float_fmt_res; - *rate = rate_res; + *float_fmt = IsEqualGUID(&wf.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT); + *rate = wf.Format.nSamplesPerSec; RARCH_LOG("[WASAPI]: Initialized exclusive %s client at %uHz, latency %ums\n", - float_fmt_res ? "float" : "pcm", rate_res, latency); + *float_fmt ? "float" : "pcm", *rate, latency); return client; @@ -429,7 +425,7 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, RARCH_LOG("[WASAPI]: Requesting %u-channel client with %s samples at %uHz\n", wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); - if (wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_SHARED)) + if (wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_SHARED, channels)) { RARCH_LOG("[WASAPI]: Got %u-channel client with %s samples at %uHz\n", wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); From 4301380c9736f183a9e41d256445765c0cc24783 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 13:06:08 -0500 Subject: [PATCH 187/309] Fix a potential hang in wasapi_microphone_read_shared_buffered --- microphone/drivers/wasapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 53b3b7bf2f3a..c1daf49c6cb1 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -246,7 +246,7 @@ static ssize_t wasapi_microphone_read_shared_buffered( return -1; } - read_avail = FIFO_WRITE_AVAIL(microphone->buffer); + read_avail = FIFO_READ_AVAIL(microphone->buffer); bytes_to_read = MIN(buffer_size, read_avail); if (bytes_to_read) fifo_read(microphone->buffer, buffer, bytes_to_read); From e5ed6e6891e56182a2c66f5fc8f9bac8c3064f6f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 13:06:24 -0500 Subject: [PATCH 188/309] Stop the microphone if the driver is stopped --- microphone/drivers/wasapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index c1daf49c6cb1..94a2293659c0 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -495,7 +495,7 @@ static bool wasapi_microphone_stop(void *driver_context) if (wasapi->microphone && wasapi_microphone_mic_alive(wasapi, wasapi->microphone)) { /* If we have a microphone that we need to pause... */ - + bool result = wasapi_microphone_stop_mic(wasapi, wasapi->microphone); } wasapi->running = false; From b4b39e43c235ee0b23713ea9473683ad303b930c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 13:06:37 -0500 Subject: [PATCH 189/309] Don't name the microphone event --- microphone/drivers/wasapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 94a2293659c0..74916607185f 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -615,7 +615,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device RARCH_LOG("[WASAPI]: Intermediate buffer is off. \n"); } - microphone->read_event = CreateEventA(NULL, FALSE, FALSE, "Microphone Read"); + microphone->read_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!microphone->read_event) { RARCH_ERR("[WASAPI]: Failed to allocate capture device's event handle\n"); From eb77f769b8ffe059723a5536c0d8ef62b3b008d8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 15:10:56 -0500 Subject: [PATCH 190/309] Ensure that wasapi_init_client reports the correct format and rate --- audio/common/wasapi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 2b93279b2283..10b071620a2d 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -279,8 +279,6 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, WAVEFORMATEXTENSIBLE wf; int i, j; IAudioClient *client = NULL; - bool float_fmt_res = *float_fmt; - unsigned rate_res = *rate; REFERENCE_TIME minimum_period = 0; REFERENCE_TIME buffer_duration = 0; UINT32 buffer_length = 0; @@ -308,7 +306,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, if (buffer_duration < minimum_period) buffer_duration = minimum_period; - wasapi_set_format(&wf, float_fmt_res, rate_res, channels); + wasapi_set_format(&wf, *float_fmt, *rate, channels); RARCH_LOG("[WASAPI]: Requesting format: %u-bit %u-channel client with %s samples at %uHz\n", wf.Format.wBitsPerSample, wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); @@ -350,7 +348,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, return NULL; } - buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; + buffer_duration = 10000.0 * 1000.0 / (*rate) * buffer_length + 0.5; hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); @@ -388,7 +386,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, goto error; } - *float_fmt = IsEqualGUID(&wf.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT); + *float_fmt = wf.Format.wFormatTag != WAVE_FORMAT_PCM; *rate = wf.Format.nSamplesPerSec; RARCH_LOG("[WASAPI]: Initialized exclusive %s client at %uHz, latency %ums\n", @@ -461,7 +459,7 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, goto error; } - *float_fmt = IsEqualGUID(&wf.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT); + *float_fmt = wf.Format.wFormatTag != WAVE_FORMAT_PCM; *rate = wf.Format.nSamplesPerSec; RARCH_LOG("[WASAPI]: Initialized shared %s client at %uHz, latency %ums\n", From 77f42fa740cf47815be3219f7262192f848aac26 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 31 Jan 2023 15:11:39 -0500 Subject: [PATCH 191/309] Implement exclusive microphone read access for WASAPI --- microphone/drivers/wasapi.c | 58 ++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 74916607185f..e34d1ff10a78 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -136,44 +136,51 @@ static bool wasapi_microphone_fetch_buffer( /** * Flushes microphone's most recent input to the provided context's FIFO queue. + * WASAPI requires that fetched input be consumed in its entirety, + * so this may return 0 if the queue doesn't have room. * @param microphone Pointer to the microphone context. - * @param buffer The buffer in which incoming samples will be stored. - * @param buffer_size The length of \c buffer, in bytes. - * @return True if the read was successful + * @return The number of bytes written to the queue, or -1 if there was an error. + * May be 0 */ -static bool wasapi_microphone_fetch_fifo( - wasapi_microphone_handle_t *microphone, - size_t buffer_size) +static ssize_t wasapi_microphone_fetch_fifo(wasapi_microphone_handle_t *microphone) { BYTE *mic_input = NULL; - UINT32 frame_count = 0; - UINT32 byte_count = 0; + UINT32 frames_read = 0; + UINT32 bytes_read = 0; + ssize_t frames_enqueued = 0; DWORD buffer_status_flags = 0; HRESULT hr; - hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frame_count, &buffer_status_flags, NULL, NULL); + hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frames_read, &buffer_status_flags, NULL, NULL); if (FAILED(hr)) { RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", microphone->device_id, hresult_name(hr)); - return false; + return -1; } - byte_count = frame_count * microphone->frame_size; - // TODO: As written, this can drop frames - // TODO: Determine if the queue can fit the available frames. If not, don't read them + bytes_read = frames_read * microphone->frame_size; + + if (FIFO_WRITE_AVAIL(microphone->buffer) >= bytes_read) + { /* If the queue has room for the packets we just got... */ + fifo_write(microphone->buffer, mic_input, bytes_read); + /* ...then enqueue the bytes directly from the mic's buffer */ + frames_enqueued = frames_read; + } + /* If there's insufficient room in the queue, then we can't read the packet. + * In that case, we leave the packet for next time. */ - fifo_write(microphone->buffer, mic_input, MIN(buffer_size, byte_count)); - hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, frame_count); + hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, frames_enqueued); if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer: %s\n", + RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer after having read %u frames: %s\n", microphone->device_id, + frames_enqueued, hresult_name(hr)); - return false; + return -1; } - return true; + return frames_enqueued * (ssize_t)microphone->frame_size; } /** @@ -242,7 +249,7 @@ static ssize_t wasapi_microphone_read_shared_buffered( bytes_to_read = MIN(write_avail, read_avail); if (bytes_to_read > 0) - if (!wasapi_microphone_fetch_fifo(microphone, bytes_to_read)) + if (wasapi_microphone_fetch_fifo(microphone) < 0) return -1; } @@ -334,7 +341,7 @@ static ssize_t wasapi_microphone_read_shared_nonblock( read_avail = microphone->engine_buffer_size - available_frames * microphone->frame_size; bytes_to_read = MIN(write_avail, read_avail); if (bytes_to_read) - if (!wasapi_microphone_fetch_fifo(microphone, bytes_to_read)) + if (wasapi_microphone_fetch_fifo(microphone) < 0) return -1; } @@ -386,22 +393,21 @@ static ssize_t wasapi_microphone_read_exclusive( size_t buffer_size, DWORD timeout) { - ssize_t bytes_read = 0; - size_t bytes_available = FIFO_READ_AVAIL(microphone->buffer); + ssize_t bytes_read = 0; /* Number of bytes sent to the core */ + ssize_t bytes_available = FIFO_READ_AVAIL(microphone->buffer); if (!bytes_available) - { /* If the incoming sample queue is empty... */ + { /* If we don't have any queued samples to give to the core... */ if (!wasapi_microphone_wait_for_capture_event(microphone, timeout)) { /* If we couldn't wait for the microphone to signal a capture event... */ return -1; } - if (!wasapi_microphone_fetch_fifo(microphone, microphone->engine_buffer_size)) + bytes_available = wasapi_microphone_fetch_fifo(microphone); + if (bytes_available < 0) { /* If we couldn't fetch samples from the microphone... */ return -1; } - - bytes_available = microphone->engine_buffer_size; } /* Now that we have samples available, let's give them to the core */ From a5ac21790587c1419ece21c214f597116ac48f7b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 16:37:42 -0500 Subject: [PATCH 192/309] Add _IAudioCaptureClient_GetNextPacketSize macro --- audio/common/mmdevice_common_inline.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audio/common/mmdevice_common_inline.h b/audio/common/mmdevice_common_inline.h index 5875317584d6..80a7405ff124 100644 --- a/audio/common/mmdevice_common_inline.h +++ b/audio/common/mmdevice_common_inline.h @@ -75,6 +75,8 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 ( (This) -> GetBuffer(ppData,pNumFramesToRead,pdwFlags,pu64DevicePosition,pu64QPCPosition) ) #define _IAudioCaptureClient_ReleaseBuffer(This,NumFramesRead) \ ( (This) -> ReleaseBuffer(NumFramesRead) ) +#define _IAudioCaptureClient_GetNextPacketSize(This,pNumFramesInNextPacket) \ + ( (This) -> GetNextPacketSize(pNumFramesInNextPacket) ) #else #define _IMMDeviceCollection_Item(This,nDevice,ppdevice) (This)->lpVtbl->Item(This,nDevice,ppdevice) #define _IAudioClient_Start(This) ( (This)->lpVtbl -> Start(This) ) @@ -105,6 +107,8 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 ( (This)->lpVtbl -> GetBuffer(This,ppData,pNumFramesToRead,pdwFlags,pu64DevicePosition,pu64QPCPosition) ) #define _IAudioCaptureClient_ReleaseBuffer(This,NumFramesRead) \ ( (This)->lpVtbl -> ReleaseBuffer(This,NumFramesRead) ) +#define _IAudioCaptureClient_GetNextPacketSize(This,pNumFramesInNextPacket) \ + ( (This)-> lpVtbl -> GetNextPacketSize(This,pNumFramesInNextPacket) ) #endif #ifdef __cplusplus From 0d2f5e8c48b1ed68eb09ee8e836e9d9103fa529c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 16:38:21 -0500 Subject: [PATCH 193/309] Organize cases in hresult_name --- audio/common/wasapi.c | 54 +++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 10b071620a2d..588fcc9e22f9 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -42,20 +42,29 @@ const char *hresult_name(HRESULT hr) { switch (hr) { + /* Standard error codes */ + case E_INVALIDARG: + return "E_INVALIDARG"; case E_NOINTERFACE: return "E_NOINTERFACE"; - case E_POINTER: - return "E_POINTER"; case E_OUTOFMEMORY: return "E_OUTOFMEMORY"; - case E_INVALIDARG: - return "E_INVALIDARG"; - case AUDCLNT_E_DEVICE_INVALIDATED: - return "AUDCLNT_E_DEVICE_INVALIDATED"; + case E_POINTER: + return "E_POINTER"; + /* Standard success codes */ + case S_FALSE: + return "S_FALSE"; + case S_OK: + return "S_OK"; + /* AUDCLNT error codes */ case AUDCLNT_E_ALREADY_INITIALIZED: return "AUDCLNT_E_ALREADY_INITIALIZED"; - case AUDCLNT_E_WRONG_ENDPOINT_TYPE: - return "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; + case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: + return "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; + case AUDCLNT_E_BUFFER_ERROR: + return "AUDCLNT_E_BUFFER_ERROR"; + case AUDCLNT_E_BUFFER_OPERATION_PENDING: + return "AUDCLNT_E_BUFFER_OPERATION_PENDING"; case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; case AUDCLNT_E_BUFFER_SIZE_ERROR: @@ -64,30 +73,31 @@ const char *hresult_name(HRESULT hr) return "AUDCLNT_E_CPUUSAGE_EXCEEDED"; case AUDCLNT_E_DEVICE_IN_USE: return "AUDCLNT_E_DEVICE_IN_USE"; + case AUDCLNT_E_DEVICE_INVALIDATED: + return "AUDCLNT_E_DEVICE_INVALIDATED"; case AUDCLNT_E_ENDPOINT_CREATE_FAILED: return "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; - case AUDCLNT_E_INVALID_DEVICE_PERIOD: - return "AUDCLNT_E_INVALID_DEVICE_PERIOD"; - case AUDCLNT_E_UNSUPPORTED_FORMAT: - return "AUDCLNT_E_UNSUPPORTED_FORMAT"; case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; - case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: - return "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; - case AUDCLNT_E_SERVICE_NOT_RUNNING: - return "AUDCLNT_E_SERVICE_NOT_RUNNING"; + case AUDCLNT_E_INVALID_DEVICE_PERIOD: + return "AUDCLNT_E_INVALID_DEVICE_PERIOD"; case AUDCLNT_E_INVALID_SIZE: return "AUDCLNT_E_INVALID_SIZE"; + case AUDCLNT_E_NOT_INITIALIZED: + return "AUDCLNT_E_NOT_INITIALIZED"; case AUDCLNT_E_OUT_OF_ORDER: return "AUDCLNT_E_OUT_OF_ORDER"; + case AUDCLNT_E_SERVICE_NOT_RUNNING: + return "AUDCLNT_E_SERVICE_NOT_RUNNING"; + case AUDCLNT_E_UNSUPPORTED_FORMAT: + return "AUDCLNT_E_UNSUPPORTED_FORMAT"; + case AUDCLNT_E_WRONG_ENDPOINT_TYPE: + return "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; + /* AUDCLNT success codes */ case AUDCLNT_S_BUFFER_EMPTY: return "AUDCLNT_S_BUFFER_EMPTY"; - case S_OK: - return "S_OK"; - case AUDCLNT_E_BUFFER_ERROR: - return "AUDCLNT_E_BUFFER_ERROR"; - case AUDCLNT_E_BUFFER_OPERATION_PENDING: - return "AUDCLNT_E_BUFFER_OPERATION_PENDING"; + /* Something else; probably from an API that we started using + * after mic support was implemented */ default: return ""; } From bb6c132c654ed054e2458874cf054b80e975eff1 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 16:38:42 -0500 Subject: [PATCH 194/309] Clear some extra fields if wasapi_set_format is setting a Pcm format --- audio/common/wasapi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 588fcc9e22f9..332b0513638b 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -280,6 +280,9 @@ static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, { wf->Format.wFormatTag = WAVE_FORMAT_PCM; wf->Format.cbSize = 0; + wf->Samples.wValidBitsPerSample = 0; + wf->dwChannelMask = 0; + memset(&wf->SubFormat, 0, sizeof(wf->SubFormat)); } } From 785de1bd24b01c4f091a65d20d3c0c4ad3f0255c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 16:39:23 -0500 Subject: [PATCH 195/309] Adjust some logs --- audio/common/wasapi.c | 51 +++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 332b0513638b..92d4055067b0 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -427,18 +427,15 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, if (FAILED(hr)) { /* If we couldn't create the IAudioClient... */ - RARCH_ERR("[WASAPI]: Failed to create %s IAudioClient with %s: %s", hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + RARCH_ERR("[WASAPI]: Failed to create %s IAudioClient: %s\n", hresult_name(hr)); return NULL; } wasapi_set_format(&wf, float_fmt_res, rate_res, channels); - RARCH_LOG("[WASAPI]: Requesting %u-channel client with %s samples at %uHz\n", - wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); - if (wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_SHARED, channels)) { - RARCH_LOG("[WASAPI]: Got %u-channel client with %s samples at %uHz\n", + RARCH_LOG("[WASAPI]: Requesting %u-channel shared-mode client with %s samples at %uHz\n", wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec); } else @@ -467,16 +464,15 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: IAudioClient::Initialize failed with %s: %s", hresult_name(hr), - wasapi_error(HRESULT_CODE(hr))); + RARCH_ERR("[WASAPI]: IAudioClient::Initialize failed: %s\n", hresult_name(hr)); goto error; } *float_fmt = wf.Format.wFormatTag != WAVE_FORMAT_PCM; *rate = wf.Format.nSamplesPerSec; - RARCH_LOG("[WASAPI]: Initialized shared %s client at %uHz, latency %ums\n", - wave_format_name(&wf), *rate, latency); + RARCH_LOG("[WASAPI]: Initialized shared %s client at %uHz\n", + wave_format_name(&wf), *rate); return client; @@ -664,28 +660,55 @@ IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, /* next calls are allowed to fail (we losing info only) */ if (*exclusive) + { hr = _IAudioClient_GetDevicePeriod(client, NULL, &device_period); + if (SUCCEEDED(hr)) + { + RARCH_LOG("[WASAPI]: Minimum exclusive-mode device period is %uns (= %.1fms)\n", + device_period * 100, (double)device_period * 100 / 1e6); + } + /* device_period is in 100ns units */ + } else + { hr = _IAudioClient_GetDevicePeriod(client, &device_period, NULL); + if (SUCCEEDED(hr)) + { + RARCH_LOG("[WASAPI]: Default shared-mode device period is %uns (= %.1fms)\n", + device_period * 100, (double)device_period * 100 / 1e6); + } + } if (FAILED(hr)) { - RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed with error 0x%.8X.\n", hr); + RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed: %s\n", hresult_name(hr)); } if (!*exclusive) { hr = _IAudioClient_GetStreamLatency(client, &stream_latency); - if (FAILED(hr)) + if (SUCCEEDED(hr)) + { + RARCH_LOG("[WASAPI]: Shared stream latency is %uns (= %.1fms)\n", + stream_latency * 100, (double)stream_latency * 100 / 1e6); + } + else { - RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed with error 0x%.8X.\n", hr); + RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed: %s\n", hresult_name(hr)); } } hr = _IAudioClient_GetBufferSize(client, &buffer_length); - if (FAILED(hr)) + if (SUCCEEDED(hr)) + { + size_t num_samples = buffer_length * channels; + size_t num_bytes = num_samples * (*float_fmt ? sizeof(float) : sizeof(int16_t)); + RARCH_LOG("[WASAPI]: Endpoint buffer size is %u audio frames (= %u samples, = %u bytes)\n", + buffer_length, num_samples, num_bytes); + } + else { - RARCH_WARN("[WASAPI]: IAudioClient::GetBufferSize failed with error 0x%.8X.\n", hr); + RARCH_WARN("[WASAPI]: IAudioClient::GetBufferSize failed: %s.\n", hresult_name(hr)); } if (*exclusive) From 1e0d0ed70c5e9273ec6f836d275679d034624c62 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 16:39:36 -0500 Subject: [PATCH 196/309] Adjust some logs --- audio/common/wasapi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 92d4055067b0..cc3eff958926 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -301,16 +301,14 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: IMMDevice::Activate failed (%s): %s", - hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s\n", hresult_name(hr)); return NULL; } hr = _IAudioClient_GetDevicePeriod(client, NULL, &minimum_period); if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: Failed to get device period of exclusive-mode client (%s): %s", - hresult_name(hr), wasapi_error(HRESULT_CODE(hr))); + RARCH_ERR("[WASAPI]: Failed to get device period of exclusive-mode client: %s\n", hresult_name(hr)); goto error; } From c19816058f25a4a4f7c36b8163e79a1e69c4d898 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 19:21:10 -0500 Subject: [PATCH 197/309] Remove unneeded local vars --- audio/common/wasapi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index cc3eff958926..cb8002a861c9 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -415,7 +415,6 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, bool *float_fmt, unsigned *rate, unsigned latency, unsigned channels) { WAVEFORMATEXTENSIBLE wf; - int i, j; IAudioClient *client = NULL; bool float_fmt_res = *float_fmt; unsigned rate_res = *rate; From 53a3a73160c55f99eaeedba533398cbe013fb34d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 19:21:21 -0500 Subject: [PATCH 198/309] Add a log --- audio/common/wasapi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index cb8002a861c9..c0a830261901 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -452,7 +452,10 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client); if (FAILED(hr)) + { + RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s\n", hresult_name(hr)); return NULL; + } hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, From fbc29b5ad53f4ed3867cf99e48f7c6831a92acdc Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 19:21:32 -0500 Subject: [PATCH 199/309] Update wasapi.c --- audio/common/wasapi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index c0a830261901..40a722fad303 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -16,7 +16,6 @@ #include "wasapi.h" #include -#include #include "microphone/microphone_driver.h" #include "queues/fifo_queue.h" #include "configuration.h" @@ -28,10 +27,9 @@ void wasapi_log_hr(HRESULT hr, char* buffer, size_t length) { FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_FROM_HMODULE, + FORMAT_MESSAGE_FROM_SYSTEM, NULL, - hr, + GetLastError(), MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), buffer, length - 1, From 4983348882eaf1ff78980e0302dff4f4d5255e1e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 19:22:18 -0500 Subject: [PATCH 200/309] Update wasapi.c --- microphone/drivers/wasapi.c | 174 ++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 79 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index e34d1ff10a78..9788d781b94a 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -137,50 +137,81 @@ static bool wasapi_microphone_fetch_buffer( /** * Flushes microphone's most recent input to the provided context's FIFO queue. * WASAPI requires that fetched input be consumed in its entirety, - * so this may return 0 if the queue doesn't have room. + * so the returned value may be less than the queue's size + * if the next packet won't fit in it. * @param microphone Pointer to the microphone context. - * @return The number of bytes written to the queue, or -1 if there was an error. - * May be 0 + * @return The number of bytes in the queue, or -1 if there was an error. */ static ssize_t wasapi_microphone_fetch_fifo(wasapi_microphone_handle_t *microphone) { - BYTE *mic_input = NULL; - UINT32 frames_read = 0; - UINT32 bytes_read = 0; - ssize_t frames_enqueued = 0; - DWORD buffer_status_flags = 0; - HRESULT hr; + UINT32 next_packet_size = 0; + volatile int loops = 0; + /* Shared-mode capture streams split their input buffer into multiple packets, + * while exclusive-mode capture streams just use the one. + * + * The following loop will run at least once; + * for exclusive-mode streams, that's all that we'll need. + */ - hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frames_read, &buffer_status_flags, NULL, NULL); - if (FAILED(hr)) + do { - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", - microphone->device_id, - hresult_name(hr)); - return -1; - } - bytes_read = frames_read * microphone->frame_size; + BYTE *mic_input = NULL; + UINT32 frames_read = 0; + UINT32 bytes_read = 0; + DWORD buffer_status_flags = 0; + HRESULT hr; + ++loops; - if (FIFO_WRITE_AVAIL(microphone->buffer) >= bytes_read) - { /* If the queue has room for the packets we just got... */ - fifo_write(microphone->buffer, mic_input, bytes_read); - /* ...then enqueue the bytes directly from the mic's buffer */ - frames_enqueued = frames_read; - } - /* If there's insufficient room in the queue, then we can't read the packet. - * In that case, we leave the packet for next time. */ + hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frames_read, &buffer_status_flags, NULL, NULL); + if (FAILED(hr)) + { + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", + microphone->device_id, + hresult_name(hr)); + return -1; + } + bytes_read = frames_read * microphone->frame_size; - hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, frames_enqueued); - if (FAILED(hr)) - { - RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer after having read %u frames: %s\n", - microphone->device_id, - frames_enqueued, - hresult_name(hr)); - return -1; + if (FIFO_WRITE_AVAIL(microphone->buffer) >= bytes_read && bytes_read > 0) + { /* If the queue has room for the packets we just got... */ + fifo_write(microphone->buffer, mic_input, bytes_read); + /* ...then enqueue the bytes directly from the mic's buffer */ + } + else + { /* Not enough space for new frames, so we can't consume this packet right now */ + frames_read = 0; + } + /* If there's insufficient room in the queue, then we can't read the packet. + * In that case, we leave the packet for next time. */ + + hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, frames_read); + if (FAILED(hr)) + { + RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer after consuming %u frames: %s\n", + microphone->device_id, + frames_read, + hresult_name(hr)); + return -1; + } + + if (!microphone->exclusive && frames_read > 0) + { /* If this is a shared-mode stream and we didn't run out of room in the sample queue... */ + hr = _IAudioCaptureClient_GetNextPacketSize(microphone->capture, &next_packet_size); + if (FAILED(hr)) + { /* Get the number of frames that the mic has for us. */ + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s next packet size: %s\n", + microphone->device_id, hresult_name(hr)); + return -1; + } + } + else + { /* Exclusive-mode streams only deliver one packet at a time, though it's bigger. */ + next_packet_size = 0; + } } + while (next_packet_size != 0); - return frames_enqueued * (ssize_t)microphone->frame_size; + return FIFO_READ_AVAIL(microphone->buffer); } /** @@ -223,38 +254,29 @@ static ssize_t wasapi_microphone_read_shared_buffered( void *buffer, size_t buffer_size) { - ssize_t bytes_to_read = -1; + ssize_t bytes_to_read = 0; + ssize_t bytes_available = FIFO_READ_AVAIL(microphone->buffer); UINT32 available_frames = 0; - size_t read_avail = FIFO_READ_AVAIL(microphone->buffer); - - if (!read_avail) - { /* If the incoming sample buffer is empty... */ - size_t write_avail = 0; - HRESULT hr; + if (!bytes_available) + { /* If we don't have any samples we could give to the core... */ if (!wasapi_microphone_wait_for_capture_event(microphone, INFINITE)) - return -1; - - hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); - if (FAILED(hr)) - { /* Get the number of frames that the mic has for us. */ - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); + { /* If we couldn't wait for the microphone to signal a capture event... */ return -1; } - write_avail = FIFO_WRITE_AVAIL(microphone->buffer); - read_avail = available_frames * microphone->frame_size; - bytes_to_read = MIN(write_avail, read_avail); + if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &available_frames))) + return -1; - if (bytes_to_read > 0) - if (wasapi_microphone_fetch_fifo(microphone) < 0) - return -1; + bytes_available = wasapi_microphone_fetch_fifo(microphone); + if (bytes_available < 0) + { /* If we couldn't fetch samples from the microphone... */ + return -1; + } } - read_avail = FIFO_READ_AVAIL(microphone->buffer); - bytes_to_read = MIN(buffer_size, read_avail); + bytes_available = FIFO_READ_AVAIL(microphone->buffer); + bytes_to_read = MIN(buffer_size, bytes_available); if (bytes_to_read) fifo_read(microphone->buffer, buffer, bytes_to_read); @@ -285,9 +307,8 @@ static ssize_t wasapi_microphone_read_shared_unbuffered( hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); if (FAILED(hr)) { /* Get the number of frames that the mic has for us. */ - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", + microphone->device_id, hresult_name(hr)); return -1; } @@ -528,7 +549,6 @@ static void wasapi_microphone_set_nonblock_state(void *driver_context, bool nonb static void *wasapi_microphone_open_mic(void *driver_context, const char *device, unsigned rate, unsigned latency, unsigned block_frames, unsigned *new_rate) { - char error_message[256] = {0}; settings_t *settings = config_get_ptr(); HRESULT hr; DWORD flags = 0; @@ -561,8 +581,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device hr = _IMMDevice_GetId(microphone->device, µphone->device_id); if (FAILED(hr)) { - wasapi_log_hr(hr, error_message, sizeof(error_message)); - RARCH_ERR("[WASAPI]: Failed to get ID of capture device: %s\n", error_message); + RARCH_ERR("[WASAPI]: Failed to get ID of capture device: %s\n", hresult_name(hr)); goto error; } @@ -577,9 +596,8 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device hr = _IAudioClient_GetBufferSize(microphone->client, &frame_count); if (FAILED(hr)) { - wasapi_log_hr(hr, error_message, sizeof(error_message)); RARCH_ERR("[WASAPI]: Failed to get buffer size of IAudioClient for capture device \"%ls\": %s\n", - microphone->device_id, error_message); + microphone->device_id, hresult_name(hr)); goto error; } @@ -595,8 +613,8 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device goto error; } - RARCH_LOG("[WASAPI]: Intermediate capture buffer length is %u frames (%.1fms).\n", - frame_count, (double)frame_count * 1000.0 / rate); + RARCH_LOG("[WASAPI]: Intermediate exclusive-mode capture buffer length is %u frames (%.1fms, %u bytes).\n", + frame_count, (double)frame_count * 1000.0 / rate, microphone->engine_buffer_size); } else if (sh_buffer_length) { @@ -613,8 +631,8 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device if (!microphone->buffer) goto error; - RARCH_LOG("[WASAPI]: Intermediate capture buffer length is %u frames (%.1fms).\n", - sh_buffer_length, (double)sh_buffer_length * 1000.0 / rate); + RARCH_LOG("[WASAPI]: Intermediate shared-mode capture buffer length is %u frames (%.1fms, %u bytes).\n", + sh_buffer_length, (double)sh_buffer_length * 1000.0 / rate, sh_buffer_length * microphone->frame_size); } else { @@ -631,8 +649,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device hr = _IAudioClient_SetEventHandle(microphone->client, microphone->read_event); if (FAILED(hr)) { - wasapi_log_hr(hr, error_message, sizeof(error_message)); - RARCH_ERR("[WASAPI]: Failed to set capture device's event handle: %s\n", error_message); + RARCH_ERR("[WASAPI]: Failed to set capture device's event handle: %s\n", hresult_name(hr)); goto error; } @@ -640,8 +657,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device IID_IAudioCaptureClient, (void**)µphone->capture); if (FAILED(hr)) { - wasapi_log_hr(hr, error_message, sizeof(error_message)); - RARCH_ERR("[WASAPI]: Failed to get capture device's IAudioCaptureClient service: %s\n", error_message); + RARCH_ERR("[WASAPI]: Failed to get capture device's IAudioCaptureClient service: %s\n", hresult_name(hr)); goto error; } @@ -649,22 +665,22 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &dest, &frame_count, &flags, NULL, NULL); if (FAILED(hr)) { - wasapi_log_hr(hr, error_message, sizeof(error_message)); - RARCH_ERR("[WASAPI]: Failed to get capture client buffer: %s\n", error_message); + RARCH_ERR("[WASAPI]: Failed to get capture client buffer: %s\n", hresult_name(hr)); goto error; } hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, 0); if (FAILED(hr)) { - wasapi_log_hr(hr, error_message, sizeof(error_message)); - RARCH_ERR("[WASAPI]: Failed to release capture client buffer: %s\n", error_message); - + RARCH_ERR("[WASAPI]: Failed to release capture client buffer: %s\n", hresult_name(hr)); goto error; } wasapi->microphone = microphone; - + if (new_rate) + { /* The rate was (possibly) modified when we initialized the client */ + *new_rate = rate; + } return microphone; error: From 5a076446ba8ec90e381cd14f81c57946589158b0 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 22:58:12 -0500 Subject: [PATCH 201/309] Fix shared-mode mic support in WASAPI producing broken input - Turns out it had nothing to do with shared mode --- microphone/drivers/wasapi.c | 14 +++++++++++++- microphone/microphone_driver.c | 5 +++++ microphone/microphone_driver.h | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 9788d781b94a..b5867ebb8147 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -817,6 +817,17 @@ static void wasapi_microphone_device_list_free(const void *driver_context, struc string_list_free(sl); } +static bool wasapi_microphone_use_float(const void *driver_context, const void *microphone_context) +{ + wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t *)microphone_context; + (void)driver_context; + + if (!driver_context || !microphone) + return false; + + return microphone->frame_size == sizeof(float); +} + microphone_driver_t microphone_wasapi = { wasapi_microphone_init, wasapi_microphone_free, @@ -832,5 +843,6 @@ microphone_driver_t microphone_wasapi = { wasapi_microphone_close_mic, wasapi_microphone_mic_alive, wasapi_microphone_start_mic, - wasapi_microphone_stop_mic + wasapi_microphone_stop_mic, + wasapi_microphone_use_float }; \ No newline at end of file diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 036d45442313..b5e8c0317623 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -285,6 +285,11 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) configuration_set_uint(settings, settings->uints.microphone_sample_rate, actual_sample_rate); } + if (mic_driver->mic_use_float && mic_driver->mic_use_float(mic_st->driver_context, microphone->microphone_context)) + { + microphone->flags |= MICROPHONE_FLAG_USE_FLOAT; + } + RARCH_LOG("[Microphone]: Initialized microphone\n"); return; error: diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index 4dfa81e9a0af..f8d5ca99c8f0 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -304,6 +304,8 @@ typedef struct microphone_driver bool (*start_mic)(void *driver_context, void *microphone_context); bool (*stop_mic)(void *driver_context, void *microphone_context); + + bool (*mic_use_float)(const void *driver_context, const void *microphone_context); } microphone_driver_t; typedef struct From 60cd9d17a3f1a0e49c8da4cde25f06bc7682c0ed Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 4 Feb 2023 22:59:32 -0500 Subject: [PATCH 202/309] Reuse a common function - Remove wasapi_microphone_read_shared_buffered - Rename wasapi_microphone_read_exclusive to wasapi_microphone_read_buffered --- microphone/drivers/wasapi.c | 55 ++++--------------------------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index b5867ebb8147..b12a6851d51b 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -239,50 +239,6 @@ static bool wasapi_microphone_wait_for_capture_event(wasapi_microphone_handle_t } } -/** - * Reads samples from a shared-mode microphone, - * fetching new ones directly from the device if necessary. - * - * @param wasapi - * @param microphone - * @param buffer - * @param buffer_size - * @return The number of bytes that were read - */ -static ssize_t wasapi_microphone_read_shared_buffered( - wasapi_microphone_handle_t *microphone, - void *buffer, - size_t buffer_size) -{ - ssize_t bytes_to_read = 0; - ssize_t bytes_available = FIFO_READ_AVAIL(microphone->buffer); - UINT32 available_frames = 0; - - if (!bytes_available) - { /* If we don't have any samples we could give to the core... */ - if (!wasapi_microphone_wait_for_capture_event(microphone, INFINITE)) - { /* If we couldn't wait for the microphone to signal a capture event... */ - return -1; - } - - if (FAILED(_IAudioClient_GetCurrentPadding(microphone->client, &available_frames))) - return -1; - - bytes_available = wasapi_microphone_fetch_fifo(microphone); - if (bytes_available < 0) - { /* If we couldn't fetch samples from the microphone... */ - return -1; - } - } - - bytes_available = FIFO_READ_AVAIL(microphone->buffer); - bytes_to_read = MIN(buffer_size, bytes_available); - if (bytes_to_read) - fifo_read(microphone->buffer, buffer, bytes_to_read); - - return bytes_to_read; -} - /** * Reads incoming samples from a shared-mode microphone, * without buffering any. @@ -408,7 +364,7 @@ static ssize_t wasapi_microphone_read_shared_nonblock( * \c INFINITE means that this function will wait indefinitely. * @return */ -static ssize_t wasapi_microphone_read_exclusive( +static ssize_t wasapi_microphone_read_buffered( wasapi_microphone_handle_t *microphone, void * buffer, size_t buffer_size, @@ -452,7 +408,7 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v if (wasapi->nonblock) { /* If microphones shouldn't block... */ if (microphone->exclusive) - return wasapi_microphone_read_exclusive(microphone, buffer, buffer_size, 0); + return wasapi_microphone_read_buffered(microphone, buffer, buffer_size, 0); return wasapi_microphone_read_shared_nonblock(microphone, buffer, buffer_size); } @@ -461,8 +417,8 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v ssize_t read; for (read = -1; bytes_read < buffer_size; bytes_read += read) { - read = wasapi_microphone_read_exclusive(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read, - INFINITE); + read = wasapi_microphone_read_buffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read, + INFINITE); if (read == -1) return -1; } @@ -474,7 +430,8 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v { for (read = -1; bytes_read < buffer_size; bytes_read += read) { - read = wasapi_microphone_read_shared_buffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read); + read = wasapi_microphone_read_buffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read, + INFINITE); if (read == -1) return -1; } From a4530072ea399a2138ba65b3fe1f4ac2f2eceba9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 5 Feb 2023 09:39:05 -0500 Subject: [PATCH 203/309] Remove some code I was using for test purposes --- microphone/drivers/wasapi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index b12a6851d51b..e2e104ae5a0d 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -145,7 +145,6 @@ static bool wasapi_microphone_fetch_buffer( static ssize_t wasapi_microphone_fetch_fifo(wasapi_microphone_handle_t *microphone) { UINT32 next_packet_size = 0; - volatile int loops = 0; /* Shared-mode capture streams split their input buffer into multiple packets, * while exclusive-mode capture streams just use the one. * @@ -160,7 +159,6 @@ static ssize_t wasapi_microphone_fetch_fifo(wasapi_microphone_handle_t *micropho UINT32 bytes_read = 0; DWORD buffer_status_flags = 0; HRESULT hr; - ++loops; hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frames_read, &buffer_status_flags, NULL, NULL); if (FAILED(hr)) From 70a8c891f2a57873a27756ec85b6f0fd087d6127 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 5 Feb 2023 09:39:23 -0500 Subject: [PATCH 204/309] Clarify some language --- microphone/drivers/wasapi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index e2e104ae5a0d..f480ff5933cd 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -574,7 +574,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device else if (sh_buffer_length) { if (sh_buffer_length < 0) - { + { /* If the user selected the "default" shared buffer length... */ hr = _IAudioClient_GetDevicePeriod(microphone->client, &dev_period, NULL); if (FAILED(hr)) goto error; @@ -591,7 +591,7 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device } else { - RARCH_LOG("[WASAPI]: Intermediate buffer is off. \n"); + RARCH_LOG("[WASAPI]: Intermediate capture buffer is off.\n"); } microphone->read_event = CreateEventA(NULL, FALSE, FALSE, NULL); From 311f80494996837e7baa8561eba1476833d3e3f8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 5 Feb 2023 09:39:42 -0500 Subject: [PATCH 205/309] Double the default shared-mode mic buffer length --- microphone/drivers/wasapi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index f480ff5933cd..d0b1e893ac1d 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -579,7 +579,9 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device if (FAILED(hr)) goto error; - sh_buffer_length = dev_period * rate / 10000000; + sh_buffer_length = (dev_period * rate / 10000000) * 2; + /* Default buffer seems to be too small, resulting in slowdown. + * Doubling it seems to work okay. Dunno why. */ } microphone->buffer = fifo_new(sh_buffer_length * microphone->frame_size); From 74e8ee6aefa4efc8ddf5f8c79cc24098286e5d0f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 5 Feb 2023 10:04:40 -0500 Subject: [PATCH 206/309] Split getting a device's name into a separate function, then use it --- audio/common/mmdevice_common.c | 57 ++++++++++++++++++------------ audio/common/mmdevice_common.h | 6 ++++ microphone/drivers/wasapi.c | 64 +++++++++++++++++----------------- 3 files changed, 73 insertions(+), 54 deletions(-) diff --git a/audio/common/mmdevice_common.c b/audio/common/mmdevice_common.c index 1dc8d155fc05..6801693b907e 100644 --- a/audio/common/mmdevice_common.c +++ b/audio/common/mmdevice_common.c @@ -21,20 +21,51 @@ #include "mmdevice_common.h" #include "mmdevice_common_inline.h" + +char* mmdevice_name(IMMDevice *device) +{ + HRESULT hr; + IPropertyStore *prop_store = NULL; + PROPVARIANT prop_var; + bool prop_var_init = false; + char* result = NULL; + + if (!device) + return NULL; + + hr = _IMMDevice_OpenPropertyStore(device, STGM_READ, &prop_store); + + if (FAILED(hr)) + return NULL; + + PropVariantInit(&prop_var); + prop_var_init = true; + hr = _IPropertyStore_GetValue(prop_store, PKEY_Device_FriendlyName, &prop_var); + if (FAILED(hr)) + goto done; + + result = utf16_to_utf8_string_alloc(prop_var.pwszVal); + +done: + if (prop_var_init) + PropVariantClear(&prop_var); + + IFACE_RELEASE(prop_store); + + return result; +} + void *mmdevice_list_new(const void *u, EDataFlow data_flow) { HRESULT hr; UINT i; - PROPVARIANT prop_var; union string_list_elem_attr attr; IMMDeviceEnumerator *enumerator = NULL; IMMDeviceCollection *collection = NULL; UINT dev_count = 0; IMMDevice *device = NULL; LPWSTR dev_id_wstr = NULL; - IPropertyStore *prop_store = NULL; bool br = false; - bool prop_var_init = false; char *dev_id_str = NULL; char *dev_name_str = NULL; struct string_list *sl = string_list_new(); @@ -75,19 +106,7 @@ void *mmdevice_list_new(const void *u, EDataFlow data_flow) if (!(dev_id_str = utf16_to_utf8_string_alloc(dev_id_wstr))) goto error; - hr = _IMMDevice_OpenPropertyStore(device, STGM_READ, &prop_store); - if (FAILED(hr)) - goto error; - - PropVariantInit(&prop_var); - prop_var_init = true; - hr = _IPropertyStore_GetValue( - prop_store, PKEY_Device_FriendlyName, - &prop_var); - if (FAILED(hr)) - goto error; - - if (!(dev_name_str = utf16_to_utf8_string_alloc(prop_var.pwszVal))) + if (!(dev_name_str = mmdevice_name(device))) goto error; br = string_list_append(sl, dev_name_str, attr); @@ -96,15 +115,12 @@ void *mmdevice_list_new(const void *u, EDataFlow data_flow) if (dev_id_str) sl->elems[sl->size-1].userdata = dev_id_str; - PropVariantClear(&prop_var); - prop_var_init = false; if (dev_id_wstr) CoTaskMemFree(dev_id_wstr); if (dev_name_str) free(dev_name_str); dev_name_str = NULL; dev_id_wstr = NULL; - IFACE_RELEASE(prop_store); IFACE_RELEASE(device); } @@ -120,9 +136,6 @@ void *mmdevice_list_new(const void *u, EDataFlow data_flow) free(dev_name_str); dev_id_str = NULL; dev_name_str = NULL; - if (prop_var_init) - PropVariantClear(&prop_var); - IFACE_RELEASE(prop_store); if (dev_id_wstr) CoTaskMemFree(dev_id_wstr); dev_id_wstr = NULL; diff --git a/audio/common/mmdevice_common.h b/audio/common/mmdevice_common.h index b77d8805c25e..ea839c28987a 100644 --- a/audio/common/mmdevice_common.h +++ b/audio/common/mmdevice_common.h @@ -23,6 +23,12 @@ RETRO_BEGIN_DECLS void *mmdevice_list_new(const void *u, EDataFlow data_flow); +/** + * Gets the friendly name of the provided IMMDevice. + * The string must be freed with free(). + */ +char* mmdevice_name(IMMDevice *device); + RETRO_END_DECLS #endif diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index d0b1e893ac1d..0db257c00ac1 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -25,7 +25,7 @@ typedef struct { HANDLE read_event; IMMDevice *device; - LPWSTR device_id; + char *device_name; IAudioClient *client; IAudioCaptureClient *capture; @@ -114,8 +114,8 @@ static bool wasapi_microphone_fetch_buffer( hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frame_count, &buffer_status_flags, NULL, NULL); if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer (%s)\n", - microphone->device_id, + RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s buffer (%s)\n", + microphone->device_name, hresult_name(hr)); return false; } @@ -125,8 +125,8 @@ static bool wasapi_microphone_fetch_buffer( hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, byte_count); if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer (%s)\n", - microphone->device_id, + RARCH_ERR("[WASAPI]: Failed to release capture device \"%s\"'s buffer (%s)\n", + microphone->device_name, hresult_name(hr)); return false; } @@ -163,8 +163,8 @@ static ssize_t wasapi_microphone_fetch_fifo(wasapi_microphone_handle_t *micropho hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frames_read, &buffer_status_flags, NULL, NULL); if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s buffer: %s\n", - microphone->device_id, + RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s buffer: %s\n", + microphone->device_name, hresult_name(hr)); return -1; } @@ -185,8 +185,8 @@ static ssize_t wasapi_microphone_fetch_fifo(wasapi_microphone_handle_t *micropho hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, frames_read); if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: Failed to release capture device \"%ls\"'s buffer after consuming %u frames: %s\n", - microphone->device_id, + RARCH_ERR("[WASAPI]: Failed to release capture device \"%s\"'s buffer after consuming %u frames: %s\n", + microphone->device_name, frames_read, hresult_name(hr)); return -1; @@ -197,8 +197,8 @@ static ssize_t wasapi_microphone_fetch_fifo(wasapi_microphone_handle_t *micropho hr = _IAudioCaptureClient_GetNextPacketSize(microphone->capture, &next_packet_size); if (FAILED(hr)) { /* Get the number of frames that the mic has for us. */ - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s next packet size: %s\n", - microphone->device_id, hresult_name(hr)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s next packet size: %s\n", + microphone->device_name, hresult_name(hr)); return -1; } } @@ -229,10 +229,10 @@ static bool wasapi_microphone_wait_for_capture_event(wasapi_microphone_handle_t return true; case WAIT_TIMEOUT: /* Time out; there's nothing here for us. */ - RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: Timeout after %ums\n", microphone->device_id, timeout); + RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%s\" event: Timeout after %ums\n", microphone->device_name, timeout); return false; default: - RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%ls\" event: %s\n", microphone->device_id, wasapi_error(GetLastError())); + RARCH_ERR("[WASAPI]: Failed to wait for capture device \"%s\" event: %s\n", microphone->device_name, wasapi_error(GetLastError())); return false; } } @@ -261,8 +261,8 @@ static ssize_t wasapi_microphone_read_shared_unbuffered( hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); if (FAILED(hr)) { /* Get the number of frames that the mic has for us. */ - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", - microphone->device_id, hresult_name(hr)); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s available frames: %s\n", + microphone->device_name, hresult_name(hr)); return -1; } @@ -308,7 +308,7 @@ static ssize_t wasapi_microphone_read_shared_nonblock( { /* Get the number of frames that the mic has for us. */ char error[256]; wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s available frames: %s\n", microphone->device_name, error); return -1; } @@ -332,7 +332,7 @@ static ssize_t wasapi_microphone_read_shared_nonblock( { /* Get the number of frames that the mic has for us. */ char error[256]; wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%ls\"'s available frames: %s\n", microphone->device_id, error); + RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s available frames: %s\n", microphone->device_name, error); return -1; } @@ -533,10 +533,10 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device goto error; } - hr = _IMMDevice_GetId(microphone->device, µphone->device_id); - if (FAILED(hr)) + microphone->device_name = mmdevice_name(microphone->device); + if (!microphone->device_name) { - RARCH_ERR("[WASAPI]: Failed to get ID of capture device: %s\n", hresult_name(hr)); + RARCH_ERR("[WASAPI]: Failed to get friendly name of capture device\n"); goto error; } @@ -544,15 +544,15 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device µphone->exclusive, &float_format, &rate, latency, 1); if (!microphone->client) { - RARCH_ERR("[WASAPI]: Failed to open client for capture device \"%ls\"\n", microphone->device_id); + RARCH_ERR("[WASAPI]: Failed to open client for capture device \"%s\"\n", microphone->device_name); goto error; } hr = _IAudioClient_GetBufferSize(microphone->client, &frame_count); if (FAILED(hr)) { - RARCH_ERR("[WASAPI]: Failed to get buffer size of IAudioClient for capture device \"%ls\": %s\n", - microphone->device_id, hresult_name(hr)); + RARCH_ERR("[WASAPI]: Failed to get buffer size of IAudioClient for capture device \"%s\": %s\n", + microphone->device_name, hresult_name(hr)); goto error; } @@ -648,8 +648,8 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device CloseHandle(microphone->read_event); if (microphone->buffer) fifo_free(microphone->buffer); - if (microphone->device_id) - CoTaskMemFree(microphone->device_id); + if (microphone->device_name) + free(microphone->device_name); free(microphone); return NULL; @@ -674,8 +674,8 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c IFACE_RELEASE(microphone->device); if (microphone->buffer) fifo_free(microphone->buffer); - if (microphone->device_id) - CoTaskMemFree(microphone->device_id); + if (microphone->device_name) + free(microphone->device_name); free(microphone); wasapi->microphone = NULL; @@ -714,8 +714,8 @@ static bool wasapi_microphone_start_mic(void *driver_context, void *microphone_c } else { - RARCH_ERR("[WASAPI mic]: Failed to start capture device \"%ls\"'s IAudioClient: %s\n", - microphone->device_id, hresult_name(hr)); + RARCH_ERR("[WASAPI mic]: Failed to start capture device \"%s\"'s IAudioClient: %s\n", + microphone->device_name, hresult_name(hr)); microphone->running = false; } } @@ -741,12 +741,12 @@ static bool wasapi_microphone_stop_mic(void *driver_context, void *microphone_co hr = _IAudioClient_Stop(microphone->client); if (FAILED(hr)) { - RARCH_ERR("[WASAPI mic]: Failed to stop capture device \"%ls\"'s IAudioClient: %s\n", - microphone->device_id, hresult_name(hr)); + RARCH_ERR("[WASAPI mic]: Failed to stop capture device \"%s\"'s IAudioClient: %s\n", + microphone->device_name, hresult_name(hr)); return false; } - RARCH_LOG("[WASAPI mic]: Stopped capture device \"%ls\"\n", microphone->device_id); + RARCH_LOG("[WASAPI mic]: Stopped capture device \"%s\"\n", microphone->device_name); microphone->running = false; From 589c5f5040d6f78a725544e5bc30b5b04428936c Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 5 Feb 2023 16:32:28 -0500 Subject: [PATCH 207/309] Fix the ALSA mic drivers - To comply with changes I previously made to the mic driver interface --- microphone/drivers/alsa.c | 49 +++++++++++++++++++++++---------- microphone/drivers/alsathread.c | 30 ++++++++++++++++---- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/microphone/drivers/alsa.c b/microphone/drivers/alsa.c index 7574d7879c6e..35a95050ce6b 100644 --- a/microphone/drivers/alsa.c +++ b/microphone/drivers/alsa.c @@ -375,7 +375,7 @@ static bool alsa_microphone_set_mic_active(void *driver_context, void *microphon return true; } -static bool alsa_microphone_get_mic_active(const void *driver_context, const void *microphone_context) +static bool alsa_microphone_mic_alive(const void *driver_context, const void *microphone_context) { alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; @@ -457,19 +457,38 @@ static void alsa_microphone_close_mic(void *driver_context, void *microphone_con } } +static bool alsa_microphone_start_mic(void *driver_context, void *microphone_context) +{ + return alsa_microphone_set_mic_active(driver_context, microphone_context, true); +} + +static bool alsa_microphone_stop_mic(void *driver_context, void *microphone_context) +{ + return alsa_microphone_set_mic_active(driver_context, microphone_context, true); +} + +static bool alsa_microphone_mic_use_float(const void *driver_context, const void *microphone_context) +{ + alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + + return microphone->stream_info.has_float; +} + microphone_driver_t microphone_alsa = { - alsa_microphone_init, - alsa_microphone_free, - alsa_microphone_read, - alsa_microphone_stop, - alsa_microphone_start, - alsa_microphone_alive, - alsa_microphone_set_mic_active, - alsa_microphone_get_mic_active, - alsa_microphone_set_nonblock_state, - "alsa", - alsa_microphone_device_list_new, - alsa_microphone_device_list_free, - alsa_microphone_open_mic, - alsa_microphone_close_mic + alsa_microphone_init, + alsa_microphone_free, + alsa_microphone_read, + alsa_microphone_start, + alsa_microphone_stop, + alsa_microphone_alive, + alsa_microphone_set_nonblock_state, + "alsa", + alsa_microphone_device_list_new, + alsa_microphone_device_list_free, + alsa_microphone_open_mic, + alsa_microphone_close_mic, + alsa_microphone_mic_alive, + alsa_microphone_start_mic, + alsa_microphone_stop_mic, + alsa_microphone_mic_use_float }; diff --git a/microphone/drivers/alsathread.c b/microphone/drivers/alsathread.c index d0bc6e7f8f0a..1dc24c9d6501 100644 --- a/microphone/drivers/alsathread.c +++ b/microphone/drivers/alsathread.c @@ -272,6 +272,7 @@ static ssize_t alsa_thread_microphone_read(void *driver_context, void *microphon } } +static bool alsa_thread_microphone_mic_alive(const void *driver_context, const void *microphone_context); static bool alsa_thread_microphone_start(void *driver_context, bool is_shutdown) { alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; @@ -374,7 +375,7 @@ static void alsa_thread_microphone_close_mic(void *driver_context, void *microph } } -static bool alsa_thread_microphone_get_mic_active(const void *driver_context, const void *microphone_context) +static bool alsa_thread_microphone_mic_alive(const void *driver_context, const void *microphone_context) { alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t *)microphone_context; @@ -433,19 +434,38 @@ static void alsa_thread_microphone_device_list_free(const void *driver_context, /* Does nothing if devices is NULL */ } +static bool alsa_thread_microphone_start_mic(void *driver_context, void *microphone_context) +{ + return alsa_thread_microphone_set_mic_active(driver_context, microphone_context, true); +} + +static bool alsa_thread_microphone_stop_mic(void *driver_context, void *microphone_context) +{ + return alsa_thread_microphone_set_mic_active(driver_context, microphone_context, true); +} + +static bool alsa_thread_microphone_mic_use_float(const void *driver_context, const void *microphone_context) +{ + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + + return microphone->info.stream_info.has_float; +} + microphone_driver_t microphone_alsathread = { alsa_thread_microphone_init, alsa_thread_microphone_free, alsa_thread_microphone_read, - alsa_thread_microphone_stop, alsa_thread_microphone_start, + alsa_thread_microphone_stop, alsa_thread_microphone_alive, - alsa_thread_microphone_set_mic_active, - alsa_thread_microphone_get_mic_active, alsa_thread_microphone_set_nonblock_state, "alsathread", alsa_thread_microphone_device_list_new, alsa_thread_microphone_device_list_free, alsa_thread_microphone_open_mic, - alsa_thread_microphone_close_mic + alsa_thread_microphone_close_mic, + alsa_thread_microphone_mic_alive, + alsa_thread_microphone_start_mic, + alsa_thread_microphone_stop_mic, + alsa_thread_microphone_mic_use_float }; From 81c860a749d79307e922870b12effb206c8233b2 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 5 Feb 2023 17:20:27 -0500 Subject: [PATCH 208/309] Remove unused synchronization primitives from the SDL microphone driver --- microphone/drivers/sdl_microphone.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/microphone/drivers/sdl_microphone.c b/microphone/drivers/sdl_microphone.c index cf4a7e5ddf66..4ff8503849a2 100644 --- a/microphone/drivers/sdl_microphone.c +++ b/microphone/drivers/sdl_microphone.c @@ -39,11 +39,6 @@ typedef struct sdl_microphone_handle typedef struct sdl_microphone { -#ifdef HAVE_THREADS - slock_t *lock; - scond_t *cond; -#endif - bool nonblock; bool is_paused; @@ -83,25 +78,9 @@ static void *sdl_microphone_init(void) if (!sdl) return NULL; -#ifdef HAVE_THREADS - sdl->lock = slock_new(); - sdl->cond = scond_new(); - if (!sdl->lock || !sdl->cond) - { - goto error; - } -#endif - return sdl; error: -#ifdef HAVE_THREADS - if (sdl) - { - slock_free(sdl->lock); - scond_free(sdl->cond); - } -#endif free(sdl); return NULL; } @@ -117,11 +96,6 @@ static void sdl_microphone_free(void *data) if (sdl->microphone) sdl_microphone_close_mic(sdl, sdl->microphone); -#ifdef HAVE_THREADS - slock_free(sdl->lock); - scond_free(sdl->cond); -#endif - SDL_QuitSubSystem(SDL_INIT_AUDIO); } free(sdl); From ff094ab4c21ab2fbdf890c7f850e372ef17f62d4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 5 Feb 2023 17:20:39 -0500 Subject: [PATCH 209/309] Add sdl_microphone_mic_use_float --- microphone/drivers/sdl_microphone.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/microphone/drivers/sdl_microphone.c b/microphone/drivers/sdl_microphone.c index 4ff8503849a2..7f79563c6946 100644 --- a/microphone/drivers/sdl_microphone.c +++ b/microphone/drivers/sdl_microphone.c @@ -419,6 +419,11 @@ static bool sdl_microphone_start(void *data, bool is_shutdown) return true; } +static bool sdl_microphone_mic_use_float(const void *driver_context, const void *microphone_context) +{ + return false; +} + microphone_driver_t microphone_sdl = { sdl_microphone_init, sdl_microphone_free, @@ -434,5 +439,6 @@ microphone_driver_t microphone_sdl = { sdl_microphone_close_mic, sdl_microphone_mic_alive, sdl_microphone_start_mic, - sdl_microphone_stop_mic + sdl_microphone_stop_mic, + sdl_microphone_mic_use_float }; \ No newline at end of file From d376a993107fa07e1f0edeb9276cab5a929ea9b4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 6 Feb 2023 12:14:00 -0500 Subject: [PATCH 210/309] Document audio_driver_state_flags - I needed to understand these to see if similar flags were required for the mic driver --- audio/audio_driver.h | 71 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/audio/audio_driver.h b/audio/audio_driver.h index 72f57044355a..809fbe96317b 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -167,13 +167,84 @@ typedef struct audio_driver size_t (*buffer_size)(void *data); } audio_driver_t; +/** + * Bit flags that describe the current state of the audio driver. + */ enum audio_driver_state_flags { + /** + * Indicates that the driver was successfully created + * and is currently valid. + * You may submit samples for output at any time. + * + * This flag does \em not mean that the player will hear anything; + * the driver might be suspended. + * + * @see AUDIO_FLAG_SUSPENDED + */ AUDIO_FLAG_ACTIVE = (1 << 0), + + /** + * Indicates that the audio driver outputs floating-point samples, + * as opposed to integer samples. + * + * All audio is sent through the resampler, + * which operates on floating-point samples. + * + * If this flag is set, then the resampled output doesn't need + * to be converted back to \c int16_t format. + * + * This won't affect the audio that the core writes; + * either way, it's supposed to output \c int16_t samples. + * + * This flag won't be set if the selected audio driver + * doesn't support (or is configured to not use) \c float samples. + * + * @see audio_driver_t::use_float + */ AUDIO_FLAG_USE_FLOAT = (1 << 1), + + /** + * Indicates that the audio driver is not currently rendering samples, + * although it's valid and can be resumed. + * + * Usually set when RetroArch needs to simulate audio output + * without actually rendering samples (e.g. runahead), + * or when reinitializing the driver. + * + * Samples will still be accepted, but they will be silently dropped. + */ AUDIO_FLAG_SUSPENDED = (1 << 2), + + /** + * Indicates that the audio mixer is available + * and can mix one or more audio streams. + * + * Will not be set if RetroArch was built without \c HAVE_AUDIOMIXER. + */ AUDIO_FLAG_MIXER_ACTIVE = (1 << 3), + + /** + * Indicates that the frontend will never need audio from the core, + * usually when runahead is active. + * + * When set, any audio received by the core will not be processed. + * + * Will not be set if RetroArch was built without \c HAVE_RUNAHEAD. + * + * @see RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE + */ AUDIO_FLAG_HARD_DISABLE = (1 << 4), + + /** + * Indicates that audio rate control is enabled. + * This means that the audio system will adjust the rate at which + * it sends samples to the driver, + * minimizing the occurrences of buffer overrun or underrun. + * + * @see audio_driver_t::write_avail + * @see audio_driver_t::buffer_size + */ AUDIO_FLAG_CONTROL = (1 << 5) }; From b36e1b35e5c8f7de7e88ed7e51fe1b7dd7949dc4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:11:26 -0500 Subject: [PATCH 211/309] Remove an unused function in wasapi.c --- audio/common/wasapi.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/audio/common/wasapi.c b/audio/common/wasapi.c index 40a722fad303..10162ee40d61 100644 --- a/audio/common/wasapi.c +++ b/audio/common/wasapi.c @@ -169,16 +169,6 @@ static const char* wasapi_data_flow_name(EDataFlow data_flow) } } -static unsigned wasapi_pref_rate(unsigned i) -{ - const unsigned r[] = { 48000, 44100, 96000, 192000, 32000 }; - - if (i >= sizeof(r) / sizeof(unsigned)) - return 0; - - return r[i]; -} - static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, bool float_fmt, unsigned rate, unsigned channels); /** From 14879697710bea736801208a9bd01cc8c3b08b4a Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:12:01 -0500 Subject: [PATCH 212/309] Add and document flags in microphone_driver.h --- microphone/microphone_driver.h | 85 ++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index f8d5ca99c8f0..09cf67665d57 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -28,15 +28,92 @@ enum microphone_driver_state_flags { /** - * Indicates that the microphone driver is active. - * Microphones can be opened, closed, and read. + * Indicates that the driver was successfully created + * and is currently valid. + * You may open microphones and query them for samples at any time. + * + * This flag does \em not mean that the core will receive anything; + * the driver might be suspended. */ - MICROPHONE_DRIVER_FLAG_ACTIVE = (1 << 0), + MICROPHONE_DRIVER_FLAG_ACTIVE = (1 << 0), + MICROPHONE_DRIVER_FLAG_ENABLED = (1 << 1), + MICROPHONE_DRIVER_FLAG_CONTROL = (1 << 2) }; enum microphone_state_flags { - MICROPHONE_FLAG_USE_FLOAT = (1 << 0) + /** + * Indicates that the microphone was successfully created + * and is currently valid. + * You may query it for samples at any time. + * + * This flag does \em not mean that the core will receive anything, + * as there are several situations where a mic will return silence. + * + * If this flag is not set, then the others are meaningless. + * Reads from this microphone will return an error. + */ + MICROPHONE_FLAG_ACTIVE = (1 << 0), + + /** + * Indicates that the core considers this microphone "on" + * and ready to retrieve audio. + * + * Even if a microphone is opened, the user might not want it running + * at all times; they might prefer to hold a button to use it. + * + * If this flag is not set, the microphone will not process input. + * Reads from it will return silence. + */ + MICROPHONE_FLAG_ENABLED = (1 << 1), + + /** + * Indicates that this microphone was requested + * before the microphone driver was initialized, + * so the driver will need to create this microphone + * when it's ready. + * + * If this flag is set, reads from this microphone return silence + * of the requested length. + */ + MICROPHONE_FLAG_PENDING = (1 << 2), + + /** + * Indicates that the microphone provides floating-point samples, + * as opposed to integer samples. + * + * All audio is sent through the resampler, + * which operates on floating-point samples. + * + * If this flag is set, then the resampled output doesn't need + * to be converted back to \c int16_t format. + * + * This won't affect the audio that the core receives; + * either way, it's supposed to receive \c int16_t samples. + * + * This flag won't be set if the selected microphone driver + * doesn't support (or is configured to not use) \c float samples. + * + * @see microphone_driver_t::mic_use_float + */ + MICROPHONE_FLAG_USE_FLOAT = (1 << 3), + + /** + * Indicates that the microphone driver is not currently retrieving samples, + * although it's valid and can be resumed. + * + * Usually set when RetroArch needs to simulate audio input + * without actually rendering samples (e.g. runahead), + * or when reinitializing the driver. + * + * If this flag is set, reads from this microphone return silence + * of the requested length. + * + * This is different from \c MICROPHONE_FLAG_ACTIVE and \c MICROPHONE_FLAG_ENABLED; + * \c MICROPHONE_FLAG_ACTIVE indicates that the microphone is valid, + * and \c MICROPHONE_FLAG_ENABLED indicates that the core has the microphone turned on. + */ + MICROPHONE_FLAG_SUSPENDED = (1 << 4) }; /** From d2538d02221ce614f56a7779acfc527b1cbdf018 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:13:28 -0500 Subject: [PATCH 213/309] Remove driver-specific mic start/stop functions - The mic driver itself doesn't do much processing - That honor goes to individual mics --- microphone/drivers/alsa.c | 3 --- microphone/drivers/alsathread.c | 3 --- microphone/drivers/sdl_microphone.c | 3 --- microphone/drivers/wasapi.c | 3 --- microphone/microphone_driver.h | 31 ----------------------------- 5 files changed, 43 deletions(-) diff --git a/microphone/drivers/alsa.c b/microphone/drivers/alsa.c index 35a95050ce6b..c72a12236ab8 100644 --- a/microphone/drivers/alsa.c +++ b/microphone/drivers/alsa.c @@ -478,9 +478,6 @@ microphone_driver_t microphone_alsa = { alsa_microphone_init, alsa_microphone_free, alsa_microphone_read, - alsa_microphone_start, - alsa_microphone_stop, - alsa_microphone_alive, alsa_microphone_set_nonblock_state, "alsa", alsa_microphone_device_list_new, diff --git a/microphone/drivers/alsathread.c b/microphone/drivers/alsathread.c index 1dc24c9d6501..eb2588878bfa 100644 --- a/microphone/drivers/alsathread.c +++ b/microphone/drivers/alsathread.c @@ -455,9 +455,6 @@ microphone_driver_t microphone_alsathread = { alsa_thread_microphone_init, alsa_thread_microphone_free, alsa_thread_microphone_read, - alsa_thread_microphone_start, - alsa_thread_microphone_stop, - alsa_thread_microphone_alive, alsa_thread_microphone_set_nonblock_state, "alsathread", alsa_thread_microphone_device_list_new, diff --git a/microphone/drivers/sdl_microphone.c b/microphone/drivers/sdl_microphone.c index 7f79563c6946..1567970f4a20 100644 --- a/microphone/drivers/sdl_microphone.c +++ b/microphone/drivers/sdl_microphone.c @@ -428,9 +428,6 @@ microphone_driver_t microphone_sdl = { sdl_microphone_init, sdl_microphone_free, sdl_microphone_read, - sdl_microphone_start, - sdl_microphone_stop, - sdl_microphone_alive, sdl_microphone_set_nonblock_state, "sdl2", NULL, diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 0db257c00ac1..391919d14c51 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -789,9 +789,6 @@ microphone_driver_t microphone_wasapi = { wasapi_microphone_init, wasapi_microphone_free, wasapi_microphone_read, - wasapi_microphone_start, - wasapi_microphone_stop, - wasapi_microphone_alive, wasapi_microphone_set_nonblock_state, "wasapi", wasapi_microphone_device_list_new, diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index 09cf67665d57..696794df743b 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -227,37 +227,6 @@ typedef struct microphone_driver */ ssize_t (*read)(void *driver_context, void *mic_context, void *buffer, size_t buffer_size); - /** - * Resumes the driver from the paused state. - * Microphones will resume activity \em if they were already active - * before the driver was stopped. - **/ - bool (*start)(void *data, bool is_shutdown); - - /** - * Temporarily pauses all microphones. - * The return value of \c get_mic_state will not be updated, - * but all microphones will stop recording until \c start is called. - * - * @param driver_context Pointer to the driver context. - * Will be the value that was returned by \c ::init(). - * @return \c true if microphone driver was successfully paused, - * \c false if there was an error. - **/ - bool (*stop)(void *driver_context); - - /** - * Queries whether the driver is active. - * This only queries the overall driver state, - * not the state of individual microphones. - * - * @param driver_context Pointer to the driver context. - * Will be the value that was returned by \c ::init(). - * @return \c true if the driver is active. - * \c false if not or if there was an error. - */ - bool (*alive)(void *driver_context); - /** * Sets the nonblocking state of all microphones. * Primarily used for fast-forwarding. From 1676214beb657c6723b9d4c33e10acbe4878cd89 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:14:07 -0500 Subject: [PATCH 214/309] Remove some unused fields in microphone_driver.h --- microphone/microphone_driver.h | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index 696794df743b..72199a828d39 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -141,24 +141,11 @@ struct retro_microphone size_t sample_buffer_length; /** - * Number of bytes that were copied into the buffer in the most recent flush. - * Accounts for resampling - **/ - size_t most_recent_copy_length; - - enum microphone_state_flags flags; - - /* May be enabled even before the driver is ready */ - bool pending_enabled; - - /** - * True if this object represents a valid or pending microphone. - * Mostly exists because retro_microphone is statically allocated, - * so there's no reason to check it against NULL. + * Bit flags that describe the state of this microphone. + * + * @see microphone_state_flags */ - bool active; - - bool error; + enum microphone_state_flags flags; }; /** @@ -391,11 +378,6 @@ typedef struct retro_microphone_t microphone; enum microphone_driver_state_flags flags; - - /** - * If \c true, all microphones return silence. - */ - bool mute_enable; } microphone_driver_state_t; /** From d89767de5a0507445334b5b5f66396cd7f478345 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:16:25 -0500 Subject: [PATCH 215/309] Add CMD_EVENT_MICROPHONE_STOP/START --- command.h | 4 ++++ menu/menu_driver.c | 26 ++++++++++++++++++++++---- retroarch.c | 15 +++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/command.h b/command.h index e33e8529af9b..3dc773a4fd14 100644 --- a/command.h +++ b/command.h @@ -120,6 +120,10 @@ enum event_command CMD_EVENT_AUDIO_START, /* Mutes audio. */ CMD_EVENT_AUDIO_MUTE_TOGGLE, + /* Stops all enabled microphones. */ + CMD_EVENT_MICROPHONE_STOP, + /* Starts all enabled microphones */ + CMD_EVENT_MICROPHONE_START, /* Volume adjustments. */ CMD_EVENT_VOLUME_UP, CMD_EVENT_VOLUME_DOWN, diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 5e29fb499cd2..d092f8ec917d 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -6808,8 +6808,15 @@ void menu_driver_toggle( /* Stop all rumbling before entering the menu. */ command_event(CMD_EVENT_RUMBLE_STOP, NULL); - if (pause_libretro && !audio_enable_menu) - command_event(CMD_EVENT_AUDIO_STOP, NULL); + if (pause_libretro) + { /* If the menu pauses the game... */ + command_event(CMD_EVENT_MICROPHONE_STOP, NULL); + + if (!audio_enable_menu) + { /* If the menu shouldn't have audio... */ + command_event(CMD_EVENT_AUDIO_STOP, NULL); + } + } /* Override keyboard callback to redirect to menu instead. * We'll use this later for something ... */ @@ -6834,8 +6841,19 @@ void menu_driver_toggle( if (!runloop_shutdown_initiated) driver_set_nonblock_state(); - if (pause_libretro && !audio_enable_menu) - command_event(CMD_EVENT_AUDIO_START, NULL); + if (pause_libretro) + { /* If the menu pauses the game... */ + + if (!audio_enable_menu) + { /* ...and the menu doesn't have audio... */ + + command_event(CMD_EVENT_AUDIO_START, NULL); + /* ...then re-enable the audio driver (which we shut off earlier) */ + } + + command_event(CMD_EVENT_MICROPHONE_START, NULL); + /* Start the microphone, if it was paused beforehand */ + } /* Restore libretro keyboard callback. */ if (key_event && frontend_key_event) diff --git a/retroarch.c b/retroarch.c index 61bfeaab5fd3..8c0b79f3557b 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2797,6 +2797,15 @@ bool command_event(enum event_command cmd, void *data) RUNLOOP_FLAG_SHUTDOWN_INITIATED)) return false; break; + case CMD_EVENT_MICROPHONE_STOP: + if (!microphone_driver_stop()) + return false; + break; + case CMD_EVENT_MICROPHONE_START: + if (!microphone_driver_start(runloop_st->flags & + RUNLOOP_FLAG_SHUTDOWN_INITIATED)) + return false; + break; case CMD_EVENT_AUDIO_MUTE_TOGGLE: { audio_driver_state_t @@ -3435,9 +3444,15 @@ bool command_event(enum event_command cmd, void *data) bool menu_pause_libretro = settings->bools.menu_pause_libretro; #endif if (menu_pause_libretro) + { /* If entering the menu pauses the game... */ command_event(CMD_EVENT_AUDIO_STOP, NULL); + command_event(CMD_EVENT_MICROPHONE_STOP, NULL); + } else + { command_event(CMD_EVENT_AUDIO_START, NULL); + command_event(CMD_EVENT_MICROPHONE_START, NULL); + } } else { From e8d09bd39cd8858a71531dcecdc739c39f6fe21d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:16:59 -0500 Subject: [PATCH 216/309] Remove unused functions from microphone_null --- microphone/microphone_driver.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index b5e8c0317623..c780a6544aaa 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -27,9 +27,6 @@ static microphone_driver_state_t mic_driver_st = {0}; /* double alignment */ microphone_driver_t microphone_null = { - NULL, - NULL, - NULL, NULL, NULL, NULL, From b2091034ea6bc053055af5fac1a46eaf7b231959 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:17:35 -0500 Subject: [PATCH 217/309] Change how the mic driver state is referenced in some places --- microphone/microphone_driver.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index c780a6544aaa..a18a3e8ff8ff 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -165,13 +165,14 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone); bool microphone_driver_init_internal(void *settings_data) { settings_t *settings = (settings_t*)settings_data; + microphone_driver_state_t *mic_st = &mic_driver_st; float slowmotion_ratio = settings->floats.slowmotion_ratio; bool verbosity_enabled = verbosity_is_enabled(); size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; if (!settings->bools.microphone_enable) { /* If the user has mic support turned off... */ - mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; RARCH_WARN("[Microphone]: Refused to initialize microphone driver because it's disabled in the settings\n"); return false; } @@ -186,21 +187,21 @@ bool microphone_driver_init_internal(void *settings_data) goto error; } - mic_driver_st.input_samples_conv_buf_length = insamples_max; - mic_driver_st.input_samples_conv_buf = (int16_t*)memalign_alloc(64, mic_driver_st.input_samples_conv_buf_length); - if (!mic_driver_st.input_samples_conv_buf) + mic_st->input_samples_conv_buf_length = insamples_max; + mic_st->input_samples_conv_buf = (int16_t*)memalign_alloc(64, mic_driver_st.input_samples_conv_buf_length); + if (!mic_st->input_samples_conv_buf) goto error; - mic_driver_st.input_samples_buf_length = insamples_max * sizeof(float); - mic_driver_st.input_samples_buf = (float*)memalign_alloc(64, mic_driver_st.input_samples_buf_length); - if (!mic_driver_st.input_samples_buf) + mic_st->input_samples_buf_length = insamples_max * sizeof(float); + mic_st->input_samples_buf = (float*)memalign_alloc(64, mic_driver_st.input_samples_buf_length); + if (!mic_st->input_samples_buf) goto error; - if (!mic_driver_st.driver || !mic_driver_st.driver->init) + if (!mic_st->driver || !mic_st->driver->init) goto error; - mic_driver_st.driver_context = mic_driver_st.driver->init(); - if (!mic_driver_st.driver_context) + mic_st->driver_context = mic_st->driver->init(); + if (!mic_st->driver_context) goto error; RARCH_LOG("[Microphone]: Initialized microphone driver\n"); @@ -226,8 +227,8 @@ bool microphone_driver_init_internal(void *settings_data) return true; error: - RARCH_ERR("[Microphone]: Failed to initialize microphone driver. Will continue without audio input.\n"); - mic_driver_st.flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + RARCH_ERR("[Microphone]: Failed to start microphone driver. Will continue without audio input.\n"); + mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; return microphone_driver_deinit(); } From af51baa8ae457f8b783b1c86d288931f3cd4b396 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:19:31 -0500 Subject: [PATCH 218/309] Simplify the SDL microphone driver - The driver backend no longer keeps a reference to the mic (the frontend does that) - Remove functions that are no longer needed - Don't track paused state, just query the mic itself --- microphone/drivers/sdl_microphone.c | 176 ++++++++++------------------ 1 file changed, 61 insertions(+), 115 deletions(-) diff --git a/microphone/drivers/sdl_microphone.c b/microphone/drivers/sdl_microphone.c index 1567970f4a20..d14ea295dd99 100644 --- a/microphone/drivers/sdl_microphone.c +++ b/microphone/drivers/sdl_microphone.c @@ -33,19 +33,12 @@ typedef struct sdl_microphone_handle * The queue used to store incoming samples from the driver. */ fifo_buffer_t *sample_buffer; - bool is_paused; SDL_AudioDeviceID device_id; } sdl_microphone_handle_t; typedef struct sdl_microphone { bool nonblock; - bool is_paused; - - /* Only one microphone is supported right now; - * the driver state should track multiple microphone handles, - * but the driver *context* should track multiple microphone contexts */ - sdl_microphone_handle_t *microphone; } sdl_microphone_t; static INLINE int find_num_frames(int rate, int latency) @@ -85,7 +78,7 @@ static void *sdl_microphone_init(void) return NULL; } -static void sdl_microphone_close_mic(void *data, void *microphone_context); +static void sdl_microphone_close_mic(void *driver_context, void *microphone_context); static void sdl_microphone_free(void *data) { @@ -93,29 +86,26 @@ static void sdl_microphone_free(void *data) if (sdl) { - if (sdl->microphone) - sdl_microphone_close_mic(sdl, sdl->microphone); - SDL_QuitSubSystem(SDL_INIT_AUDIO); } free(sdl); + /* NOTE: The microphone frontend should've closed the mics by now */ } static void sdl_audio_record_cb(void *data, Uint8 *stream, int len) { - sdl_microphone_t *sdl = (sdl_microphone_t*)data; - size_t avail = FIFO_WRITE_AVAIL(sdl->microphone->sample_buffer); - size_t read_size = len > (int)avail ? avail : len; - /* If the sample buffer is almost full, - * just write as much as we can into it*/ + sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t*)data; + size_t avail = FIFO_WRITE_AVAIL(microphone->sample_buffer); + size_t read_size = MIN(len, (int)avail); + /* If the sample buffer is almost full, just write as much as we can into it*/ - fifo_write(sdl->microphone->sample_buffer, stream, read_size); + fifo_write(microphone->sample_buffer, stream, read_size); #ifdef HAVE_THREADS - scond_signal(sdl->microphone->cond); + scond_signal(microphone->cond); #endif } -static void *sdl_microphone_open_mic(void *data, +static void *sdl_microphone_open_mic(void *driver_context, const char *device, unsigned rate, unsigned latency, @@ -124,24 +114,19 @@ static void *sdl_microphone_open_mic(void *data, { int frames; size_t bufsize; - sdl_microphone_t *sdl = (sdl_microphone_t*)data; sdl_microphone_handle_t *microphone = NULL; - SDL_AudioSpec desired_spec = {0}; + SDL_AudioSpec desired_spec = {0}; + void *tmp = NULL; SDL_AudioSpec out; - void *tmp = NULL; - if (!sdl || !SDL_WasInit(SDL_INIT_AUDIO)) + (void)driver_context; + + if (!SDL_WasInit(SDL_INIT_AUDIO)) { /* If the audio driver wasn't initialized yet... */ RARCH_ERR("[SDL mic]: Attempted to initialize input device before initializing the audio subsystem\n"); return NULL; } - if (sdl->microphone) - { - RARCH_ERR("[SDL mic]: Attempted to initialize a second microphone, but the driver only supports one right now\n"); - return NULL; - } - microphone = (sdl_microphone_handle_t *)calloc(1, sizeof(sdl_microphone_handle_t)); if (!microphone) return NULL; @@ -166,7 +151,7 @@ static void *sdl_microphone_open_mic(void *data, desired_spec.format = AUDIO_S16SYS; desired_spec.channels = 1; /* Microphones only usually provide input in mono */ desired_spec.samples = frames; - desired_spec.userdata = sdl; + desired_spec.userdata = microphone; desired_spec.callback = sdl_audio_record_cb; microphone->device_id = SDL_OpenAudioDevice(NULL, true, &desired_spec, &out, 0); @@ -220,8 +205,6 @@ static void *sdl_microphone_open_mic(void *data, free(tmp); } - sdl->microphone = microphone; - RARCH_LOG("[SDL audio]: Initialized microphone with device ID %u\n", microphone->device_id); return microphone; @@ -230,27 +213,19 @@ static void *sdl_microphone_open_mic(void *data, return NULL; } -static void sdl_microphone_close_mic(void *data, void *microphone_context) +static void sdl_microphone_close_mic(void *driver_context, void *microphone_context) { - sdl_microphone_t *sdl = (sdl_microphone_t*)data; sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t *)microphone_context; + (void)driver_context; - if (sdl && microphone) + if (microphone) { - retro_assert(sdl->microphone == microphone); - /* Driver only supports one microphone for now; when multi-mic support - * is added, you may still want to assert that the microphone - * was indeed created by the driver */ - if (microphone->device_id > 0) { /* If the microphone was originally initialized successfully... */ SDL_CloseAudioDevice(microphone->device_id); } - if (microphone->sample_buffer) - { - fifo_free(microphone->sample_buffer); - } + fifo_free(microphone->sample_buffer); #ifdef HAVE_THREADS slock_free(microphone->lock); @@ -258,64 +233,79 @@ static void sdl_microphone_close_mic(void *data, void *microphone_context) #endif RARCH_LOG("[SDL audio]: Freed microphone with former device ID %u\n", microphone->device_id); - sdl->microphone = NULL; free(microphone); } } static bool sdl_microphone_mic_alive(const void *data, const void *microphone_context) { - const sdl_microphone_t *sdl = (const sdl_microphone_t*)data; const sdl_microphone_handle_t *microphone = (const sdl_microphone_handle_t*)microphone_context; + (void)data; - if (!sdl || !microphone) + if (!microphone) return false; /* Both params must be non-null */ - return !microphone->is_paused; - /* The mic might be paused due to app requirements, - * or it might be stopped because the entire audio driver is stopped. */ + return SDL_GetAudioDeviceStatus(microphone->device_id) == SDL_AUDIO_PLAYING; } -static bool sdl_microphone_set_mic_active(void *data, void *microphone_context, bool enabled) +static bool sdl_microphone_start_mic(void *driver_context, void *microphone_context) { - sdl_microphone_t *sdl = (sdl_microphone_t*)data; sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t*)microphone_context; + (void)driver_context; - if (!sdl || !microphone) + if (!microphone) return false; - microphone->is_paused = !enabled; - if (!sdl->is_paused) - { /* If the entire audio driver isn't paused... */ - SDL_PauseAudioDevice(microphone->device_id, microphone->is_paused); + SDL_PauseAudioDevice(microphone->device_id, false); + + if (SDL_GetAudioDeviceStatus(microphone->device_id) != SDL_AUDIO_PLAYING) + { + RARCH_ERR("[SDL mic]: Failed to start microphone %u: %s\n", microphone->device_id, SDL_GetError()); + return false; } - RARCH_DBG("[SDL audio]: Set state of microphone %u to %s\n", - microphone->device_id, enabled ? "enabled" : "disabled"); + + RARCH_DBG("[SDL mic]: Started microphone %u\n", microphone->device_id); return true; } -static bool sdl_microphone_start_mic(void *data, void *microphone_context) +static bool sdl_microphone_stop_mic(void *driver_context, void *microphone_context) { - return sdl_microphone_set_mic_active(data, microphone_context, true); -} + sdl_microphone_t *sdl = (sdl_microphone_t*)driver_context; + sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t*)microphone_context; -static bool sdl_microphone_stop_mic(void *data, void *microphone_context) -{ - return sdl_microphone_set_mic_active(data, microphone_context, false); + if (!sdl || !microphone) + return false; + + SDL_PauseAudioDevice(microphone->device_id, true); + + switch (SDL_GetAudioDeviceStatus(microphone->device_id)) + { + case SDL_AUDIO_PAUSED: + return true; + case SDL_AUDIO_PLAYING: + RARCH_ERR("[SDL mic]: Microphone %u failed to pause\n", microphone->device_id); + return false; + case SDL_AUDIO_STOPPED: + RARCH_WARN("[SDL mic]: Microphone %u is in state STOPPED; it may not start again\n", microphone->device_id); + return true; + default: + RARCH_ERR("[SDL mic]: Microphone %u is in unknown state\n", microphone->device_id); + return false; + } } -static void sdl_microphone_set_nonblock_state(void *data, bool state) +static void sdl_microphone_set_nonblock_state(void *driver_context, bool state) { - sdl_microphone_t *sdl = (sdl_microphone_t*)data; + sdl_microphone_t *sdl = (sdl_microphone_t*)driver_context; if (sdl) sdl->nonblock = state; } -static ssize_t sdl_microphone_read(void *data, void *microphone_context, void *buf, size_t size) +static ssize_t sdl_microphone_read(void *driver_context, void *microphone_context, void *buf, size_t size) { - ssize_t ret = 0; - sdl_microphone_t *sdl = (sdl_microphone_t*)data; + ssize_t ret = 0; + sdl_microphone_t *sdl = (sdl_microphone_t*)driver_context; sdl_microphone_handle_t *microphone = (sdl_microphone_handle_t*)microphone_context; if (!sdl || !microphone || !buf) @@ -380,50 +370,6 @@ static ssize_t sdl_microphone_read(void *data, void *microphone_context, void *b return ret; } -static bool sdl_microphone_stop(void *data) -{ - sdl_microphone_t *sdl = (sdl_microphone_t*)data; - sdl->is_paused = true; - - if (sdl->microphone) - { - /* Stop the microphone independently of whether it's paused; - * note that upon sdl_audio_start, the microphone won't resume - * if it was previously paused */ - SDL_PauseAudioDevice(sdl->microphone->device_id, true); - } - - return true; -} - -static bool sdl_microphone_alive(void *data) -{ - sdl_microphone_t *sdl = (sdl_microphone_t*)data; - if (!sdl) - return false; - return !sdl->is_paused; -} - -static bool sdl_microphone_start(void *data, bool is_shutdown) -{ - sdl_microphone_t *sdl = (sdl_microphone_t*)data; - sdl->is_paused = false; - - if (sdl->microphone) - { - if (!sdl->microphone->is_paused) - SDL_PauseAudioDevice(sdl->microphone->device_id, false); - /* If the microphone wasn't paused beforehand... */ - } - - return true; -} - -static bool sdl_microphone_mic_use_float(const void *driver_context, const void *microphone_context) -{ - return false; -} - microphone_driver_t microphone_sdl = { sdl_microphone_init, sdl_microphone_free, @@ -437,5 +383,5 @@ microphone_driver_t microphone_sdl = { sdl_microphone_mic_alive, sdl_microphone_start_mic, sdl_microphone_stop_mic, - sdl_microphone_mic_use_float + NULL /* mic_use_float */ }; \ No newline at end of file From 7d9e5eb26710d4b32804688604316d2531c738f4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:20:26 -0500 Subject: [PATCH 219/309] Simplify the WASAPI microphone driver - Don't track the driver running state or the microphone handle, the frontend does that now - Remove support for unbuffered input (hunterk suggested that it wasn't necessary) --- microphone/drivers/wasapi.c | 282 +++--------------------------------- 1 file changed, 24 insertions(+), 258 deletions(-) diff --git a/microphone/drivers/wasapi.c b/microphone/drivers/wasapi.c index 391919d14c51..fbc0838f4de6 100644 --- a/microphone/drivers/wasapi.c +++ b/microphone/drivers/wasapi.c @@ -52,9 +52,7 @@ typedef struct typedef struct wasapi_microphone { - wasapi_microphone_handle_t *microphone; bool nonblock; - bool running; } wasapi_microphone_t; @@ -71,7 +69,6 @@ static void *wasapi_microphone_init(void) return NULL; } - wasapi->running = true; wasapi->nonblock = !settings->bools.audio_sync; RARCH_DBG("[WASAPI mic]: Initialized microphone driver context\n"); @@ -85,55 +82,9 @@ static void wasapi_microphone_free(void *driver_context) if (!wasapi) return; - if (wasapi->microphone) - { - wasapi_microphone_close_mic(wasapi, wasapi->microphone); - } - free(wasapi); } -/** - * Flushes microphone's most recent input to the provided buffer. - * @param microphone Pointer to the microphone context. - * @param buffer The buffer in which incoming samples will be stored. - * @param buffer_size The length of \c buffer, in bytes. - * @return True if the read was successful - */ -static bool wasapi_microphone_fetch_buffer( - wasapi_microphone_handle_t *microphone, - void *buffer, - size_t buffer_size) -{ - BYTE *mic_input = NULL; - UINT32 frame_count = 0; - UINT32 byte_count = 0; - DWORD buffer_status_flags = 0; - HRESULT hr; - - hr = _IAudioCaptureClient_GetBuffer(microphone->capture, &mic_input, &frame_count, &buffer_status_flags, NULL, NULL); - if (FAILED(hr)) - { - RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s buffer (%s)\n", - microphone->device_name, - hresult_name(hr)); - return false; - } - byte_count = frame_count * microphone->frame_size; - - memcpy(buffer, mic_input, MIN(buffer_size, frame_count)); - hr = _IAudioCaptureClient_ReleaseBuffer(microphone->capture, byte_count); - if (FAILED(hr)) - { - RARCH_ERR("[WASAPI]: Failed to release capture device \"%s\"'s buffer (%s)\n", - microphone->device_name, - hresult_name(hr)); - return false; - } - - return true; -} - /** * Flushes microphone's most recent input to the provided context's FIFO queue. * WASAPI requires that fetched input be consumed in its entirety, @@ -237,118 +188,6 @@ static bool wasapi_microphone_wait_for_capture_event(wasapi_microphone_handle_t } } -/** - * Reads incoming samples from a shared-mode microphone, - * without buffering any. - * @param microphone - * @param buffer - * @param buffer_size - * @return - */ -static ssize_t wasapi_microphone_read_shared_unbuffered( - wasapi_microphone_handle_t *microphone, - void *buffer, - size_t buffer_size) -{ - size_t read_avail = 0; - ssize_t bytes_to_read = -1; - UINT32 available_frames = 0; - HRESULT hr; - - if (!wasapi_microphone_wait_for_capture_event(microphone, INFINITE)) - return -1; - - hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); - if (FAILED(hr)) - { /* Get the number of frames that the mic has for us. */ - RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s available frames: %s\n", - microphone->device_name, hresult_name(hr)); - return -1; - } - - read_avail = microphone->engine_buffer_size - available_frames * microphone->frame_size; - if (!read_avail) - return 0; - - bytes_to_read = MIN(buffer_size, read_avail); - if (bytes_to_read) - if (!wasapi_microphone_fetch_buffer(microphone, buffer, bytes_to_read)) - return -1; - - return bytes_to_read; -} - -/** - * Reads from a shared-mode microphone into the provided buffer - * Handles both buffered and unbuffered microphone input, - * depending on the presence of microphone::buffer. - * @param microphone - * @param buffer - * @param buffer_size - * @return The number of bytes read, - * or -1 if there was an error. - */ -static ssize_t wasapi_microphone_read_shared_nonblock( - wasapi_microphone_handle_t *microphone, - void *buffer, - size_t buffer_size) -{ - size_t read_avail = 0; - ssize_t bytes_to_read = -1; - UINT32 available_frames = 0; - - if (microphone->buffer) - { /* If we're buffering mic input... */ - read_avail = FIFO_READ_AVAIL(microphone->buffer); - if (!read_avail) - { /* If the incoming sample queue is empty... */ - size_t write_avail = 0; - HRESULT hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); - if (FAILED(hr)) - { /* Get the number of frames that the mic has for us. */ - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s available frames: %s\n", microphone->device_name, error); - return -1; - } - - write_avail = FIFO_WRITE_AVAIL(microphone->buffer); - read_avail = microphone->engine_buffer_size - available_frames * microphone->frame_size; - bytes_to_read = MIN(write_avail, read_avail); - if (bytes_to_read) - if (wasapi_microphone_fetch_fifo(microphone) < 0) - return -1; - } - - read_avail = FIFO_READ_AVAIL(microphone->buffer); - bytes_to_read = MIN(buffer_size, read_avail); - if (bytes_to_read) - fifo_write(microphone->buffer, buffer, bytes_to_read); - } - else - { - HRESULT hr = _IAudioClient_GetCurrentPadding(microphone->client, &available_frames); - if (FAILED(hr)) - { /* Get the number of frames that the mic has for us. */ - char error[256]; - wasapi_log_hr(hr, error, sizeof(error)); - RARCH_ERR("[WASAPI]: Failed to get capture device \"%s\"'s available frames: %s\n", microphone->device_name, error); - return -1; - } - - read_avail = microphone->engine_buffer_size - available_frames * microphone->frame_size; - if (!read_avail) - return 0; - - bytes_to_read = MIN(buffer_size, read_avail); - if (bytes_to_read) - if (!wasapi_microphone_fetch_buffer(microphone, buffer, bytes_to_read)) - return -1; - } - - return bytes_to_read; -} - /** * Reads samples from an exclusive-mode microphone, * fetching more from it if necessary. @@ -405,9 +244,7 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v if (wasapi->nonblock) { /* If microphones shouldn't block... */ - if (microphone->exclusive) - return wasapi_microphone_read_buffered(microphone, buffer, buffer_size, 0); - return wasapi_microphone_read_shared_nonblock(microphone, buffer, buffer_size); + return wasapi_microphone_read_buffered(microphone, buffer, buffer_size, 0); } if (microphone->exclusive) @@ -424,74 +261,19 @@ static ssize_t wasapi_microphone_read(void *driver_context, void *mic_context, v else { ssize_t read; - if (microphone->buffer) - { - for (read = -1; bytes_read < buffer_size; bytes_read += read) - { - read = wasapi_microphone_read_buffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read, - INFINITE); - if (read == -1) - return -1; - } - } - else + + for (read = -1; bytes_read < buffer_size; bytes_read += read) { - for (read = -1; bytes_read < buffer_size; bytes_read += read) - { - read = wasapi_microphone_read_shared_unbuffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read); - if (read == -1) - return -1; - } + read = wasapi_microphone_read_buffered(microphone, (char *) buffer + bytes_read, buffer_size - bytes_read, + INFINITE); + if (read == -1) + return -1; } } return bytes_read; } -static bool wasapi_microphone_mic_alive(const void *driver_context, const void *mic_context); -static bool wasapi_microphone_start_mic(void *driver_context, void *microphone_context); -static bool wasapi_microphone_start(void *driver_context, bool is_shutdown) -{ - wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; - - if (!wasapi) - return false; - - if (wasapi->microphone && wasapi_microphone_mic_alive(wasapi, wasapi->microphone)) - { /* If we have a microphone that was active at the time the driver stopped... */ - bool result = wasapi_microphone_start_mic(wasapi, wasapi->microphone); - } - - wasapi->running = true; - - return true; -} - -static bool wasapi_microphone_stop_mic(void *driver_context, void *microphone_context); -static bool wasapi_microphone_stop(void *driver_context) -{ - wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; - - if (!wasapi) - return false; - - if (wasapi->microphone && wasapi_microphone_mic_alive(wasapi, wasapi->microphone)) - { /* If we have a microphone that we need to pause... */ - bool result = wasapi_microphone_stop_mic(wasapi, wasapi->microphone); - } - - wasapi->running = false; - - return true; -} - -static bool wasapi_microphone_alive(void *driver_context) -{ - wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; - - return wasapi->running; -} - static void wasapi_microphone_set_nonblock_state(void *driver_context, bool nonblock) { wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; @@ -512,9 +294,9 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device BYTE *dest = NULL; bool float_format = settings->bools.microphone_wasapi_float_format; bool exclusive_mode = settings->bools.microphone_wasapi_exclusive_mode; - int sh_buffer_length = settings->ints.microphone_wasapi_sh_buffer_length; - wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; + unsigned sh_buffer_length = settings->uints.microphone_wasapi_sh_buffer_length; wasapi_microphone_handle_t *microphone = calloc(1, sizeof(wasapi_microphone_handle_t)); + (void)driver_context; if (!microphone) return NULL; @@ -571,9 +353,9 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device RARCH_LOG("[WASAPI]: Intermediate exclusive-mode capture buffer length is %u frames (%.1fms, %u bytes).\n", frame_count, (double)frame_count * 1000.0 / rate, microphone->engine_buffer_size); } - else if (sh_buffer_length) + else { - if (sh_buffer_length < 0) + if (sh_buffer_length <= 0) { /* If the user selected the "default" shared buffer length... */ hr = _IAudioClient_GetDevicePeriod(microphone->client, &dev_period, NULL); if (FAILED(hr)) @@ -591,10 +373,6 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device RARCH_LOG("[WASAPI]: Intermediate shared-mode capture buffer length is %u frames (%.1fms, %u bytes).\n", sh_buffer_length, (double)sh_buffer_length * 1000.0 / rate, sh_buffer_length * microphone->frame_size); } - else - { - RARCH_LOG("[WASAPI]: Intermediate capture buffer is off.\n"); - } microphone->read_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!microphone->read_event) @@ -633,7 +411,6 @@ static void *wasapi_microphone_open_mic(void *driver_context, const char *device goto error; } - wasapi->microphone = microphone; if (new_rate) { /* The rate was (possibly) modified when we initialized the client */ *new_rate = rate; @@ -678,8 +455,6 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c free(microphone->device_name); free(microphone); - wasapi->microphone = NULL; - ir = WaitForSingleObject(write_event, 20); if (ir == WAIT_FAILED) { @@ -697,45 +472,36 @@ static void wasapi_microphone_close_mic(void *driver_context, void *microphone_c static bool wasapi_microphone_start_mic(void *driver_context, void *microphone_context) { - wasapi_microphone_t *wasapi = (wasapi_microphone_t*)driver_context; wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t*)microphone_context; + HRESULT hr; + (void)driver_context; - if (!wasapi || !microphone) + if (!microphone) return false; - if (wasapi_microphone_alive(wasapi)) - { /* If the microphone should be active... */ - HRESULT hr = _IAudioClient_Start(microphone->client); + hr = _IAudioClient_Start(microphone->client); - if (SUCCEEDED(hr) || hr == AUDCLNT_E_NOT_STOPPED) - { /* Starting an active microphone is not an error */ - - microphone->running = true; - } - else - { - RARCH_ERR("[WASAPI mic]: Failed to start capture device \"%s\"'s IAudioClient: %s\n", - microphone->device_name, hresult_name(hr)); - microphone->running = false; - } + if (SUCCEEDED(hr) || hr == AUDCLNT_E_NOT_STOPPED) + { /* Starting an active microphone is not an error */ + microphone->running = true; } else { - microphone->running = true; - /* The microphone will resume next time the driver itself is resumed */ + RARCH_ERR("[WASAPI mic]: Failed to start capture device \"%s\"'s IAudioClient: %s\n", + microphone->device_name, hresult_name(hr)); + microphone->running = false; } - return microphone->running; } static bool wasapi_microphone_stop_mic(void *driver_context, void *microphone_context) { - wasapi_microphone_t *w = (wasapi_microphone_t*)driver_context; wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t*)microphone_context; HRESULT hr; + (void)driver_context; - if (!w || !microphone) + if (!microphone) return false; hr = _IAudioClient_Stop(microphone->client); @@ -779,7 +545,7 @@ static bool wasapi_microphone_use_float(const void *driver_context, const void * wasapi_microphone_handle_t *microphone = (wasapi_microphone_handle_t *)microphone_context; (void)driver_context; - if (!driver_context || !microphone) + if (!microphone) return false; return microphone->frame_size == sizeof(float); From 8a77d8939dbadec79720ef32332e4c5a59cada56 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:21:33 -0500 Subject: [PATCH 220/309] Make microphone_wasapi_sh_buffer_length a uint, not an int - It won't be negative anymore - 0 now represents the default value --- configuration.c | 4 +++- configuration.h | 2 +- menu/menu_displaylist.c | 2 +- menu/menu_setting.c | 20 ++++++++++++++++---- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/configuration.c b/configuration.c index a59e1eb9568a..53f09742e6fa 100644 --- a/configuration.c +++ b/configuration.c @@ -2251,6 +2251,9 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("audio_resampler_quality", &settings->uints.audio_resampler_quality, true, DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL, false); SETTING_UINT("audio_block_frames", &settings->uints.audio_block_frames, true, 0, false); SETTING_UINT("microphone_block_frames", &settings->uints.microphone_block_frames, true, 0, false); +#ifdef HAVE_WASAPI + SETTING_UINT("microphone_wasapi_sh_buffer_length", &settings->uints.microphone_wasapi_sh_buffer_length, true, DEFAULT_WASAPI_SH_BUFFER_LENGTH, false); +#endif #ifdef ANDROID SETTING_UINT("input_block_timeout", &settings->uints.input_block_timeout, true, 0, false); #endif @@ -2501,7 +2504,6 @@ static struct config_int_setting *populate_settings_int( #endif #ifdef HAVE_WASAPI SETTING_INT("audio_wasapi_sh_buffer_length", &settings->ints.audio_wasapi_sh_buffer_length, true, DEFAULT_WASAPI_SH_BUFFER_LENGTH, false); - SETTING_INT("microphone_wasapi_sh_buffer_length", &settings->ints.microphone_wasapi_sh_buffer_length, true, DEFAULT_WASAPI_SH_BUFFER_LENGTH, false); #endif SETTING_INT("crt_switch_center_adjust", &settings->ints.crt_switch_center_adjust, false, DEFAULT_CRT_SWITCH_CENTER_ADJUST, false); SETTING_INT("crt_switch_porch_adjust", &settings->ints.crt_switch_porch_adjust, false, DEFAULT_CRT_SWITCH_PORCH_ADJUST, false); diff --git a/configuration.h b/configuration.h index d2942929666b..f17d76e02e5f 100644 --- a/configuration.h +++ b/configuration.h @@ -105,7 +105,6 @@ typedef struct settings int location_update_interval_distance; int state_slot; int audio_wasapi_sh_buffer_length; - int microphone_wasapi_sh_buffer_length; int crt_switch_center_adjust; int crt_switch_porch_adjust; #ifdef HAVE_VULKAN @@ -162,6 +161,7 @@ typedef struct settings unsigned microphone_block_frames; unsigned audio_latency; unsigned microphone_latency; + unsigned microphone_wasapi_sh_buffer_length; unsigned fps_update_interval; unsigned memory_update_interval; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 86c5df6658c4..3fb0e44d199f 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6708,7 +6708,7 @@ unsigned menu_displaylist_build_list( count++; if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, MENU_ENUM_LABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, - PARSE_ONLY_INT, false) == 0) + PARSE_ONLY_UINT, false) == 0) count++; break; case DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST: diff --git a/menu/menu_setting.c b/menu/menu_setting.c index a52c2aa29d90..e0ccb43551b6 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5093,6 +5093,18 @@ static void setting_get_string_representation_int_audio_wasapi_sh_buffer_length( else strlcpy(s, "Auto", len); } + +static void setting_get_string_representation_uint_microphone_wasapi_sh_buffer_length(rarch_setting_t *setting, + char *s, size_t len) +{ + if (!setting) + return; + + if (*setting->value.target.unsigned_integer > 0) + snprintf(s, len, "%u", *setting->value.target.unsigned_integer); + else + strlcpy(s, "Auto", len); +} #endif static void setting_get_string_representation_crt_switch_resolution_super( @@ -14034,9 +14046,9 @@ static bool setting_append_list( SD_FLAG_NONE ); - CONFIG_INT( + CONFIG_UINT( list, list_info, - &settings->ints.microphone_wasapi_sh_buffer_length, + &settings->uints.microphone_wasapi_sh_buffer_length, MENU_ENUM_LABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, MENU_ENUM_LABEL_VALUE_MICROPHONE_WASAPI_SH_BUFFER_LENGTH, DEFAULT_WASAPI_SH_BUFFER_LENGTH, @@ -14045,10 +14057,10 @@ static bool setting_append_list( parent_group, general_write_handler, general_read_handler); - menu_settings_list_current_add_range(list, list_info, -16.0f, 0.0f, 16.0f, true, false); + menu_settings_list_current_add_range(list, list_info, 0.0f, 0.0f, 16.0f, true, false); SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED); (*list)[list_info->index - 1].get_string_representation = - &setting_get_string_representation_int_audio_wasapi_sh_buffer_length; + &setting_get_string_representation_uint_microphone_wasapi_sh_buffer_length; } #endif From 6875c95e15d245e2fb2cea65c0cc88a027741b2f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:23:20 -0500 Subject: [PATCH 221/309] Make the microphone frontend more robust - Improve documentation for how various functions should be implemented - Closes all microphones before freeing the driver (so backends don't have to) - Tracks the enabled state of each microphone, so backends don't have to (but they still can) --- microphone/microphone_driver.c | 260 ++++++++++++++++++++------------- microphone/microphone_driver.h | 62 ++++++-- 2 files changed, 210 insertions(+), 112 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index a18a3e8ff8ff..d3505c5fe6cf 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -23,8 +23,9 @@ #include "audio/conversion/s16_to_float.h" #include "audio/conversion/float_to_s16.h" #include "list_special.h" +#include "retro_assert.h" -static microphone_driver_state_t mic_driver_st = {0}; /* double alignment */ +static microphone_driver_state_t mic_driver_st; microphone_driver_t microphone_null = { NULL, @@ -65,6 +66,70 @@ unsigned mic_driver_get_sample_size(const retro_microphone_t *microphone) return (microphone->flags & MICROPHONE_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t); } +static void mic_driver_open_mic_internal(retro_microphone_t* microphone); +bool microphone_driver_start(bool is_shutdown) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + retro_microphone_t *microphone = &mic_st->microphone; + + if (microphone->flags & MICROPHONE_FLAG_ACTIVE) + { /* If there's an opened microphone that the core turned on... */ + + if (microphone->flags & MICROPHONE_FLAG_PENDING) + { /* If this microphone was requested before the driver was ready...*/ + retro_assert(microphone->microphone_context == NULL); + /* The microphone context shouldn't have been created yet */ + + /* Now that the driver and driver context are ready, let's initialize the mic */ + mic_driver_open_mic_internal(microphone); + + if (microphone->microphone_context) + { + /* open_mic_internal will start the microphone if it's enabled */ + RARCH_DBG("[Microphone]: Initialized a previously-pending microphone\n"); + } + else + { + RARCH_ERR("[Microphone]: Failed to initialize a previously pending microphone; microphone will not be used\n"); + + microphone_driver_close_mic(microphone); + } + } + else + { /* The mic was already created, so let's just unpause it */ + microphone_driver_set_mic_state(microphone, true); + + RARCH_DBG("[Microphone]: Started a microphone that was enabled when the driver was last stopped\n"); + } + } + + RARCH_DBG("[Microphone]: Started microphone driver \"%s\" (is_shutdown=%s)\n", + mic_st->driver->ident, + is_shutdown ? "true" : "false"); + + return true; +} + +bool microphone_driver_stop(void) +{ + microphone_driver_state_t *mic_st = &mic_driver_st; + retro_microphone_t *microphone = &mic_st->microphone; + bool result = true; + + if ((microphone->flags & MICROPHONE_FLAG_ACTIVE) + && (microphone->flags & MICROPHONE_FLAG_ENABLED) + && !(microphone->flags & MICROPHONE_FLAG_PENDING)) + { /* If there's an opened microphone that the core turned on and received... */ + + result = mic_st->driver->stop_mic(mic_st->driver_context, microphone->microphone_context); + } + /* If the mic is pending, then we don't need to do anything. */ + + RARCH_DBG("[Audio]: Stopped microphone driver \"%s\"\n", mic_st->driver->ident); + + return result; +} + /** * config_get_microphone_driver_options: * @@ -122,13 +187,10 @@ static void mic_driver_microphone_handle_init(retro_microphone_t *microphone) { if (microphone) { - microphone->microphone_context = NULL; - microphone->pending_enabled = false; - microphone->active = true; - microphone->sample_buffer = NULL; - microphone->sample_buffer_length = 0; - microphone->most_recent_copy_length = 0; - microphone->error = false; + microphone->microphone_context = NULL; + microphone->flags = MICROPHONE_FLAG_ACTIVE; + microphone->sample_buffer = NULL; + microphone->sample_buffer_length = 0; } } @@ -160,8 +222,6 @@ static void mic_driver_microphone_handle_free(retro_microphone_t *microphone) /* Do NOT free the microphone handle itself! It's allocated statically! */ } -static void mic_driver_open_mic_internal(retro_microphone_t* microphone); - bool microphone_driver_init_internal(void *settings_data) { settings_t *settings = (settings_t*)settings_data; @@ -206,23 +266,11 @@ bool microphone_driver_init_internal(void *settings_data) RARCH_LOG("[Microphone]: Initialized microphone driver\n"); - mic_driver_st.flags |= MICROPHONE_DRIVER_FLAG_ACTIVE; + /* The mic driver was initialized, now we're ready to open mics */ + mic_st->flags |= MICROPHONE_DRIVER_FLAG_ACTIVE; - if ( mic_driver_st.microphone.active && - !mic_driver_st.microphone.microphone_context) - { /* If the core requested a microphone before the driver was able to provide one...*/ - /* Now that the driver and driver context are ready, let's initialize the mic */ - mic_driver_open_mic_internal(&mic_driver_st.microphone); - - if (mic_driver_st.microphone.microphone_context) - RARCH_DBG("[Microphone]: Initialized a previously-pending microphone\n"); - else - { - RARCH_ERR("[Microphone]: Failed to initialize a previously pending microphone; microphone will not be used\n"); - - microphone_driver_close_mic(&mic_driver_st.microphone); - } - } + if (!microphone_driver_start(false)) + goto error; return true; @@ -251,11 +299,10 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) float slowmotion_ratio = settings->floats.slowmotion_ratio; size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; - if (!microphone || !mic_driver) + if (!microphone || !mic_driver || !(mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE)) return; microphone->sample_buffer_length = insamples_max * sizeof(int16_t); - microphone->error = false; microphone->sample_buffer = (int16_t*)memalign_alloc(64, microphone->sample_buffer_length); @@ -272,7 +319,7 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) if (!microphone->microphone_context) goto error; - microphone_driver_set_mic_state(microphone, microphone->pending_enabled); + microphone_driver_set_mic_state(microphone, microphone->flags & MICROPHONE_FLAG_ENABLED); if (actual_sample_rate != 0) { @@ -288,6 +335,7 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) microphone->flags |= MICROPHONE_FLAG_USE_FLOAT; } + microphone->flags &= ~MICROPHONE_FLAG_PENDING; RARCH_LOG("[Microphone]: Initialized microphone\n"); return; error: @@ -316,39 +364,62 @@ bool microphone_driver_set_mic_state(retro_microphone_t *microphone, bool state) const microphone_driver_t *mic_driver = mic_st->driver; void *driver_context = mic_st->driver_context; - if (!microphone || !microphone->active || !mic_driver || !mic_driver->start_mic || !mic_driver->stop_mic || !mic_driver->mic_alive) + if (!microphone + || !(microphone->flags & MICROPHONE_FLAG_ACTIVE) + || !mic_driver + || !mic_driver->start_mic + || !mic_driver->stop_mic) return false; + /* If the provided microphone was null or invalid, or the driver is incomplete, stop. */ if (driver_context && microphone->microphone_context) { /* If the driver is initialized... */ bool success; if (state) { /* If we want to enable this mic... */ - success = mic_driver->mic_alive(driver_context, microphone->microphone_context) - || mic_driver->start_mic(driver_context, microphone->microphone_context); + success = mic_driver->start_mic(driver_context, microphone->microphone_context); + /* Enable the mic. (Enabling an active mic is a successful noop.) */ - /* Enable the mic, or do nothing and report success if it's already enabled. */ + if (success) + { + microphone->flags |= MICROPHONE_FLAG_ENABLED; + RARCH_LOG("[Microphone]: Enabled microphone\n"); + } + else + { + RARCH_ERR("[Microphone]: Failed to enable microphone\n"); + } } else { /* If we want to pause this mic... */ - success = !mic_driver->mic_alive(driver_context, microphone->microphone_context) - || mic_driver->stop_mic(driver_context, microphone->microphone_context); - /* Disable the mic, or do nothing and report success if it's already disabled. */ - } + success = mic_driver->stop_mic(driver_context, microphone->microphone_context); + /* Disable the mic. (If the mic is already stopped, disabling it should still be successful.) */ - if (success) - RARCH_DBG("[Microphone]: Set initialized mic state to %s\n", - state ? "enabled" : "disabled"); - else - RARCH_ERR("[Microphone]: Failed to set initialized mic state to %s\n", - state ? "enabled" : "disabled"); + if (success) + { + microphone->flags &= ~MICROPHONE_FLAG_ENABLED; + RARCH_LOG("[Microphone]: Disabled microphone\n"); + } + else + { + RARCH_ERR("[Microphone]: Failed to disable microphone\n"); + } + } return success; } else { /* The driver's not ready yet, so we'll make a note * of what the mic's state should be */ - microphone->pending_enabled = state; + if (state) + { + microphone->flags |= MICROPHONE_FLAG_ENABLED; + } + else + { + microphone->flags &= ~MICROPHONE_FLAG_ENABLED; + } + RARCH_DBG("[Microphone]: Set pending mic state to %s\n", state ? "enabled" : "disabled"); return true; @@ -358,22 +429,10 @@ bool microphone_driver_set_mic_state(retro_microphone_t *microphone, bool state) bool microphone_driver_get_mic_state(const retro_microphone_t *microphone) { - microphone_driver_state_t *mic_st = &mic_driver_st; - const microphone_driver_t *mic_driver = mic_st->driver; - void *driver_context = mic_st->driver_context; - - if (!microphone || !microphone->active || !mic_driver || !mic_driver->mic_alive) + if (!microphone || !(microphone->flags & MICROPHONE_FLAG_ACTIVE)) return false; - if (driver_context && microphone->microphone_context) - { /* If the driver is initialized... */ - return mic_driver->mic_alive(driver_context, microphone->microphone_context); - } - else - { /* The driver's not ready yet, - * so we'll use that note we made of the mic's active state */ - return microphone->pending_enabled; - } + return microphone->flags & MICROPHONE_FLAG_ENABLED; } /** @@ -412,14 +471,17 @@ static void microphone_driver_flush( mic_st->driver_context && /* ...and the mic driver is initialized... */ mic_st->input_samples_buf && /* ...with scratch space... */ microphone && - microphone->active && /* ...and the mic itself is initialized... */ + (microphone->flags & MICROPHONE_FLAG_ACTIVE) && /* ...and the mic itself is initialized... */ + (microphone->flags & MICROPHONE_FLAG_ENABLED) && /* ...and enabled... */ + !(microphone->flags & MICROPHONE_FLAG_PENDING) && + !(microphone->flags & MICROPHONE_FLAG_SUSPENDED) && microphone->microphone_context && /* ...and ready... */ microphone->sample_buffer && microphone->sample_buffer_length && /* ...with a non-empty sample buffer... */ mic_st->driver->read && /* ...and valid function pointers... */ mic_st->driver->mic_alive && - mic_st->driver->mic_alive( /* ...and it's enabled... */ + mic_st->driver->mic_alive( /* ...and it's running... */ mic_st->driver_context, microphone->microphone_context)) { @@ -433,16 +495,9 @@ static void microphone_driver_flush( bytes_to_read); /* First, get the most recent mic data */ - if (bytes_read < 0) - { /* If there was an error... */ - microphone->error = true; - return; - } - else if (bytes_read == 0) - { - microphone->error = false; + if (bytes_read <= 0) return; - } + if (microphone->flags & MICROPHONE_FLAG_USE_FLOAT) { @@ -466,25 +521,32 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size microphone_driver_state_t *mic_st = &mic_driver_st; if (!(mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) - || num_frames == 0 || !frames || !microphone - || !microphone->active - || !microphone_driver_get_mic_state(microphone) + || !(microphone->flags & MICROPHONE_FLAG_ACTIVE) ) - { /* If the driver is suspended, or the core didn't actually ask for frames, - * or the microphone is disabled... */ + { /* If the driver, microphone, or provided arguments aren't valid... */ return -1; } - if (!(mic_st->driver_context && microphone->microphone_context)) - { /* If the microphone isn't ready... */ + if (num_frames == 0) + /* If the core didn't actually ask for any frames... */ + return 0; + + if ((microphone->flags & MICROPHONE_FLAG_PENDING) + || (microphone->flags & MICROPHONE_FLAG_SUSPENDED) + || !(microphone->flags & MICROPHONE_FLAG_ENABLED) + ) + { /* If the microphone is pending, suspended, or disabled... */ memset(frames, 0, num_frames * sizeof(*frames)); - return num_frames; /* Not an error */ + return (int)num_frames; + /* ...then copy silence to the provided buffer. Not an error */ } runloop_flags = runloop_get_flags(); + // TODO: Check if the mic is alive here + do { size_t frames_to_read = @@ -540,18 +602,19 @@ retro_microphone_t *microphone_driver_open_mic(void) * don't forget, the core can ask for a mic * before the audio driver is ready to create one. */ RARCH_WARN("[Microphone]: Refused to open microphone because it's disabled in the settings\n"); + // TODO: Log a message to the on-screen display return NULL; } - if (mic_st->microphone.active) + if (mic_st->microphone.flags & MICROPHONE_FLAG_ACTIVE) { /* If the core has requested a second microphone... */ RARCH_ERR("[Microphone]: Failed to open a second microphone, frontend only supports one at a time right now\n"); - if (mic_st->microphone.microphone_context) - /* If that mic is initialized... */ - RARCH_ERR("[Microphone]: An initialized microphone exists\n"); - else - /* That mic is pending */ + if (mic_st->microphone.flags & MICROPHONE_FLAG_PENDING) + /* If that mic is pending... */ RARCH_ERR("[Microphone]: A microphone is pending initialization\n"); + else + /* That mic is initialized */ + RARCH_ERR("[Microphone]: An initialized microphone exists\n"); return NULL; } @@ -561,7 +624,7 @@ retro_microphone_t *microphone_driver_open_mic(void) * But the user still wants a handle, so we'll give them one. */ mic_driver_microphone_handle_init(&mic_st->microphone); - /* If context is NULL, the handle won't have a valid microphone context (but we'll create one later) */ + /* If driver_context is NULL, the handle won't have a valid microphone context (but we'll create one later) */ if (driver_context) { /* If the microphone driver is ready to open a microphone... */ @@ -574,12 +637,14 @@ retro_microphone_t *microphone_driver_open_mic(void) } else { /* If the driver isn't ready to create a microphone... */ + mic_st->microphone.flags |= MICROPHONE_FLAG_PENDING; RARCH_LOG("[Microphone]: Microphone requested before driver context was ready; deferring initialization\n"); } return &mic_st->microphone; error: mic_driver_microphone_handle_free(&mic_st->microphone); + /* This function cleans up any resources and unsets all flags */ return NULL; } @@ -589,13 +654,6 @@ retro_microphone_t *microphone_driver_open_mic(void) static bool microphone_driver_close_microphones(void) { microphone_driver_state_t *mic_st = &mic_driver_st; - const microphone_driver_t *mic_driver = mic_st->driver; - - if ( !mic_driver || - !mic_driver->close_mic || - !mic_st->driver_context || - !mic_st->microphone.active) - return false; microphone_driver_close_mic(&mic_st->microphone); @@ -617,13 +675,17 @@ static bool microphone_driver_free_devices_list(void) return true; } -static bool microphone_driver_deinit_internal(void) +bool microphone_driver_deinit(void) { microphone_driver_state_t *mic_st = &mic_driver_st; - if (mic_st->driver && mic_st->driver->free) - { + const microphone_driver_t *driver = mic_st->driver; + + microphone_driver_free_devices_list(); + microphone_driver_close_microphones(); + + if (driver && driver->free) { if (mic_st->driver_context) - mic_st->driver->free(mic_st->driver_context); + driver->free(mic_st->driver_context); mic_st->driver_context = NULL; } @@ -640,14 +702,6 @@ static bool microphone_driver_deinit_internal(void) return true; } -bool microphone_driver_deinit(void) -{ - settings_t *settings = config_get_ptr(); - microphone_driver_free_devices_list(); - microphone_driver_close_microphones(); - return microphone_driver_deinit_internal(); -} - bool microphone_driver_get_devices_list(void **data) { struct string_list**ptr = (struct string_list**)data; diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index 72199a828d39..f0d0cc965951 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -171,8 +171,9 @@ typedef struct microphone_driver /** - * Frees the driver context and closes all microphones. - * There is no need to call close_mic after calling this function. + * Frees the driver context. + * There is no need to close the microphones in this function, + * the microphone system will do that before calling this. * Does nothing if \c driver_context is \c NULL. * * @param driver_context Pointer to the microphone driver context. @@ -215,7 +216,7 @@ typedef struct microphone_driver ssize_t (*read)(void *driver_context, void *mic_context, void *buffer, size_t buffer_size); /** - * Sets the nonblocking state of all microphones. + * Sets the nonblocking state of the driver. * Primarily used for fast-forwarding. * If the driver is in blocking mode (the default), * \c ::read() will block the current thread @@ -275,6 +276,9 @@ typedef struct microphone_driver * but driver implementations do not need to concern themselves with that; * when the driver is ready, it will call this function. * + * Opened microphones must *not* be activated, + * i.e. \c mic_alive on a newly-opened microphone should return \c false. + * * @param data Handle to the driver context * that was originally returned by ::init. * @param device A specific device name (or other options) @@ -321,9 +325,8 @@ typedef struct microphone_driver /** * Returns the active state of the provided microphone. - * Note that this describes the user-facing state of the microphone; - * this function can still return \c true if the mic driver is paused - * or muted. + * This is the state of the device itself, + * not of any user-decided state. * * @param[in] driver_context Pointer to the driver context. * Will be the value that was returned by \c ::init(). @@ -334,10 +337,43 @@ typedef struct microphone_driver */ bool (*mic_alive)(const void *driver_context, const void *mic_context); + /** + * Begins capture activity on the provided microphone. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] mic_context Pointer to a particular microphone's context. + * Will be a value that was returned by \c ::open_mic(). + * @return \c true if the microphone was successfully started + * or if it was already running. \c false if there was an error. + */ bool (*start_mic)(void *driver_context, void *microphone_context); + /** + * Pauses capture activity on the provided microphone. + * This function must not deallocate the microphone. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] mic_context Pointer to a particular microphone's context. + * Will be a value that was returned by \c ::open_mic(). + * @return \c true if the microphone was successfully paused + * or if it was already stopped. \c false if there was an error. + */ bool (*stop_mic)(void *driver_context, void *microphone_context); + /** + * Queries whether this microphone captures floating-point samples, + * as opposed to 16-bit integer samples. + * + * Optional; if not provided, then \c int16_t samples are assumed. + * + * @param[in] driver_context Pointer to the driver context. + * Will be the value that was returned by \c ::init(). + * @param[in] mic_context Pointer to a particular microphone's context. + * Will be a value that was returned by \c ::open_mic(). + * @return \c true if this microphone provides floating-point samples. + */ bool (*mic_use_float)(const void *driver_context, const void *microphone_context); } microphone_driver_t; @@ -381,17 +417,25 @@ typedef struct } microphone_driver_state_t; /** - * Starts the active microphone driver. + * Starts all enabled microphones, + * and opens all pending microphones. + * * @param is_shutdown TODO - * @return \c true if the configured driver was started, + * @return \c true if the configured driver was started + * and pending microphones opened, * \c false if there was an error. */ bool microphone_driver_start(bool is_shutdown); /** - * Stops the active microphone driver. + * Stops all enabled microphones. + * It is not an error to call this function + * if the mic driver is already stopped, + * or if there are no open microphones. + * * Microphones will not receive any input * until \c microphone_driver_start is called again. + * * @return \c true if the driver was stopped, * \c false if there was an error. */ From 980ba3e359b34a456b456d9960aec297d64bc508 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 11:23:35 -0500 Subject: [PATCH 222/309] Stop the mic driver in core_unload_game --- runloop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runloop.c b/runloop.c index 3a699f38da35..871640d91f03 100644 --- a/runloop.c +++ b/runloop.c @@ -698,6 +698,7 @@ void runloop_runtime_log_deinit( static bool runloop_clear_all_thread_waits( unsigned clear_threads, void *data) { + /* Does this need to treat the microphone driver the same way? */ if (clear_threads > 0) audio_driver_start(false); else @@ -3779,6 +3780,7 @@ static bool core_unload_game(void) } audio_driver_stop(); + microphone_driver_stop(); return true; } From 20210c1f39514ad47e835ebd4d27bdf3f2483c21 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 13:08:27 -0500 Subject: [PATCH 223/309] Ensure mic support is compatible with the revised menu code --- intl/msg_hash_lbl.h | 4 ++ menu/cbs/menu_cbs_deferred_push.c | 2 + menu/cbs/menu_cbs_ok.c | 34 ++++++++++ menu/cbs/menu_cbs_title.c | 1 + menu/menu_cbs.h | 1 + menu/menu_displaylist.c | 103 ++++++++++++++++++++++++++++++ menu/menu_displaylist.h | 1 + menu/menu_driver.h | 1 + menu/menu_setting.c | 98 +++++++++++++++++++++++++++- msg_hash.h | 2 + 10 files changed, 246 insertions(+), 1 deletion(-) diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 3ab5c6cde0bc..1c0471e4ca99 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -995,6 +995,10 @@ MSG_HASH( MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_AUDIO_DEVICE, "deferred_dropdown_box_list_audio_device" ) +MSG_HASH( + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE, + "deferred_dropdown_box_list_microphone_device" + ) MSG_HASH( MENU_ENUM_LABEL_DEFERRED_CONFIGURATIONS_LIST, "deferred_configurations_list" diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c index 14c2d19a1e48..39b481e596c2 100644 --- a/menu/cbs/menu_cbs_deferred_push.c +++ b/menu/cbs/menu_cbs_deferred_push.c @@ -634,6 +634,7 @@ GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list, PUSH_DEFAULT, DIS GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_special, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_SPECIAL) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_resolution, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_RESOLUTION) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_audio_device, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_AUDIO_DEVICE) +GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_microphone_device, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_MICROPHONE_DEVICE) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_video_shader_num_passes, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_VIDEO_SHADER_NUM_PASSES) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_shader_parameter, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_VIDEO_SHADER_PARAMETER) GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_shader_preset_parameter, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_VIDEO_SHADER_PRESET_PARAMETER) @@ -677,6 +678,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label( {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_SPECIAL, deferred_push_dropdown_box_list_special}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_RESOLUTION, deferred_push_dropdown_box_list_resolution}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_AUDIO_DEVICE, deferred_push_dropdown_box_list_audio_device}, + {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE, deferred_push_dropdown_box_list_microphone_device}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_NUM_PASSES, deferred_push_dropdown_box_list_video_shader_num_passes}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_PARAMETER, deferred_push_dropdown_box_list_shader_parameter}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_PRESET_PARAMETER, deferred_push_dropdown_box_list_shader_preset_parameter}, diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 85effaf11f54..31d7d0e679a7 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -281,6 +281,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl) return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_RESOLUTION; case ACTION_OK_DL_DROPDOWN_BOX_LIST_AUDIO_DEVICE: return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_AUDIO_DEVICE; + case ACTION_OK_DL_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE: + return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE; case ACTION_OK_DL_DROPDOWN_BOX_LIST_PLAYLIST_DEFAULT_CORE: return MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_PLAYLIST_DEFAULT_CORE; case ACTION_OK_DL_DROPDOWN_BOX_LIST_PLAYLIST_LABEL_DISPLAY_MODE: @@ -844,6 +846,15 @@ int generic_action_ok_displaylist_push(const char *path, info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_AUDIO_DEVICE; dl_type = DISPLAYLIST_GENERIC; break; + case ACTION_OK_DL_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE: + info.type = type; + info.directory_ptr = idx; + info_path = path; + info_label = msg_hash_to_str( + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE); + info.enum_idx = MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE; + dl_type = DISPLAYLIST_GENERIC; + break; #ifdef HAVE_NETWORKING case ACTION_OK_DL_DROPDOWN_BOX_LIST_NETPLAY_MITM_SERVER: info.type = type; @@ -6845,6 +6856,26 @@ static int action_ok_push_dropdown_item_audio_device(const char *path, return action_cancel_pop_default(NULL, NULL, 0, 0); } +static int action_ok_push_dropdown_item_microphone_device(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + const char *menu_path = NULL; + enum msg_hash_enums enum_idx; + rarch_setting_t *setting; + menu_entries_get_last_stack(&menu_path, NULL, NULL, NULL, NULL); + enum_idx = (enum msg_hash_enums)atoi(menu_path); + setting = menu_setting_find_enum(enum_idx); + + if (!setting) + return -1; + + strlcpy(setting->value.target.string, label, setting->size); + + command_event(CMD_EVENT_MICROPHONE_REINIT, NULL); + + return action_cancel_pop_default(NULL, NULL, 0, 0); +} + static int action_ok_push_dropdown_item_input_device_type(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -8786,6 +8817,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs, case MENU_SETTING_DROPDOWN_ITEM_AUDIO_DEVICE: BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_audio_device); break; + case MENU_SETTING_DROPDOWN_ITEM_MICROPHONE_DEVICE: + BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_microphone_device); + break; #ifdef HAVE_NETWORKING case MENU_SETTING_DROPDOWN_ITEM_NETPLAY_MITM_SERVER: BIND_ACTION_OK(cbs, action_ok_push_dropdown_item_netplay_mitm_server); diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index 177315d07963..09924326b329 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -1810,6 +1810,7 @@ int menu_cbs_init_bind_title(menu_file_list_cbs_t *cbs, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_SPECIAL, action_get_title_dropdown_item}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_RESOLUTION, action_get_title_dropdown_resolution_item}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_AUDIO_DEVICE, action_get_title_dropdown_item}, + {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE, action_get_title_dropdown_item}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_PARAMETER, action_get_title_dropdown_video_shader_parameter_item}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_PRESET_PARAMETER, action_get_title_dropdown_video_shader_preset_parameter_item}, {MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_NUM_PASSES, action_get_title_dropdown_video_shader_num_pass_item}, diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h index eaf8e7a17e11..c5a6f0719421 100644 --- a/menu/menu_cbs.h +++ b/menu/menu_cbs.h @@ -40,6 +40,7 @@ enum ACTION_OK_DL_DROPDOWN_BOX_LIST_SPECIAL, ACTION_OK_DL_DROPDOWN_BOX_LIST_RESOLUTION, ACTION_OK_DL_DROPDOWN_BOX_LIST_AUDIO_DEVICE, + ACTION_OK_DL_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE, ACTION_OK_DL_DROPDOWN_BOX_LIST_SHADER_PARAMETER, ACTION_OK_DL_DROPDOWN_BOX_LIST_SHADER_PRESET_PARAMETER, ACTION_OK_DL_DROPDOWN_BOX_LIST_VIDEO_SHADER_NUM_PASSES, diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index a6b2654485d2..2c7fae8a0588 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -123,6 +123,7 @@ #include "../core_backup.h" #include "../misc/cpufreq/cpufreq.h" #include "../input/input_remapping.h" +#include "microphone/microphone_driver.h" #ifdef HAVE_MIST #include "../steam/steam.h" @@ -5154,6 +5155,102 @@ static int menu_displaylist_parse_audio_device_list( return count; } +static int menu_displaylist_parse_microphone_device_list( + menu_displaylist_info_t *info, settings_t *settings) +{ + enum msg_hash_enums enum_idx = (enum msg_hash_enums)atoi(info->path); + rarch_setting_t *setting = menu_setting_find_enum(enum_idx); + size_t menu_index = 0; + unsigned count = 0; + int i = -1; + int mic_device_index = -1; + struct string_list *ptr = NULL; + + if (!settings || !setting) + goto end; + + if (!microphone_driver_get_devices_list((void**)&ptr)) + goto end; + + if (!ptr) + goto end; + + /* Get index in the string list */ + mic_device_index = string_list_find_elem(ptr, setting->value.target.string) - 1; + + /* Add "Default" */ + if (i == -1) + { + bool add = false; + + if (menu_entries_append(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE), + "", + MENU_ENUM_LABEL_MICROPHONE_DEVICE_LIST, + MENU_SETTING_DROPDOWN_ITEM_MICROPHONE_DEVICE, + 0, i, NULL)) + add = true; + + if (add) + { + /* Add checkmark if input is currently + * mapped to this entry */ + if (mic_device_index == i) + { + menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info->list->list[menu_index].actiondata; + if (cbs) + cbs->checked = true; + menu_navigation_set_selection(menu_index); + } + + count++; + menu_index++; + } + } + + for (i = 0; i < ptr->size; i++) + { + bool add = false; + + /* Add menu entry */ + if (menu_entries_append(info->list, + ptr->elems[i].data, + ptr->elems[i].data, + MENU_ENUM_LABEL_MICROPHONE_DEVICE_LIST, + MENU_SETTING_DROPDOWN_ITEM_MICROPHONE_DEVICE, + 0, i, NULL)) + add = true; + + if (add) + { + /* Add checkmark if input is currently + * mapped to this entry */ + if (mic_device_index == i) + { + menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info->list->list[menu_index].actiondata; + if (cbs) + cbs->checked = true; + menu_navigation_set_selection(menu_index); + } + + count++; + menu_index++; + } + } + +end: + /* Fallback */ + if (count == 0) + if (menu_entries_append(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ENTRIES_TO_DISPLAY), + msg_hash_to_str(MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY), + MENU_ENUM_LABEL_NO_ENTRIES_TO_DISPLAY, + FILE_TYPE_NONE, 0, 0, NULL)) + count++; + + return count; +} + static int menu_displaylist_parse_input_device_type_list( menu_displaylist_info_t *info, settings_t *settings) { @@ -13320,6 +13417,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, info->flags |= MD_FLAG_NEED_REFRESH | MD_FLAG_NEED_PUSH; break; + case DISPLAYLIST_DROPDOWN_LIST_MICROPHONE_DEVICE: + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); + count = menu_displaylist_parse_microphone_device_list(info, settings); + info->flags |= MD_FLAG_NEED_REFRESH + | MD_FLAG_NEED_PUSH; + break; #ifdef HAVE_NETWORKING case DISPLAYLIST_DROPDOWN_LIST_NETPLAY_MITM_SERVER: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h index 1ac16777045a..f6725f1b854f 100644 --- a/menu/menu_displaylist.h +++ b/menu/menu_displaylist.h @@ -58,6 +58,7 @@ enum menu_displaylist_ctl_state DISPLAYLIST_DROPDOWN_LIST_SPECIAL, DISPLAYLIST_DROPDOWN_LIST_RESOLUTION, DISPLAYLIST_DROPDOWN_LIST_AUDIO_DEVICE, + DISPLAYLIST_DROPDOWN_LIST_MICROPHONE_DEVICE, DISPLAYLIST_DROPDOWN_LIST_VIDEO_SHADER_PARAMETER, DISPLAYLIST_DROPDOWN_LIST_VIDEO_SHADER_PRESET_PARAMETER, DISPLAYLIST_DROPDOWN_LIST_VIDEO_SHADER_NUM_PASSES, diff --git a/menu/menu_driver.h b/menu/menu_driver.h index e7fe155a9901..6f5b05ea5b66 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -114,6 +114,7 @@ enum menu_settings_type MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION, MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION_KBD, MENU_SETTING_DROPDOWN_ITEM_AUDIO_DEVICE, + MENU_SETTING_DROPDOWN_ITEM_MICROPHONE_DEVICE, #ifdef HAVE_NETWORKING MENU_SETTING_DROPDOWN_ITEM_NETPLAY_MITM_SERVER, #endif diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 850b7975d498..764b1d844006 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -2937,6 +2937,33 @@ static int setting_string_action_start_audio_device(rarch_setting_t *setting) command_event(CMD_EVENT_AUDIO_REINIT, NULL); return 0; } + +static int setting_string_action_start_microphone_device(rarch_setting_t *setting) +{ + if (!setting) + return -1; + + strlcpy(setting->value.target.string, "", setting->size); + + command_event(CMD_EVENT_MICROPHONE_REINIT, NULL); + return 0; +} + +static int setting_string_action_ok_microphone_device( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + char enum_idx[16]; + if (!setting) + return -1; + + snprintf(enum_idx, sizeof(enum_idx), "%d", setting->enum_idx); + + generic_action_ok_displaylist_push( + enum_idx, /* we will pass the enumeration index of the string as a path */ + NULL, NULL, 0, idx, 0, + ACTION_OK_DL_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE); + return 0; +} #endif static int setting_string_action_left_string_options( @@ -5918,6 +5945,38 @@ static int setting_string_action_left_audio_device( command_event(CMD_EVENT_AUDIO_REINIT, NULL); return 0; } + +static int setting_string_action_left_microphone_device( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + int mic_device_index; + struct string_list *ptr = NULL; + + if (!microphone_driver_get_devices_list((void**)&ptr)) + return -1; + + if (!ptr) + return -1; + + /* Get index in the string list */ + mic_device_index = string_list_find_elem( + ptr, setting->value.target.string) - 1; + mic_device_index--; + + /* Reset index if needed */ + if (mic_device_index < -1) + mic_device_index = (int)(ptr->size - 1); + + if (mic_device_index < 0) + strlcpy(setting->value.target.string, + "", setting->size); + else + strlcpy(setting->value.target.string, + ptr->elems[mic_device_index].data, setting->size); + + command_event(CMD_EVENT_MICROPHONE_REINIT, NULL); + return 0; +} #endif static int setting_string_action_left_driver( @@ -6206,6 +6265,37 @@ static int setting_string_action_right_audio_device( command_event(CMD_EVENT_AUDIO_REINIT, NULL); return 0; } + +static int setting_string_action_right_microphone_device( + rarch_setting_t *setting, size_t idx, bool wraparound) +{ + int mic_device_index; + struct string_list *ptr = NULL; + + if (!microphone_driver_get_devices_list((void**)&ptr)) + return -1; + + if (!ptr) + return -1; + + /* Get index in the string list */ + mic_device_index = string_list_find_elem(ptr,setting->value.target.string) - 1; + mic_device_index++; + + /* Reset index if needed */ + if (mic_device_index == (signed)ptr->size) + mic_device_index = -1; + + if (mic_device_index < 0) + strlcpy(setting->value.target.string, + "", setting->size); + else + strlcpy(setting->value.target.string, + ptr->elems[mic_device_index].data, setting->size); + + command_event(CMD_EVENT_MICROPHONE_REINIT, NULL); + return 0; +} #endif static int setting_string_action_right_driver( @@ -13838,9 +13928,12 @@ static bool setting_append_list( general_read_handler); SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT; - (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; + (*list)[list_info->index - 1].action_start = setting_string_action_start_microphone_device; (*list)[list_info->index - 1].action_left = &setting_string_action_left_microphone_device; (*list)[list_info->index - 1].action_right = &setting_string_action_right_microphone_device; + (*list)[list_info->index - 1].action_ok = &setting_string_action_ok_microphone_device; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_string_audio_device; #endif CONFIG_UINT( @@ -14035,6 +14128,9 @@ static bool setting_append_list( (*list)[list_info->index - 1].action_start = setting_generic_action_start_default; (*list)[list_info->index - 1].action_left = &setting_string_action_left_microphone_device; (*list)[list_info->index - 1].action_right = &setting_string_action_right_microphone_device; + (*list)[list_info->index - 1].action_ok = &setting_string_action_ok_microphone_device; + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_string_audio_device; #endif CONFIG_UINT( diff --git a/msg_hash.h b/msg_hash.h index b8d2df96fad7..04a69e8d29ed 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1783,6 +1783,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_SPECIAL, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_RESOLUTION, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_AUDIO_DEVICE, + MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_MICROPHONE_DEVICE, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_PARAMETER, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_PRESET_PARAMETER, MENU_ENUM_LABEL_DEFERRED_DROPDOWN_BOX_LIST_VIDEO_SHADER_NUM_PASSES, @@ -2088,6 +2089,7 @@ enum msg_hash_enums MENU_LABEL(MICROPHONE_INPUT_RATE), MENU_LABEL(MICROPHONE_LATENCY), MENU_LBL_H(MICROPHONE_DEVICE), + MENU_ENUM_LABEL_MICROPHONE_DEVICE_LIST, MENU_LABEL(MICROPHONE_BLOCK_FRAMES), MENU_LABEL(MICROPHONE_WASAPI_EXCLUSIVE_MODE), MENU_LABEL(MICROPHONE_WASAPI_FLOAT_FORMAT), From 9afa9c66d2b98d97fc7451545f341ab34ddb8f2d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Feb 2023 13:35:58 -0500 Subject: [PATCH 224/309] Move alsa.h into audio/common --- audio/{drivers => common}/alsa.h | 0 audio/drivers/alsa.c | 2 +- audio/drivers/alsathread.c | 2 +- microphone/drivers/alsa.c | 2 +- microphone/drivers/alsathread.c | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename audio/{drivers => common}/alsa.h (100%) diff --git a/audio/drivers/alsa.h b/audio/common/alsa.h similarity index 100% rename from audio/drivers/alsa.h rename to audio/common/alsa.h diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 0e754b2e11fd..703a88bfe71d 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -23,7 +23,7 @@ #include #include "../audio_driver.h" -#include "alsa.h" +#include "../common/alsa.h" #include "../../verbosity.h" typedef struct alsa diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 99d662332450..3c6edd805d67 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -27,7 +27,7 @@ #include #include "../audio_driver.h" -#include "alsa.h" +#include "../common/alsa.h" /* For some common functions/types */ #include "../../verbosity.h" typedef struct alsa_thread diff --git a/microphone/drivers/alsa.c b/microphone/drivers/alsa.c index c72a12236ab8..89f03b00a475 100644 --- a/microphone/drivers/alsa.c +++ b/microphone/drivers/alsa.c @@ -17,7 +17,7 @@ #include #include -#include "audio/drivers/alsa.h" /* For some common functions/types */ +#include "audio/common/alsa.h" /* For some common functions/types */ #include "microphone/microphone_driver.h" #include "verbosity.h" diff --git a/microphone/drivers/alsathread.c b/microphone/drivers/alsathread.c index eb2588878bfa..27b3deedcd58 100644 --- a/microphone/drivers/alsathread.c +++ b/microphone/drivers/alsathread.c @@ -17,7 +17,7 @@ #include #include -#include "audio/drivers/alsa.h" /* For some common functions/types */ +#include "audio/common/alsa.h" /* For some common functions/types */ #include "microphone/microphone_driver.h" #include "verbosity.h" #include "retro_assert.h" From d74c122557a882b70ee37ec0172a3d6f1fbeb27b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 9 Feb 2023 09:23:47 -0500 Subject: [PATCH 225/309] Remove RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED - It was never really needed --- libretro-common/include/libretro.h | 5 ----- runloop.c | 11 ----------- 2 files changed, 16 deletions(-) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index a2d873a8783c..03da334c9005 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1806,11 +1806,6 @@ enum retro_mod * Returns false if mic support is disabled */ -#define RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED (75 | RETRO_ENVIRONMENT_EXPERIMENTAL) - /* bool * -- - * Returns true if the user has enabled the microphone, - * regardless of whether the current audio driver supports it. - */ /* VFS functionality */ /* File paths: diff --git a/runloop.c b/runloop.c index aaa787ddc4c8..8d180a180738 100644 --- a/runloop.c +++ b/runloop.c @@ -3387,17 +3387,6 @@ bool runloop_environment_cb(unsigned cmd, void *data) } } break; - case RETRO_ENVIRONMENT_GET_MICROPHONE_ENABLED: - { - bool *microphone_enabled = (bool *)data; - - if (microphone_enabled == NULL) - return false; - /* User didn't provide a pointer for a response, what can we do? */ - - *microphone_enabled = settings->bools.microphone_enable; - return true; - } case RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT: { struct retro_hw_render_context_negotiation_interface *iface = From 4f0ca8d46e692be78ad59c9f25bd825c0283a34d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 9 Feb 2023 09:31:11 -0500 Subject: [PATCH 226/309] Refactor the ALSA microphone driver - Move common ALSA functions to audio/common/alsa.c - Replace alsa_set_mic_enabled_internal with alsa_start/stop_pcm - Don't track the microphone handle in the ALSA driver context - Remove unneeded fields --- Makefile.common | 3 +- audio/common/alsa.c | 472 ++++++++++++++++++++++++++++++++ audio/common/alsa.h | 3 +- audio/drivers/alsa.c | 352 ------------------------ microphone/drivers/alsa.c | 237 ++-------------- microphone/drivers/alsathread.c | 105 ++----- 6 files changed, 520 insertions(+), 652 deletions(-) create mode 100644 audio/common/alsa.c diff --git a/Makefile.common b/Makefile.common index b9fc060aef32..7496da94af4c 100644 --- a/Makefile.common +++ b/Makefile.common @@ -845,7 +845,8 @@ endif ifeq ($(HAVE_ALSA), 1) OBJ += audio/drivers/alsa.o \ - microphone/drivers/alsa.o + microphone/drivers/alsa.o \ + audio/common/alsa.o ifneq ($(HAVE_HAKCHI), 1) ifneq ($(HAVE_SEGAM), 1) diff --git a/audio/common/alsa.c b/audio/common/alsa.c new file mode 100644 index 000000000000..518304130ed3 --- /dev/null +++ b/audio/common/alsa.c @@ -0,0 +1,472 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 Daniel De Matteis + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ +#include +#include + +#include +#include + +#include "../audio_driver.h" +#include "../common/alsa.h" +#include "../../verbosity.h" + +int alsa_init_pcm(snd_pcm_t **pcm, + const char* device, + snd_pcm_stream_t stream, + unsigned rate, + unsigned latency, + unsigned channels, + alsa_stream_info_t *stream_info, + unsigned *new_rate, + int mode) +{ + snd_pcm_format_t format; + snd_pcm_uframes_t buffer_size; + snd_pcm_hw_params_t *params = NULL; + snd_pcm_sw_params_t *sw_params = NULL; + unsigned latency_usec = latency * 1000; + unsigned periods = 4; + unsigned orig_rate = rate; + const char *alsa_dev = device ? device : "default"; + int errnum = 0; + + RARCH_DBG("[ALSA]: Requesting device \"%s\" for %s stream\n", alsa_dev, snd_pcm_stream_name(stream)); + + if ((errnum = snd_pcm_open(pcm, alsa_dev, stream, mode)) < 0) + { + RARCH_ERR("[ALSA]: Failed to open %s stream on device \"%s\": %s\n", + snd_pcm_stream_name(stream), + alsa_dev, + snd_strerror(errnum)); + + goto error; + } + + if ((errnum = snd_pcm_hw_params_malloc(¶ms)) < 0) + { + RARCH_ERR("[ALSA]: Failed to allocate hardware parameters: %s\n", + snd_strerror(errnum)); + + goto error; + } + + if ((errnum = snd_pcm_hw_params_any(*pcm, params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to query hardware parameters from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + format = (snd_pcm_hw_params_test_format(*pcm, params, SND_PCM_FORMAT_FLOAT) == 0) + ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; + stream_info->has_float = (format == SND_PCM_FORMAT_FLOAT); + + RARCH_LOG("[ALSA]: Using %s sample format for %s device \"%s\"\n", + snd_pcm_format_name(format), + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm) + ); + + if ((errnum = snd_pcm_hw_params_set_access(*pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + { + RARCH_ERR("[ALSA]: Failed to set %s access for %s device \"%s\": %s\n", + snd_pcm_access_name(SND_PCM_ACCESS_RW_INTERLEAVED), + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + stream_info->frame_bits = snd_pcm_format_physical_width(format) * channels; + + if ((errnum = snd_pcm_hw_params_set_format(*pcm, params, format)) < 0) + { + RARCH_ERR("[ALSA]: Failed to set %s format for %s device \"%s\": %s\n", + snd_pcm_format_name(format), + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + if ((errnum = snd_pcm_hw_params_set_channels(*pcm, params, channels)) < 0) + { + RARCH_ERR("[ALSA]: Failed to set %u-channel audio for %s device \"%s\": %s\n", + channels, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + /* Don't allow rate resampling when probing for the default rate (but ignore if this call fails) */ + if ((errnum = snd_pcm_hw_params_set_rate_resample(*pcm, params, false)) < 0) + { + RARCH_WARN("[ALSA]: Failed to request a default unsampled rate for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + } + + if ((errnum = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0)) < 0) + { + RARCH_ERR("[ALSA]: Failed to request a rate near %uHz for %s device \"%s\": %s\n", + rate, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + if (new_rate && (rate != orig_rate)) + *new_rate = rate; + + if ((snd_pcm_hw_params_set_buffer_time_near(*pcm, params, &latency_usec, NULL)) < 0) + { + RARCH_ERR("[ALSA]: Failed to request a buffer time near %uus for %s device \"%s\": %s\n", + latency_usec, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + + } + + if ((errnum = snd_pcm_hw_params_set_periods_near(*pcm, params, &periods, NULL)) < 0) + { + RARCH_ERR("[ALSA]: Failed to request %u periods per buffer for %s device \"%s\": %s\n", + periods, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + if ((errnum = snd_pcm_hw_params(*pcm, params)) < 0) + { /* This calls snd_pcm_prepare() under the hood */ + RARCH_ERR("[ALSA]: Failed to install hardware parameters for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + /* Shouldn't have to bother with this, + * but some drivers are apparently broken. */ + if ((errnum = snd_pcm_hw_params_get_period_size(params, &stream_info->period_frames, NULL)) < 0) + { + RARCH_WARN("[ALSA]: Failed to get an exact period size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + RARCH_WARN("[ALSA]: Trying the minimum period size instead\n"); + + if ((errnum = snd_pcm_hw_params_get_period_size_min(params, &stream_info->period_frames, NULL)) < 0) + { + RARCH_ERR("[ALSA]: Failed to get min period size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + goto error; + } + } + + stream_info->period_size = snd_pcm_frames_to_bytes(*pcm, stream_info->period_frames); + if (stream_info->period_size < 0) + { + RARCH_ERR("[ALSA]: Failed to convert a period size of %lu frames to bytes: %s\n", + stream_info->period_frames, + snd_strerror(stream_info->period_frames)); + goto error; + } + + RARCH_LOG("[ALSA]: Period: %u periods per buffer (%lu frames, %lu bytes)\n", + periods, + stream_info->period_frames, + stream_info->period_size); + + if ((errnum = snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) < 0) + { + RARCH_WARN("[ALSA]: Failed to get exact buffer size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + RARCH_WARN("[ALSA]: Trying the maximum buffer size instead\n"); + + if ((errnum = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size)) < 0) + { + RARCH_ERR("[ALSA]: Failed to get max buffer size from %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + goto error; + } + } + + + stream_info->buffer_size = snd_pcm_frames_to_bytes(*pcm, buffer_size); + if (stream_info->buffer_size < 0) + { + RARCH_ERR("[ALSA]: Failed to convert a buffer size of %lu frames to bytes: %s\n", + buffer_size, + snd_strerror(buffer_size)); + goto error; + } + RARCH_LOG("[ALSA]: Buffer size: %lu frames (%lu bytes)\n", buffer_size, stream_info->buffer_size); + + stream_info->can_pause = snd_pcm_hw_params_can_pause(params); + + RARCH_LOG("[ALSA]: Can pause: %s.\n", stream_info->can_pause ? "yes" : "no"); + + if ((errnum = snd_pcm_sw_params_malloc(&sw_params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to allocate software parameters: %s\n", + snd_strerror(errnum)); + + goto error; + } + + if ((errnum = snd_pcm_sw_params_current(*pcm, sw_params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to query current software parameters for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + if ((errnum = snd_pcm_sw_params_set_start_threshold(*pcm, sw_params, buffer_size / 2)) < 0) + { + RARCH_ERR("[ALSA]: Failed to set start %lu-frame threshold for %s device \"%s\": %s\n", + buffer_size / 2, + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + if ((errnum = snd_pcm_sw_params(*pcm, sw_params)) < 0) + { + RARCH_ERR("[ALSA]: Failed to install software parameters for %s device \"%s\": %s\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm), + snd_strerror(errnum)); + + goto error; + } + + snd_pcm_hw_params_free(params); + snd_pcm_sw_params_free(sw_params); + + RARCH_LOG("[ALSA]: Initialized %s device \"%s\"\n", + snd_pcm_stream_name(stream), + snd_pcm_name(*pcm)); + + return 0; +error: + if (params) + snd_pcm_hw_params_free(params); + + if (sw_params) + snd_pcm_sw_params_free(sw_params); + + if (*pcm) + { + alsa_free_pcm(*pcm); + *pcm = NULL; + } + + return errnum; +} + +void alsa_free_pcm(snd_pcm_t *pcm) +{ + if (pcm) + { + int errnum = 0; + + if ((errnum = snd_pcm_drop(pcm)) < 0) + { + RARCH_WARN("[ALSA]: Failed to drop remaining samples in %s stream \"%s\": %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_strerror(errnum)); + } + + if ((errnum = snd_pcm_close(pcm)) < 0) + { + RARCH_WARN("[ALSA]: Failed to close %s stream \"%s\": %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_strerror(errnum)); + } + } +} + +bool alsa_start_pcm(snd_pcm_t *pcm) +{ + int errnum = 0; + snd_pcm_state_t pcm_state; + + if (!pcm) + return false; + + pcm_state = snd_pcm_state(pcm); + switch (pcm_state) + { + case SND_PCM_STATE_PAUSED: /* If we're unpausing a valid (but paused) stream... */ + if ((errnum = snd_pcm_pause(pcm, false)) < 0) + { /* ...but we failed... */ + goto error; + } + break; + case SND_PCM_STATE_PREPARED: + /* If we're starting this stream for the first time... */ + if ((errnum = snd_pcm_start(pcm)) < 0) + { /* ..but we failed... */ + goto error; + } + break; + default: + RARCH_ERR("[ALSA]: Failed to start %s stream \"%s\" in unexpected state %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_pcm_state_name(pcm_state)); + return false; + } + + RARCH_DBG("[ALSA]: Started %s stream \"%s\", transitioning from %s to %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_pcm_state_name(pcm_state), + snd_pcm_state_name(snd_pcm_state(pcm))); + + return true; + +error: + RARCH_ERR("[ALSA]: Failed to start %s stream \"%s\" in state %s: %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_pcm_state_name(pcm_state), + snd_strerror(errnum)); + + return false; +} + +bool alsa_stop_pcm(snd_pcm_t *pcm) +{ + int errnum = 0; + snd_pcm_state_t pcm_state; + + if (!pcm) + return false; + + pcm_state = snd_pcm_state(pcm); + switch (pcm_state) + { + case SND_PCM_STATE_PREPARED: + case SND_PCM_STATE_RUNNING: + /* If we're pausing an active stream... */ + if ((errnum = snd_pcm_pause(pcm, true)) < 0) + { /* ...but we failed... */ + goto error; + } + break; + default: + RARCH_ERR("[ALSA]: Failed to stop %s stream \"%s\" in unexpected state %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_pcm_state_name(pcm_state)); + + return false; + } + + RARCH_DBG("[ALSA]: Stopped %s stream \"%s\", transitioning from %s to %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_pcm_state_name(pcm_state), + snd_pcm_state_name(snd_pcm_state(pcm))); + + return true; + +error: + RARCH_ERR("[ALSA]: Failed to stop %s stream \"%s\" in state %s: %s\n", + snd_pcm_stream_name(snd_pcm_stream(pcm)), + snd_pcm_name(pcm), + snd_pcm_state_name(pcm_state), + snd_strerror(errnum)); + + return false; +} + +struct string_list *alsa_device_list_type_new(const char* type) +{ + void **hints, **n; + union string_list_elem_attr attr; + struct string_list *s = string_list_new(); + + if (!s) + return NULL; + + attr.i = 0; + + if (snd_device_name_hint(-1, "pcm", &hints) != 0) + goto error; + + n = hints; + + while (*n) + { + char *name = snd_device_name_get_hint(*n, "NAME"); + char *io = snd_device_name_get_hint(*n, "IOID"); + char *desc = snd_device_name_get_hint(*n, "DESC"); + + /* description of device IOID - input / output identifcation + * ("Input" or "Output"), NULL means both) */ + + if (!io || (string_is_equal(io, type))) + string_list_append(s, name, attr); + + if (name) + free(name); + if (io) + free(io); + if (desc) + free(desc); + + n++; + } + + /* free hint buffer too */ + snd_device_name_free_hint(hints); + + return s; + + error: + string_list_free(s); + return NULL; +} diff --git a/audio/common/alsa.h b/audio/common/alsa.h index e97888527c9e..dbc604a3107d 100644 --- a/audio/common/alsa.h +++ b/audio/common/alsa.h @@ -66,6 +66,7 @@ void alsa_thread_free_info_members(alsa_thread_info_t *info); /** * Sets the state of the PCM stream without updating the mic state */ -bool alsa_set_mic_enabled_internal(snd_pcm_t *microphone, bool enabled); +bool alsa_start_pcm(snd_pcm_t *pcm); +bool alsa_stop_pcm(snd_pcm_t *pcm); #endif /* _RETROARCH_ALSA */ diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 703a88bfe71d..43a47f34ae2b 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -63,310 +63,6 @@ static void alsa_log_error(const char *file, int line, const char *function, int RARCH_ERR("[ALSA]: [%s:%s:%d]: %s%s\n", file, function, line, temp, errno_temp); /* To ensure that there's a newline at the end */ } -int alsa_init_pcm(snd_pcm_t **pcm, - const char* device, - snd_pcm_stream_t stream, - unsigned rate, - unsigned latency, - unsigned channels, - alsa_stream_info_t *stream_info, - unsigned *new_rate, - int mode) -{ - snd_pcm_format_t format; - snd_pcm_uframes_t buffer_size; - snd_pcm_hw_params_t *params = NULL; - snd_pcm_sw_params_t *sw_params = NULL; - unsigned latency_usec = latency * 1000; - unsigned periods = 4; - unsigned orig_rate = rate; - const char *alsa_dev = device ? device : "default"; - int errnum = 0; - - RARCH_DBG("[ALSA]: Requesting device \"%s\" for %s stream\n", alsa_dev, snd_pcm_stream_name(stream)); - - if ((errnum = snd_pcm_open(pcm, alsa_dev, stream, mode)) < 0) - { - RARCH_ERR("[ALSA]: Failed to open %s stream on device \"%s\": %s\n", - snd_pcm_stream_name(stream), - alsa_dev, - snd_strerror(errnum)); - - goto error; - } - - if ((errnum = snd_pcm_hw_params_malloc(¶ms)) < 0) - { - RARCH_ERR("[ALSA]: Failed to allocate hardware parameters: %s\n", - snd_strerror(errnum)); - - goto error; - } - - if ((errnum = snd_pcm_hw_params_any(*pcm, params)) < 0) - { - RARCH_ERR("[ALSA]: Failed to query hardware parameters from %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - format = (snd_pcm_hw_params_test_format(*pcm, params, SND_PCM_FORMAT_FLOAT) == 0) - ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16; - stream_info->has_float = (format == SND_PCM_FORMAT_FLOAT); - - RARCH_LOG("[ALSA]: Using %s sample format for %s device \"%s\"\n", - snd_pcm_format_name(format), - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm) - ); - - if ((errnum = snd_pcm_hw_params_set_access(*pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) - { - RARCH_ERR("[ALSA]: Failed to set %s access for %s device \"%s\": %s\n", - snd_pcm_access_name(SND_PCM_ACCESS_RW_INTERLEAVED), - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - stream_info->frame_bits = snd_pcm_format_physical_width(format) * channels; - - if ((errnum = snd_pcm_hw_params_set_format(*pcm, params, format)) < 0) - { - RARCH_ERR("[ALSA]: Failed to set %s format for %s device \"%s\": %s\n", - snd_pcm_format_name(format), - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - if ((errnum = snd_pcm_hw_params_set_channels(*pcm, params, channels)) < 0) - { - RARCH_ERR("[ALSA]: Failed to set %u-channel audio for %s device \"%s\": %s\n", - channels, - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - /* Don't allow rate resampling when probing for the default rate (but ignore if this call fails) */ - if ((errnum = snd_pcm_hw_params_set_rate_resample(*pcm, params, false)) < 0) - { - RARCH_WARN("[ALSA]: Failed to request a default unsampled rate for %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - } - - if ((errnum = snd_pcm_hw_params_set_rate_near(*pcm, params, &rate, 0)) < 0) - { - RARCH_ERR("[ALSA]: Failed to request a rate near %uHz for %s device \"%s\": %s\n", - rate, - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - if (new_rate && (rate != orig_rate)) - *new_rate = rate; - - if ((snd_pcm_hw_params_set_buffer_time_near(*pcm, params, &latency_usec, NULL)) < 0) - { - RARCH_ERR("[ALSA]: Failed to request a buffer time near %uus for %s device \"%s\": %s\n", - latency_usec, - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - - } - - if ((errnum = snd_pcm_hw_params_set_periods_near(*pcm, params, &periods, NULL)) < 0) - { - RARCH_ERR("[ALSA]: Failed to request %u periods per buffer for %s device \"%s\": %s\n", - periods, - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - if ((errnum = snd_pcm_hw_params(*pcm, params)) < 0) - { /* This calls snd_pcm_prepare() under the hood */ - RARCH_ERR("[ALSA]: Failed to install hardware parameters for %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - /* Shouldn't have to bother with this, - * but some drivers are apparently broken. */ - if ((errnum = snd_pcm_hw_params_get_period_size(params, &stream_info->period_frames, NULL)) < 0) - { - RARCH_WARN("[ALSA]: Failed to get an exact period size from %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - RARCH_WARN("[ALSA]: Trying the minimum period size instead\n"); - - if ((errnum = snd_pcm_hw_params_get_period_size_min(params, &stream_info->period_frames, NULL)) < 0) - { - RARCH_ERR("[ALSA]: Failed to get min period size from %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - goto error; - } - } - - stream_info->period_size = snd_pcm_frames_to_bytes(*pcm, stream_info->period_frames); - if (stream_info->period_size < 0) - { - RARCH_ERR("[ALSA]: Failed to convert a period size of %lu frames to bytes: %s\n", - stream_info->period_frames, - snd_strerror(stream_info->period_frames)); - goto error; - } - - RARCH_LOG("[ALSA]: Period: %u periods per buffer (%lu frames, %lu bytes)\n", - periods, - stream_info->period_frames, - stream_info->period_size); - - if ((errnum = snd_pcm_hw_params_get_buffer_size(params, &buffer_size)) < 0) - { - RARCH_WARN("[ALSA]: Failed to get exact buffer size from %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - RARCH_WARN("[ALSA]: Trying the maximum buffer size instead\n"); - - if ((errnum = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size)) < 0) - { - RARCH_ERR("[ALSA]: Failed to get max buffer size from %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - goto error; - } - } - - - stream_info->buffer_size = snd_pcm_frames_to_bytes(*pcm, buffer_size); - if (stream_info->buffer_size < 0) - { - RARCH_ERR("[ALSA]: Failed to convert a buffer size of %lu frames to bytes: %s\n", - buffer_size, - snd_strerror(buffer_size)); - goto error; - } - RARCH_LOG("[ALSA]: Buffer size: %lu frames (%lu bytes)\n", buffer_size, stream_info->buffer_size); - - stream_info->can_pause = snd_pcm_hw_params_can_pause(params); - - RARCH_LOG("[ALSA]: Can pause: %s.\n", stream_info->can_pause ? "yes" : "no"); - - if ((errnum = snd_pcm_sw_params_malloc(&sw_params)) < 0) - { - RARCH_ERR("[ALSA]: Failed to allocate software parameters: %s\n", - snd_strerror(errnum)); - - goto error; - } - - if ((errnum = snd_pcm_sw_params_current(*pcm, sw_params)) < 0) - { - RARCH_ERR("[ALSA]: Failed to query current software parameters for %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - if ((errnum = snd_pcm_sw_params_set_start_threshold(*pcm, sw_params, buffer_size / 2)) < 0) - { - RARCH_ERR("[ALSA]: Failed to set start %lu-frame threshold for %s device \"%s\": %s\n", - buffer_size / 2, - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - if ((errnum = snd_pcm_sw_params(*pcm, sw_params)) < 0) - { - RARCH_ERR("[ALSA]: Failed to install software parameters for %s device \"%s\": %s\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm), - snd_strerror(errnum)); - - goto error; - } - - snd_pcm_hw_params_free(params); - snd_pcm_sw_params_free(sw_params); - - RARCH_LOG("[ALSA]: Initialized %s device \"%s\"\n", - snd_pcm_stream_name(stream), - snd_pcm_name(*pcm)); - - return 0; -error: - if (params) - snd_pcm_hw_params_free(params); - - if (sw_params) - snd_pcm_sw_params_free(sw_params); - - if (*pcm) - { - alsa_free_pcm(*pcm); - *pcm = NULL; - } - - return errnum; -} - -void alsa_free_pcm(snd_pcm_t *pcm) -{ - if (pcm) - { - int errnum = 0; - - if ((errnum = snd_pcm_drop(pcm)) < 0) - { - RARCH_WARN("[ALSA]: Failed to drop remaining samples in %s device \"%s\": %s\n", - snd_pcm_stream_name(snd_pcm_stream(pcm)), - snd_pcm_name(pcm), - snd_strerror(errnum)); - } - - if ((errnum = snd_pcm_close(pcm)) < 0) - { - RARCH_WARN("[ALSA]: Failed to close %s device \"%s\": %s\n", - snd_pcm_stream_name(snd_pcm_stream(pcm)), - snd_pcm_name(pcm), - snd_strerror(errnum)); - } - } -} - static void alsa_free(void *data); static void *alsa_init(const char *device, unsigned rate, unsigned latency, unsigned block_frames, @@ -580,54 +276,6 @@ static size_t alsa_buffer_size(void *data) return alsa->stream_info.buffer_size; } -struct string_list *alsa_device_list_type_new(const char* type) -{ - void **hints, **n; - union string_list_elem_attr attr; - struct string_list *s = string_list_new(); - - if (!s) - return NULL; - - attr.i = 0; - - if (snd_device_name_hint(-1, "pcm", &hints) != 0) - goto error; - - n = hints; - - while (*n) - { - char *name = snd_device_name_get_hint(*n, "NAME"); - char *io = snd_device_name_get_hint(*n, "IOID"); - char *desc = snd_device_name_get_hint(*n, "DESC"); - - /* description of device IOID - input / output identifcation - * ("Input" or "Output"), NULL means both) */ - - if (!io || (string_is_equal(io, type))) - string_list_append(s, name, attr); - - if (name) - free(name); - if (io) - free(io); - if (desc) - free(desc); - - n++; - } - - /* free hint buffer too */ - snd_device_name_free_hint(hints); - - return s; - - error: - string_list_free(s); - return NULL; -} - void *alsa_device_list_new(void *data) { return alsa_device_list_type_new("Output"); diff --git a/microphone/drivers/alsa.c b/microphone/drivers/alsa.c index 89f03b00a475..8e832987cc5a 100644 --- a/microphone/drivers/alsa.c +++ b/microphone/drivers/alsa.c @@ -29,18 +29,11 @@ typedef struct alsa_microphone_handle { snd_pcm_t *pcm; alsa_stream_info_t stream_info; - bool is_paused; } alsa_microphone_handle_t; typedef struct alsa { - /* Only one microphone is supported right now; - * the driver state should track multiple microphone handles, - * but the driver *context* should track multiple microphone contexts */ - alsa_microphone_handle_t *microphone; - bool nonblock; - bool is_paused; } alsa_microphone_t; static void *alsa_microphone_init(void) @@ -62,17 +55,16 @@ static void alsa_microphone_close_mic(void *driver_context, void *microphone_con static void alsa_microphone_free(void *driver_context) { alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; + /* The mic frontend should've closed all mics before calling free(). */ if (alsa) { - alsa_microphone_close_mic(alsa, alsa->microphone); - snd_config_update_free_global(); free(alsa); } } -static bool alsa_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled); +static bool alsa_microphone_start_mic(void *driver_context, void *microphone_context); static ssize_t alsa_microphone_read(void *driver_context, void *microphone_context, void *buf_, size_t size_) { alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; @@ -90,12 +82,6 @@ static ssize_t alsa_microphone_read(void *driver_context, void *microphone_conte size = BYTES_TO_FRAMES(size_, microphone->stream_info.frame_bits); frames_size = microphone->stream_info.has_float ? sizeof(float) : sizeof(int16_t); - /* Workaround buggy menu code. - * If a read happens while we're paused, we might never progress. */ - if (microphone->is_paused) - if (!alsa_microphone_set_mic_active(alsa, microphone, true)) - return -1; - state = snd_pcm_state(microphone->pcm); if (state != SND_PCM_STATE_RUNNING) { @@ -189,204 +175,15 @@ static ssize_t alsa_microphone_read(void *driver_context, void *microphone_conte return FRAMES_TO_BYTES(read, microphone->stream_info.frame_bits); } -static bool alsa_microphone_stop(void *driver_context) -{ - alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; - if (alsa->is_paused) - return true; - - if (alsa->microphone) - { - /* Stop the microphone independently of whether it's paused; - * note that upon alsa_microphone_start, the microphone won't resume - * if it was previously paused */ - int errnum = snd_pcm_pause(alsa->microphone->pcm, true); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to pause microphone \"%s\": %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_strerror(errnum)); - return false; - } - } - - return true; -} - -static bool alsa_microphone_start(void *driver_context, bool is_shutdown) -{ - alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; - if (!alsa->is_paused) - return true; - - alsa->is_paused = false; - - if (alsa->microphone && !alsa->microphone->is_paused) - { /* If the mic wasn't paused at the time the overall driver was paused... */ - snd_pcm_state_t mic_state = snd_pcm_state(alsa->microphone->pcm); - - /* If we're calling this function with a pending microphone, - * (as happens when a core requests a microphone at the start), - * the mic will be in the PREPARED state rather than the PAUSED state. - **/ - switch (mic_state) - { - case SND_PCM_STATE_PREPARED: - { - int errnum = snd_pcm_start(alsa->microphone->pcm); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to start microphone \"%s\": %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_strerror(errnum)); - - return false; - } - break; - } - case SND_PCM_STATE_PAUSED: - { - int errnum = snd_pcm_pause(alsa->microphone->pcm, false); - if (errnum < 0) - { - RARCH_ERR("[ALSA]: Failed to unpause microphone \"%s\" in state %s: %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_pcm_state_name(snd_pcm_state(alsa->microphone->pcm)), - snd_strerror(errnum)); - - return false; - } - break; - } - default: - { - RARCH_ERR("[ALSA]: Expected microphone \"%s\" to be in state PREPARED or PAUSED, it was in %s\n", - snd_pcm_name(alsa->microphone->pcm), - snd_pcm_state_name(mic_state)); - return false; - } - } - } - - return true; -} - -static bool alsa_microphone_alive(void *driver_context) -{ - alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; - if (!alsa) - return false; - return !alsa->is_paused; -} - -bool alsa_set_mic_enabled_internal(snd_pcm_t *microphone, bool enabled) -{ - snd_pcm_state_t microphone_state = snd_pcm_state(microphone); - int errnum = 0; - - if (enabled) - { /* If we're trying to unpause a mic (or maybe activate it for the first time)... */ - switch (microphone_state) - { - case SND_PCM_STATE_PAUSED: /* If we're unpausing a valid (but paused) mic... */ - if ((errnum = snd_pcm_pause(microphone, false)) < 0) - { /* ...but we failed... */ - goto error; - } - break; - case SND_PCM_STATE_PREPARED: - /* If we're activating this mic for the first time... */ - if ((errnum = snd_pcm_start(microphone)) < 0) - { /* ..but we failed... */ - goto error; - } - break; - default: - goto unexpected_state; - } - } - else - { /* We're pausing this mic */ - switch (microphone_state) - { - case SND_PCM_STATE_PREPARED: - case SND_PCM_STATE_RUNNING: - /* If we're pausing an active mic... */ - if ((errnum = snd_pcm_pause(microphone, true)) < 0) - { /* ...but we failed... */ - goto error; - } - break; - default: - goto unexpected_state; - } - } - - RARCH_DBG("[ALSA]: %s microphone \"%s\", transitioning from %s to %s\n", - enabled ? "Unpaused" : "Paused", - snd_pcm_name(microphone), - snd_pcm_state_name(microphone_state), - snd_pcm_state_name(snd_pcm_state(microphone))); - - return true; - error: - RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in state %s: %s\n", - enabled ? "unpause" : "pause", - snd_pcm_name(microphone), - snd_pcm_state_name(microphone_state), - snd_strerror(errnum)); - - return false; - - unexpected_state: - RARCH_ERR("[ALSA]: Failed to %s microphone \"%s\" in unexpected state %s\n", - enabled ? "unpause" : "pause", - snd_pcm_name(microphone), - snd_pcm_state_name(microphone_state)); - - return false; -} - -static bool alsa_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled) -{ - alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; - alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; - - if (!alsa || !microphone) - return false; - /* Both params must be non-null */ - - if (!microphone->stream_info.can_pause) - { - RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->pcm)); - return true; - } - - if (!alsa->is_paused) - { /* If the entire audio driver isn't paused... */ - if (alsa_set_mic_enabled_internal(microphone->pcm, enabled)) - { - microphone->is_paused = !enabled; - return true; - } - return false; - } - - return true; -} - static bool alsa_microphone_mic_alive(const void *driver_context, const void *microphone_context) { - alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context; alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + (void)driver_context; - if (!alsa || !microphone) + if (!microphone) return false; - /* Both params must be non-null */ - return !microphone->is_paused; - /* The mic might be paused due to app requirements, - * or it might be stopped because the entire audio driver is stopped. */ + return snd_pcm_state(microphone->pcm) == SND_PCM_STATE_RUNNING; } static void alsa_microphone_set_nonblock_state(void *driver_context, bool nonblock) @@ -432,7 +229,6 @@ static void *alsa_microphone_open_mic(void *driver_context, goto error; } - alsa->microphone = microphone; return microphone; error: @@ -445,31 +241,42 @@ static void *alsa_microphone_open_mic(void *driver_context, } static void alsa_microphone_close_mic(void *driver_context, void *microphone_context) { - alsa_microphone_t *alsa = (alsa_microphone_t *)driver_context; alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + (void)driver_context; - if (alsa && microphone) + if (microphone) { alsa_free_pcm(microphone->pcm); - - alsa->microphone = NULL; free(microphone); } } static bool alsa_microphone_start_mic(void *driver_context, void *microphone_context) { - return alsa_microphone_set_mic_active(driver_context, microphone_context, true); + alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + (void)driver_context; + + if (!microphone) + return false; + + return alsa_start_pcm(microphone->pcm); } static bool alsa_microphone_stop_mic(void *driver_context, void *microphone_context) { - return alsa_microphone_set_mic_active(driver_context, microphone_context, true); + alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + (void)driver_context; + + if (!microphone) + return false; + + return alsa_stop_pcm(microphone->pcm); } static bool alsa_microphone_mic_use_float(const void *driver_context, const void *microphone_context) { alsa_microphone_handle_t *microphone = (alsa_microphone_handle_t*)microphone_context; + (void)driver_context; return microphone->stream_info.has_float; } diff --git a/microphone/drivers/alsathread.c b/microphone/drivers/alsathread.c index 27b3deedcd58..97b4571827b0 100644 --- a/microphone/drivers/alsathread.c +++ b/microphone/drivers/alsathread.c @@ -26,17 +26,11 @@ typedef struct alsa_thread_microphone_handle { alsa_thread_info_t info; - bool is_paused; } alsa_thread_microphone_handle_t; typedef struct alsa_thread { - /* Only one microphone is supported right now; - * the driver state should track multiple microphone handles, - * but the driver *context* should track multiple microphone contexts */ - alsa_thread_microphone_handle_t *microphone; bool nonblock; - bool is_paused; } alsa_thread_microphone_t; static void *alsa_thread_microphone_init(void) @@ -61,9 +55,6 @@ static void alsa_thread_microphone_free(void *driver_context) if (alsa) { - if (alsa->microphone) - alsa_thread_microphone_close_mic(alsa, alsa->microphone); - free(alsa); } } @@ -160,7 +151,7 @@ static void alsa_microphone_worker_thread(void *microphone_context) } } - end: +end: slock_lock(microphone->info.cond_lock); microphone->info.thread_dead = true; scond_signal(microphone->info.cond); @@ -169,7 +160,6 @@ static void alsa_microphone_worker_thread(void *microphone_context) RARCH_DBG("[ALSA] [capture thread %p]: Ending microphone worker thread\n", thread_id); } -static bool alsa_thread_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled); static ssize_t alsa_thread_microphone_read(void *driver_context, void *microphone_context, void *buf, size_t size) { alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; @@ -182,10 +172,6 @@ static ssize_t alsa_thread_microphone_read(void *driver_context, void *microphon if (microphone->info.thread_dead) /* If the mic thread is shutting down... */ return -1; - if (microphone->is_paused) - if (!alsa_thread_microphone_set_mic_active(alsa, microphone, true)) - return -1; - state = snd_pcm_state(microphone->info.pcm); if (state != SND_PCM_STATE_RUNNING) { @@ -273,34 +259,7 @@ static ssize_t alsa_thread_microphone_read(void *driver_context, void *microphon } static bool alsa_thread_microphone_mic_alive(const void *driver_context, const void *microphone_context); -static bool alsa_thread_microphone_start(void *driver_context, bool is_shutdown) -{ - alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; - - if (alsa) - alsa->is_paused = false; - return true; -} - -static bool alsa_thread_microphone_alive(void *driver_context) -{ - alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; - if (!alsa) - return false; - return !alsa->is_paused; -} -static bool alsa_thread_microphone_stop(void *driver_context) -{ - alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; - - if (alsa) - alsa->is_paused = true; - return true; -} - - -static void alsa_thread_microphone_close_mic(void *driver_context, void *microphone_context); static void *alsa_thread_microphone_open_mic(void *driver_context, const char *device, unsigned rate, @@ -342,10 +301,9 @@ static void *alsa_thread_microphone_open_mic(void *driver_context, } RARCH_DBG("[ALSA]: Initialized microphone worker thread\n"); - alsa->microphone = microphone; return microphone; - error: +error: RARCH_ERR("[ALSA]: Failed to initialize microphone...\n"); if (microphone) @@ -357,62 +315,31 @@ static void *alsa_thread_microphone_open_mic(void *driver_context, alsa_thread_microphone_close_mic(alsa, microphone); } - alsa->microphone = NULL; + return NULL; } static void alsa_thread_microphone_close_mic(void *driver_context, void *microphone_context) { - alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t *)driver_context; alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + (void)driver_context; - if (alsa && microphone) + if (microphone) { alsa_thread_free_info_members(µphone->info); - - alsa->microphone = NULL; free(microphone); } } static bool alsa_thread_microphone_mic_alive(const void *driver_context, const void *microphone_context) { - alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t *)microphone_context; + (void)driver_context; - if (!alsa || !microphone) - return false; - - return !microphone->is_paused; -} - -static bool alsa_thread_microphone_set_mic_active(void *driver_context, void *microphone_context, bool enabled) -{ - alsa_thread_microphone_t *alsa = (alsa_thread_microphone_t*)driver_context; - alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; - - if (!alsa || !microphone) + if (!microphone) return false; - /* Both params must be non-null */ - if (!microphone->info.stream_info.can_pause) - { - RARCH_WARN("[ALSA]: Microphone \"%s\" cannot be paused\n", snd_pcm_name(microphone->info.pcm)); - return true; - } - - if (!alsa->is_paused) - { /* If the entire audio driver isn't paused... */ - if (alsa_set_mic_enabled_internal(microphone->info.pcm, enabled)) - { - // TODO: Do I need to synchronize microphone->is_paused? - microphone->is_paused = !enabled; - return true; - } - return false; - } - - return true; + return snd_pcm_state(microphone->info.pcm) == SND_PCM_STATE_RUNNING; } static void alsa_thread_microphone_set_nonblock_state(void *driver_context, bool state) @@ -436,12 +363,24 @@ static void alsa_thread_microphone_device_list_free(const void *driver_context, static bool alsa_thread_microphone_start_mic(void *driver_context, void *microphone_context) { - return alsa_thread_microphone_set_mic_active(driver_context, microphone_context, true); + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + (void)driver_context; + + if (!microphone) + return false; + + return alsa_start_pcm(microphone->info.pcm); } static bool alsa_thread_microphone_stop_mic(void *driver_context, void *microphone_context) { - return alsa_thread_microphone_set_mic_active(driver_context, microphone_context, true); + alsa_thread_microphone_handle_t *microphone = (alsa_thread_microphone_handle_t*)microphone_context; + (void)driver_context; + + if (!microphone) + return false; + + return alsa_stop_pcm(microphone->info.pcm); } static bool alsa_thread_microphone_mic_use_float(const void *driver_context, const void *microphone_context) From c3ddae9bdd2e7ee6e708cf36fa049196068e38a1 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 9 Feb 2023 09:52:02 -0500 Subject: [PATCH 227/309] Move some common alsathread code into audio/common/alsathread.c --- Makefile.common | 3 ++- audio/common/alsa.h | 17 ------------- audio/common/alsathread.c | 45 +++++++++++++++++++++++++++++++++ audio/common/alsathread.h | 40 +++++++++++++++++++++++++++++ audio/drivers/alsathread.c | 32 +---------------------- microphone/drivers/alsathread.c | 3 ++- 6 files changed, 90 insertions(+), 50 deletions(-) create mode 100644 audio/common/alsathread.c create mode 100644 audio/common/alsathread.h diff --git a/Makefile.common b/Makefile.common index 7496da94af4c..81eeb2b2803c 100644 --- a/Makefile.common +++ b/Makefile.common @@ -859,7 +859,8 @@ ifeq ($(HAVE_ALSA), 1) ifneq ($(MIYOO), 1) ifeq ($(HAVE_THREADS), 1) OBJ += audio/drivers/alsathread.o \ - microphone/drivers/alsathread.o + microphone/drivers/alsathread.o \ + audio/common/alsathread.o endif endif diff --git a/audio/common/alsa.h b/audio/common/alsa.h index dbc604a3107d..47fc9fce5e8e 100644 --- a/audio/common/alsa.h +++ b/audio/common/alsa.h @@ -35,18 +35,6 @@ typedef struct alsa_stream_info { bool can_pause; } alsa_stream_info_t; -typedef struct alsa_thread_info -{ - snd_pcm_t *pcm; - fifo_buffer_t *buffer; - sthread_t *worker_thread; - slock_t *fifo_lock; - scond_t *cond; - slock_t *cond_lock; - alsa_stream_info_t stream_info; - volatile bool thread_dead; -} alsa_thread_info_t; - int alsa_init_pcm(snd_pcm_t **pcm, const char* device, snd_pcm_stream_t stream, @@ -61,11 +49,6 @@ void *alsa_device_list_new(void *data); struct string_list *alsa_device_list_type_new(const char* type); void alsa_device_list_free(void *data, void *array_list_data); -void alsa_thread_free_info_members(alsa_thread_info_t *info); - -/** - * Sets the state of the PCM stream without updating the mic state - */ bool alsa_start_pcm(snd_pcm_t *pcm); bool alsa_stop_pcm(snd_pcm_t *pcm); diff --git a/audio/common/alsathread.c b/audio/common/alsathread.c new file mode 100644 index 000000000000..48a771f3042d --- /dev/null +++ b/audio/common/alsathread.c @@ -0,0 +1,45 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 Daniel De Matteis + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "alsathread.h" + +void alsa_thread_free_info_members(alsa_thread_info_t *info) +{ + if (info) + { + if (info->worker_thread) + { + slock_lock(info->cond_lock); + info->thread_dead = true; + slock_unlock(info->cond_lock); + sthread_join(info->worker_thread); + } + if (info->buffer) + fifo_free(info->buffer); + if (info->cond) + scond_free(info->cond); + if (info->fifo_lock) + slock_free(info->fifo_lock); + if (info->cond_lock) + slock_free(info->cond_lock); + if (info->pcm) + { + alsa_free_pcm(info->pcm); + } + } + /* Do NOT free() info itself; it's embedded within another struct + * that will be freed. */ +} \ No newline at end of file diff --git a/audio/common/alsathread.h b/audio/common/alsathread.h new file mode 100644 index 000000000000..dafa5be8e050 --- /dev/null +++ b/audio/common/alsathread.h @@ -0,0 +1,40 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 Daniel De Matteis + * Copyright (C) 2023 Jesse Talavera-Greenberg + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef RETROARCH_ALSATHREAD_H +#define RETROARCH_ALSATHREAD_H + +#include +#include +#include "queues/fifo_queue.h" +#include "rthreads/rthreads.h" +#include "./alsa.h" + +typedef struct alsa_thread_info +{ + snd_pcm_t *pcm; + fifo_buffer_t *buffer; + sthread_t *worker_thread; + slock_t *fifo_lock; + scond_t *cond; + slock_t *cond_lock; + alsa_stream_info_t stream_info; + volatile bool thread_dead; +} alsa_thread_info_t; + +void alsa_thread_free_info_members(alsa_thread_info_t *info); + +#endif diff --git a/audio/drivers/alsathread.c b/audio/drivers/alsathread.c index 3c6edd805d67..030f23dad03a 100644 --- a/audio/drivers/alsathread.c +++ b/audio/drivers/alsathread.c @@ -16,18 +16,16 @@ #include -#include - #include #include #include #include -#include #include #include "../audio_driver.h" #include "../common/alsa.h" /* For some common functions/types */ +#include "../common/alsathread.h" #include "../../verbosity.h" typedef struct alsa_thread @@ -98,34 +96,6 @@ static void alsa_worker_thread(void *data) RARCH_DBG("[ALSA] [playback thread %p]: Ending playback worker thread\n", thread_id); } -void alsa_thread_free_info_members(alsa_thread_info_t *info) -{ - if (info) - { - if (info->worker_thread) - { - slock_lock(info->cond_lock); - info->thread_dead = true; - slock_unlock(info->cond_lock); - sthread_join(info->worker_thread); - } - if (info->buffer) - fifo_free(info->buffer); - if (info->cond) - scond_free(info->cond); - if (info->fifo_lock) - slock_free(info->fifo_lock); - if (info->cond_lock) - slock_free(info->cond_lock); - if (info->pcm) - { - alsa_free_pcm(info->pcm); - } - } - /* Do NOT free() info itself; it's embedded within another struct - * that will be freed. */ -} - static bool alsa_thread_use_float(void *data) { alsa_thread_t *alsa = (alsa_thread_t*)data; diff --git a/microphone/drivers/alsathread.c b/microphone/drivers/alsathread.c index 97b4571827b0..5c4831971560 100644 --- a/microphone/drivers/alsathread.c +++ b/microphone/drivers/alsathread.c @@ -17,7 +17,8 @@ #include #include -#include "audio/common/alsa.h" /* For some common functions/types */ +#include "audio/common/alsa.h" +#include "audio/common/alsathread.h" #include "microphone/microphone_driver.h" #include "verbosity.h" #include "retro_assert.h" From 2d2e6ea66d864328590e8ccc64342a5f4be05d63 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 10 Feb 2023 08:57:38 -0500 Subject: [PATCH 228/309] Change return type of mic_driver_open_mic_internal to bool --- microphone/microphone_driver.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index d3505c5fe6cf..b4e0f2a3a991 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -66,7 +66,7 @@ unsigned mic_driver_get_sample_size(const retro_microphone_t *microphone) return (microphone->flags & MICROPHONE_FLAG_USE_FLOAT) ? sizeof(float) : sizeof(int16_t); } -static void mic_driver_open_mic_internal(retro_microphone_t* microphone); +static bool mic_driver_open_mic_internal(retro_microphone_t* microphone); bool microphone_driver_start(bool is_shutdown) { microphone_driver_state_t *mic_st = &mic_driver_st; @@ -81,9 +81,7 @@ bool microphone_driver_start(bool is_shutdown) /* The microphone context shouldn't have been created yet */ /* Now that the driver and driver context are ready, let's initialize the mic */ - mic_driver_open_mic_internal(microphone); - - if (microphone->microphone_context) + if (mic_driver_open_mic_internal(microphone)) { /* open_mic_internal will start the microphone if it's enabled */ RARCH_DBG("[Microphone]: Initialized a previously-pending microphone\n"); @@ -283,9 +281,8 @@ bool microphone_driver_init_internal(void *settings_data) /** * * @param microphone Handle to the microphone to init with a context - * Check the value of microphone->microphone_context to see if this function succeeded */ -static void mic_driver_open_mic_internal(retro_microphone_t* microphone) +static bool mic_driver_open_mic_internal(retro_microphone_t* microphone) { microphone_driver_state_t *mic_st = &mic_driver_st; settings_t *settings = config_get_ptr(); @@ -300,7 +297,7 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; if (!microphone || !mic_driver || !(mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE)) - return; + return false; microphone->sample_buffer_length = insamples_max * sizeof(int16_t); microphone->sample_buffer = @@ -337,10 +334,11 @@ static void mic_driver_open_mic_internal(retro_microphone_t* microphone) microphone->flags &= ~MICROPHONE_FLAG_PENDING; RARCH_LOG("[Microphone]: Initialized microphone\n"); - return; + return true; error: mic_driver_microphone_handle_free(microphone); RARCH_ERR("[Microphone]: Driver attempted to initialize the microphone but failed\n"); + return false; } void microphone_driver_close_mic(retro_microphone_t *microphone) @@ -628,9 +626,7 @@ retro_microphone_t *microphone_driver_open_mic(void) if (driver_context) { /* If the microphone driver is ready to open a microphone... */ - mic_driver_open_mic_internal(&mic_st->microphone); - - if (mic_st->microphone.microphone_context) /* If the microphone was successfully initialized... */ + if (mic_driver_open_mic_internal(&mic_st->microphone)) /* If the microphone was successfully initialized... */ RARCH_LOG("[Microphone]: Opened the requested microphone successfully\n"); else goto error; From 040d0f502a2ac35c14675a0a8bc01425f7987877 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 10 Feb 2023 10:13:13 -0500 Subject: [PATCH 229/309] First crack at resampling mic input --- microphone/microphone_driver.c | 179 ++++++++++++++++++++++++++++----- microphone/microphone_driver.h | 89 ++++++++++++++-- 2 files changed, 233 insertions(+), 35 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index b4e0f2a3a991..09407f0e84c0 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -24,6 +24,8 @@ #include "audio/conversion/float_to_s16.h" #include "list_special.h" #include "retro_assert.h" +#include "string/stdstring.h" +#include "audio/conversion/dual_mono.h" static microphone_driver_state_t mic_driver_st; @@ -220,13 +222,21 @@ static void mic_driver_microphone_handle_free(retro_microphone_t *microphone) /* Do NOT free the microphone handle itself! It's allocated statically! */ } +static enum resampler_quality microphone_driver_get_resampler_quality( + settings_t *settings) +{ + if (settings) + return (enum resampler_quality)settings->uints.audio_resampler_quality; + return RESAMPLER_QUALITY_DONTCARE; +} + bool microphone_driver_init_internal(void *settings_data) { settings_t *settings = (settings_t*)settings_data; microphone_driver_state_t *mic_st = &mic_driver_st; float slowmotion_ratio = settings->floats.slowmotion_ratio; bool verbosity_enabled = verbosity_is_enabled(); - size_t insamples_max = AUDIO_CHUNK_SIZE_NONBLOCKING * 1 * AUDIO_MAX_RATIO * slowmotion_ratio; + size_t max_frames = AUDIO_CHUNK_SIZE_NONBLOCKING * AUDIO_MAX_RATIO * slowmotion_ratio; if (!settings->bools.microphone_enable) { /* If the user has mic support turned off... */ @@ -245,14 +255,35 @@ bool microphone_driver_init_internal(void *settings_data) goto error; } - mic_st->input_samples_conv_buf_length = insamples_max; - mic_st->input_samples_conv_buf = (int16_t*)memalign_alloc(64, mic_driver_st.input_samples_conv_buf_length); - if (!mic_st->input_samples_conv_buf) + mic_st->input_frames_length = max_frames * sizeof(float); + mic_st->input_frames = (float*)memalign_alloc(64, mic_st->input_frames_length); + if (!mic_st->input_frames) + goto error; + + mic_st->converted_input_frames_length = max_frames * sizeof(float); + mic_st->converted_input_frames = (float*)memalign_alloc(64, mic_st->converted_input_frames_length); + if (!mic_st->converted_input_frames) + goto error; + + /* Need room for dual-mono frames */ + mic_st->dual_mono_frames_length = max_frames * sizeof(float) * 2; + mic_st->dual_mono_frames = (float*)memalign_alloc(64, mic_st->dual_mono_frames_length); + if (!mic_st->dual_mono_frames) + goto error; + + mic_st->resampled_frames_length = max_frames * sizeof(float) * 2; + mic_st->resampled_frames = (float*) memalign_alloc(64, mic_st->resampled_frames_length); + if (!mic_st->resampled_frames) goto error; - mic_st->input_samples_buf_length = insamples_max * sizeof(float); - mic_st->input_samples_buf = (float*)memalign_alloc(64, mic_driver_st.input_samples_buf_length); - if (!mic_st->input_samples_buf) + mic_st->resampled_mono_frames_length = max_frames * sizeof(float); + mic_st->resampled_mono_frames = (float*) memalign_alloc(64, mic_st->resampled_mono_frames_length); + if (!mic_st->resampled_mono_frames) + goto error; + + mic_st->final_frames_length = max_frames * sizeof(int16_t); + mic_st->final_frames = (int16_t*) memalign_alloc(64, mic_st->final_frames_length); + if (!mic_st->final_frames) goto error; if (!mic_st->driver || !mic_st->driver->init) @@ -262,6 +293,26 @@ bool microphone_driver_init_internal(void *settings_data) if (!mic_st->driver_context) goto error; + if (!string_is_empty(settings->arrays.audio_resampler)) + strlcpy(mic_st->resampler_ident, + settings->arrays.audio_resampler, + sizeof(mic_st->resampler_ident)); + else + mic_st->resampler_ident[0] = '\0'; + + mic_st->resampler_quality = microphone_driver_get_resampler_quality(settings); + + if (!retro_resampler_realloc( + &mic_st->resampler_data, + &mic_st->resampler, + mic_st->resampler_ident, + mic_st->resampler_quality, + mic_st->source_ratio_original)) + { + RARCH_ERR("[Microphone]: Failed to initialize resampler \"%s\".\n", mic_st->resampler_ident); + goto error; + } + RARCH_LOG("[Microphone]: Initialized microphone driver\n"); /* The mic driver was initialized, now we're ready to open mics */ @@ -463,11 +514,32 @@ static void microphone_driver_flush( int16_t *frames, size_t num_frames, bool is_slowmotion, bool is_fastmotion) { + // TODO: Get rid of some of these checks, microphone_driver_read does them + if (!mic_st || !mic_st->driver || !mic_st->driver_context) + /* If the driver state is invalid... */ + return; + + if (!(mic_st->flags & MICROPHONE_FLAG_ACTIVE)) + /* If mic support isn't on... */ + return; + + if (!microphone || !microphone->microphone_context) + /* If the mic isn't initialized... */ + return; + + if (!(microphone->flags & MICROPHONE_FLAG_ACTIVE) + || !(microphone->flags & MICROPHONE_FLAG_ENABLED) + || (microphone->flags & MICROPHONE_FLAG_PENDING) + || (microphone->flags & MICROPHONE_FLAG_SUSPENDED)) + return; + + + if (microphone->flags & MICROPHONE_FLAG_PENDING) if (mic_st && /* If the driver state is valid... */ (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) && /* ...and mic support is on... */ mic_st->driver && - mic_st->driver_context && /* ...and the mic driver is initialized... */ - mic_st->input_samples_buf && /* ...with scratch space... */ + mic_st->driver_context && /* ...and the mic driver is initialized... */ + mic_st->input_frames && /* ...with scratch space... */ microphone && (microphone->flags & MICROPHONE_FLAG_ACTIVE) && /* ...and the mic itself is initialized... */ (microphone->flags & MICROPHONE_FLAG_ENABLED) && /* ...and enabled... */ @@ -483,31 +555,55 @@ static void microphone_driver_flush( mic_st->driver_context, microphone->microphone_context)) { - void *buffer_source = NULL; + struct resampler_data resampler_data; unsigned sample_size = mic_driver_get_sample_size(microphone); - size_t bytes_to_read = MIN(mic_st->input_samples_buf_length, num_frames * sample_size); + size_t bytes_to_read = MIN(mic_st->input_frames_length, num_frames * sample_size); ssize_t bytes_read = mic_st->driver->read( mic_st->driver_context, microphone->microphone_context, - mic_st->input_samples_buf, + mic_st->input_frames, bytes_to_read); /* First, get the most recent mic data */ if (bytes_read <= 0) return; + resampler_data.input_frames = bytes_read / sample_size; + /* This is in frames, not samples or bytes; + * we're up-channeling the audio to stereo, + * so this number still applies. */ + + resampler_data.output_frames = 0; + /* The resampler sets the value of output_frames */ + /* First we need to format the input for the resampler. */ if (microphone->flags & MICROPHONE_FLAG_USE_FLOAT) - { - convert_float_to_s16(mic_st->input_samples_conv_buf, mic_st->input_samples_buf, bytes_read / sample_size); - buffer_source = mic_st->input_samples_conv_buf; + {/* If this mic provides floating-point samples... */ + + /* Samples are already in floating-point, so we just need to up-channel them. */ + convert_to_dual_mono_float(mic_st->dual_mono_frames, mic_st->input_frames, resampler_data.input_frames); } else { - buffer_source = mic_st->input_samples_buf; + /* Samples are 16-bit, so we need to convert them first. */ + convert_s16_to_float(mic_st->converted_input_frames, mic_st->input_frames, resampler_data.input_frames, 1.0f); + convert_to_dual_mono_float(mic_st->dual_mono_frames, mic_st->converted_input_frames, resampler_data.input_frames); } - memcpy(frames, buffer_source, num_frames * sizeof(int16_t)); + /* Now we resample the mic data. */ + resampler_data.data_in = mic_st->dual_mono_frames; + resampler_data.data_out = mic_st->resampled_frames; + mic_st->resampler->process(mic_st->resampler_data, &resampler_data); + + /* Next, we convert the resampled data back to mono... */ + convert_to_mono_float_left(mic_st->resampled_mono_frames, mic_st->resampled_frames, resampler_data.input_frames); + /* Why the left channel? No particular reason. + * Left and right channels are the same in this case anyway. */ + + /* Finally, we convert the audio back to 16-bit ints, as the mic interface requires. */ + convert_float_to_s16(mic_st->final_frames, mic_st->resampled_mono_frames, resampler_data.input_frames); + + memcpy(frames, mic_st->final_frames, resampler_data.input_frames * sizeof(int16_t)); } } @@ -553,7 +649,7 @@ int microphone_driver_read(retro_microphone_t *microphone, int16_t* frames, size if (!(runloop_flags & RUNLOOP_FLAG_PAUSED) && (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) - && mic_st->input_samples_buf) + && mic_st->input_frames) /* If the game is running, the audio driver and mic are running, * and the input sample buffer is valid... */ microphone_driver_flush(mic_st, @@ -686,14 +782,45 @@ bool microphone_driver_deinit(void) mic_st->driver_context = NULL; } - if (mic_st->input_samples_conv_buf) - memalign_free(mic_st->input_samples_conv_buf); - mic_st->input_samples_conv_buf = NULL; - - if (mic_st->input_samples_buf) - memalign_free(mic_st->input_samples_buf); - mic_st->input_samples_buf = NULL; - mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + if (mic_st->input_frames) + memalign_free(mic_st->input_frames); + mic_st->input_frames = NULL; + mic_st->input_frames_length = 0; + + if (mic_st->converted_input_frames) + memalign_free(mic_st->converted_input_frames); + mic_st->converted_input_frames = NULL; + mic_st->converted_input_frames_length = 0; + + if (mic_st->dual_mono_frames) + memalign_free(mic_st->dual_mono_frames); + mic_st->dual_mono_frames = NULL; + mic_st->dual_mono_frames_length = 0; + + if (mic_st->resampled_frames) + memalign_free(mic_st->resampled_frames); + mic_st->resampled_frames = NULL; + mic_st->resampled_frames_length = 0; + + if (mic_st->resampled_mono_frames) + memalign_free(mic_st->resampled_mono_frames); + mic_st->resampled_mono_frames = NULL; + mic_st->resampled_mono_frames_length = 0; + + if (mic_st->final_frames) + memalign_free(mic_st->final_frames); + mic_st->final_frames = NULL; + mic_st->final_frames_length = 0; + + if (mic_st->resampler && mic_st->resampler_data) + mic_st->resampler->free(mic_st->resampler_data); + + mic_st->resampler = NULL; + mic_st->resampler_data = NULL; + mic_st->resampler_quality = RESAMPLER_QUALITY_DONTCARE; + mic_st->flags &= ~MICROPHONE_DRIVER_FLAG_ACTIVE; + + memset(mic_st->resampler_ident, '\0', sizeof(mic_st->resampler_ident)); return true; } diff --git a/microphone/microphone_driver.h b/microphone/microphone_driver.h index f0d0cc965951..d2d0bc08e689 100644 --- a/microphone/microphone_driver.h +++ b/microphone/microphone_driver.h @@ -21,6 +21,7 @@ #include #include #include "audio/audio_defines.h" +#include "audio/audio_resampler.h" #define MAX_SUPPORTED_MICROPHONES 8 #define MICROPHONE_BUFFER_FREE_SAMPLES_COUNT (8 * 1024) @@ -377,22 +378,79 @@ typedef struct microphone_driver bool (*mic_use_float)(const void *driver_context, const void *microphone_context); } microphone_driver_t; -typedef struct +typedef struct microphone_driver_state { - struct string_list *devices_list; + /** + * The buffer that receives samples from the microphone backend, + * before they're processed. + */ + void *input_frames; + + /** + * The length of \c input_frames in bytes. + */ + size_t input_frames_length; + + /** + * The buffer that receives samples that have been + * converted to floating-point format, if necessary. + */ + float *converted_input_frames; + + /** + * The length of \c converted_input_frames in bytes. + */ + size_t converted_input_frames_length; + + /** + * The buffer that stores microphone samples + * after they've been converted to floating-point format + * and up-channeled to dual-mono. + */ + float *dual_mono_frames; + + /** + * The length of \c dual_mono_frames in bytes. + */ + size_t dual_mono_frames_length; + + /** + * The buffer that stores microphone samples + * after they've been converted to float, + * up-channeled to dual-mono, + * and resampled. + */ + float *resampled_frames; + + /** + * The length of \c resampled_frames in bytes. + */ + size_t resampled_frames_length; + /** - * A scratch buffer for audio input to be received. + * The buffer that stores microphone samples + * after they've been resampled + * and converted to mono. */ - void *input_samples_buf; - size_t input_samples_buf_length; + float *resampled_mono_frames; /** - * A scratch buffer for processed audio output to be converted to 16-bit, - * so that it can be sent to the core. + * The length of \c resampled_mono_frames in bytes. */ - int16_t *input_samples_conv_buf; - size_t input_samples_conv_buf_length; + size_t resampled_mono_frames_length; + + /** + * The buffer that contains the microphone input + * after it's been totally processed and converted. + * The contents of this buffer will be provided to the core. + */ + int16_t *final_frames; + + /** + * The length of \c final_frames in bytes. + */ + size_t final_frames_length; /** * The current microphone driver. @@ -400,6 +458,8 @@ typedef struct */ const microphone_driver_t *driver; + struct string_list *devices_list; + /** * Opaque handle to the driver-specific context. */ @@ -414,6 +474,17 @@ typedef struct retro_microphone_t microphone; enum microphone_driver_state_flags flags; + + enum resampler_quality resampler_quality; + + char resampler_ident[64]; + + const retro_resampler_t *resampler; + + void *resampler_data; + + double source_ratio_original; + double source_ratio_current; } microphone_driver_state_t; /** From 1f31eeefb21e21d3983ef8da05ae51e5ed3e30f2 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 10 Feb 2023 10:20:48 -0500 Subject: [PATCH 230/309] Remove an extraneous check - I think something distracted me when I was writing this line --- microphone/microphone_driver.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/microphone/microphone_driver.c b/microphone/microphone_driver.c index 09407f0e84c0..2508411a87c8 100644 --- a/microphone/microphone_driver.c +++ b/microphone/microphone_driver.c @@ -533,8 +533,6 @@ static void microphone_driver_flush( || (microphone->flags & MICROPHONE_FLAG_SUSPENDED)) return; - - if (microphone->flags & MICROPHONE_FLAG_PENDING) if (mic_st && /* If the driver state is valid... */ (mic_st->flags & MICROPHONE_DRIVER_FLAG_ACTIVE) && /* ...and mic support is on... */ mic_st->driver && From 05a51dda9712f8f7357527d004af79b9cac662e7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 10 Feb 2023 11:37:34 -0500 Subject: [PATCH 231/309] Add stereo/mono conversion functions --- Makefile.common | 4 +- .../audio/conversion/mono_to_stereo_float.c | 39 +++++++++++ .../audio/conversion/stereo_to_mono_float.c | 38 ++++++++++ .../include/audio/conversion/dual_mono.h | 69 +++++++++++++++++++ 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 libretro-common/audio/conversion/mono_to_stereo_float.c create mode 100644 libretro-common/audio/conversion/stereo_to_mono_float.c create mode 100644 libretro-common/include/audio/conversion/dual_mono.h diff --git a/Makefile.common b/Makefile.common index 81eeb2b2803c..7a25148810ad 100644 --- a/Makefile.common +++ b/Makefile.common @@ -958,7 +958,9 @@ ifeq ($(HAVE_NEON),1) endif OBJ += $(LIBRETRO_COMM_DIR)/audio/conversion/s16_to_float.o \ - $(LIBRETRO_COMM_DIR)/audio/conversion/float_to_s16.o + $(LIBRETRO_COMM_DIR)/audio/conversion/float_to_s16.o \ + $(LIBRETRO_COMM_DIR)/audio/conversion/mono_to_stereo_float.o \ + $(LIBRETRO_COMM_DIR)/audio/conversion/stereo_to_mono_float.o \ ifeq ($(HAVE_RWAV), 1) DEFINES += -DHAVE_RWAV diff --git a/libretro-common/audio/conversion/mono_to_stereo_float.c b/libretro-common/audio/conversion/mono_to_stereo_float.c new file mode 100644 index 000000000000..4a46f553dc7c --- /dev/null +++ b/libretro-common/audio/conversion/mono_to_stereo_float.c @@ -0,0 +1,39 @@ +/* Copyright (C) 2010-2023 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (mono_to_stereo.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include + +#include