diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index 20ee5105..264e7b57 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -590,6 +590,15 @@ and commit the results. Refresh with the following command: Sets the performance mode as pointed out by Oboe's documentation. + + oboe.error-recovery-mode + str + Reconnect + Reconnect, Stop + + Sets the error recovery mode when audio device error such as earphone disconnection occurred. It reconnects by default (same as OpenSLES behavior), but can be stopped if Stop is specified. + + oss.device str diff --git a/src/drivers/fluid_oboe.cpp b/src/drivers/fluid_oboe.cpp index eb7de5dc..f95af01f 100644 --- a/src/drivers/fluid_oboe.cpp +++ b/src/drivers/fluid_oboe.cpp @@ -43,6 +43,7 @@ using namespace oboe; constexpr int NUM_CHANNELS = 2; class OboeAudioStreamCallback; +class OboeAudioStreamErrorCallback; /** fluid_oboe_audio_driver_t * @@ -55,7 +56,16 @@ typedef struct fluid_synth_t *synth = nullptr; bool cont = false; std::unique_ptr oboe_callback; + std::unique_ptr oboe_error_callback; std::shared_ptr stream; + + double sample_rate; + int is_sample_format_float; + int device_id; + int sharing_mode; // 0: Shared, 1: Exclusive + int performance_mode; // 0: None, 1: PowerSaving, 2: LowLatency + oboe::SampleRateConversionQuality srate_conversion_quality; + int error_recovery_mode; // 0: Reconnect, 1: Stop } fluid_oboe_audio_driver_t; @@ -93,20 +103,34 @@ private: void *user_data; }; +class OboeAudioStreamErrorCallback : public AudioStreamErrorCallback +{ + fluid_oboe_audio_driver_t *dev; + +public: + OboeAudioStreamErrorCallback(fluid_oboe_audio_driver_t *dev) : dev(dev) {} + + void onErrorAfterClose(AudioStream *stream, Result result); +}; + +constexpr char OBOE_ID[] = "audio.oboe.id"; +constexpr char SHARING_MODE[] = "audio.oboe.sharing-mode"; +constexpr char PERF_MODE[] = "audio.oboe.performance-mode"; constexpr char SRCQ_SET[] = "audio.oboe.sample-rate-conversion-quality"; +constexpr char RECOVERY_MODE[] = "audio.oboe.error-recovery-mode"; void fluid_oboe_audio_driver_settings(fluid_settings_t *settings) { - fluid_settings_register_int(settings, "audio.oboe.id", 0, 0, 0x7FFFFFFF, 0); + fluid_settings_register_int(settings, OBOE_ID, 0, 0, 0x7FFFFFFF, 0); - fluid_settings_register_str(settings, "audio.oboe.sharing-mode", "Shared", 0); - fluid_settings_add_option(settings, "audio.oboe.sharing-mode", "Shared"); - fluid_settings_add_option(settings, "audio.oboe.sharing-mode", "Exclusive"); + fluid_settings_register_str(settings, SHARING_MODE, "Shared", 0); + fluid_settings_add_option(settings, SHARING_MODE, "Shared"); + fluid_settings_add_option(settings, SHARING_MODE, "Exclusive"); - fluid_settings_register_str(settings, "audio.oboe.performance-mode", "None", 0); - fluid_settings_add_option(settings, "audio.oboe.performance-mode", "None"); - fluid_settings_add_option(settings, "audio.oboe.performance-mode", "PowerSaving"); - fluid_settings_add_option(settings, "audio.oboe.performance-mode", "LowLatency"); + fluid_settings_register_str(settings, PERF_MODE, "None", 0); + fluid_settings_add_option(settings, PERF_MODE, "None"); + fluid_settings_add_option(settings, PERF_MODE, "PowerSaving"); + fluid_settings_add_option(settings, PERF_MODE, "LowLatency"); fluid_settings_register_str(settings, SRCQ_SET, "Medium", 0); fluid_settings_add_option(settings, SRCQ_SET, "None"); @@ -115,6 +139,10 @@ void fluid_oboe_audio_driver_settings(fluid_settings_t *settings) fluid_settings_add_option(settings, SRCQ_SET, "Medium"); fluid_settings_add_option(settings, SRCQ_SET, "High"); fluid_settings_add_option(settings, SRCQ_SET, "Best"); + + fluid_settings_register_str(settings, RECOVERY_MODE, "Reconnect", 0); + fluid_settings_add_option(settings, RECOVERY_MODE, "Reconnect"); + fluid_settings_add_option(settings, RECOVERY_MODE, "Stop"); } static oboe::SampleRateConversionQuality get_srate_conversion_quality(fluid_settings_t *settings) @@ -157,6 +185,28 @@ static oboe::SampleRateConversionQuality get_srate_conversion_quality(fluid_sett return q; } +Result +fluid_oboe_connect_or_reconnect(fluid_oboe_audio_driver_t *dev) +{ + AudioStreamBuilder builder; + builder.setDeviceId(dev->device_id) + ->setDirection(Direction::Output) + ->setChannelCount(NUM_CHANNELS) + ->setSampleRate(dev->sample_rate) + ->setFormat(dev->is_sample_format_float ? AudioFormat::Float : AudioFormat::I16) + ->setSharingMode(dev->sharing_mode == 1 ? SharingMode::Exclusive : SharingMode::Shared) + ->setPerformanceMode( + dev->performance_mode == 1 ? PerformanceMode::PowerSaving : + dev->performance_mode == 2 ? PerformanceMode::LowLatency : PerformanceMode::None) + ->setUsage(Usage::Media) + ->setContentType(ContentType::Music) + ->setCallback(dev->oboe_callback.get()) + ->setErrorCallback(dev->oboe_error_callback.get()) + ->setSampleRateConversionQuality(dev->srate_conversion_quality); + + return builder.openStream(dev->stream); +} + /* * new_fluid_oboe_audio_driver */ @@ -168,44 +218,24 @@ new_fluid_oboe_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth) try { Result result; - AudioStreamBuilder builder_obj; - AudioStreamBuilder *builder = &builder_obj; - - double sample_rate; - int is_sample_format_float; - int device_id; - int sharing_mode; // 0: Shared, 1: Exclusive - int performance_mode; // 0: None, 1: PowerSaving, 2: LowLatency - dev = new fluid_oboe_audio_driver_t(); dev->synth = synth; dev->oboe_callback = std::make_unique(dev); + dev->oboe_error_callback = std::make_unique(dev); - fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate); - is_sample_format_float = fluid_settings_str_equal(settings, "audio.sample-format", "float"); - fluid_settings_getint(settings, "audio.oboe.id", &device_id); - sharing_mode = - fluid_settings_str_equal(settings, "audio.oboe.sharing-mode", "Exclusive") ? 1 : 0; - performance_mode = - fluid_settings_str_equal(settings, "audio.oboe.performance-mode", "PowerSaving") ? 1 : - fluid_settings_str_equal(settings, "audio.oboe.performance-mode", "LowLatency") ? 2 : 0; + fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate); + dev->is_sample_format_float = fluid_settings_str_equal(settings, "audio.sample-format", "float"); + fluid_settings_getint(settings, OBOE_ID, &dev->device_id); + dev->sharing_mode = + fluid_settings_str_equal(settings, SHARING_MODE, "Exclusive") ? 1 : 0; + dev->performance_mode = + fluid_settings_str_equal(settings, PERF_MODE, "PowerSaving") ? 1 : + fluid_settings_str_equal(settings, PERF_MODE, "LowLatency") ? 2 : 0; + dev->srate_conversion_quality = get_srate_conversion_quality(settings); + dev->error_recovery_mode = fluid_settings_str_equal(settings, RECOVERY_MODE, "Stop") ? 1 : 0; - builder->setDeviceId(device_id) - ->setDirection(Direction::Output) - ->setChannelCount(NUM_CHANNELS) - ->setSampleRate(sample_rate) - ->setFormat(is_sample_format_float ? AudioFormat::Float : AudioFormat::I16) - ->setSharingMode(sharing_mode == 1 ? SharingMode::Exclusive : SharingMode::Shared) - ->setPerformanceMode( - performance_mode == 1 ? PerformanceMode::PowerSaving : - performance_mode == 2 ? PerformanceMode::LowLatency : PerformanceMode::None) - ->setUsage(Usage::Media) - ->setContentType(ContentType::Music) - ->setCallback(dev->oboe_callback.get()) - ->setSampleRateConversionQuality(get_srate_conversion_quality(settings)); - - result = builder->openStream(dev->stream); + result = fluid_oboe_connect_or_reconnect(dev); if(result != Result::OK) { @@ -269,5 +299,38 @@ void delete_fluid_oboe_audio_driver(fluid_audio_driver_t *p) delete dev; } + +void +OboeAudioStreamErrorCallback::onErrorAfterClose(AudioStream *stream, Result result) +{ + if(dev->error_recovery_mode == 1) // Stop + { + FLUID_LOG(FLUID_ERR, "Oboe driver encountered an error (such as earphone unplugged). Stopped."); + dev->stream.reset(); + return; + } + else + { + FLUID_LOG(FLUID_WARN, "Oboe driver encountered an error (such as earphone unplugged). Recovering..."); + } + + result = fluid_oboe_connect_or_reconnect(dev); + + if(result != Result::OK) + { + FLUID_LOG(FLUID_ERR, "Unable to reconnect Oboe audio stream"); + return; // cannot do anything further + } + + // start the new stream. + result = dev->stream->start(); + + if(result != Result::OK) + { + FLUID_LOG(FLUID_ERR, "Unable to restart Oboe audio stream"); + return; // cannot do anything further + } +} + #endif // OBOE_SUPPORT