mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-01 13:44:21 +00:00
AudioServer: Handle missing audio device gracefully
On several platforms, we don't yet have audio support, so audio devices are missing. Instead of having AudioServer crash repeatedly, and not having the ability to open any app that relies on it, we should instead handle missing devices gracefully. This implementation is minimal, audio device hotplugging support and such should be implemented together with multi-device support. AudioServer will start up and seem to function normally without an audio device, but it will pretend the device has a sample rate of 44.1 kHz and all audio input is discarded.
This commit is contained in:
parent
e254810d0a
commit
9f0ab281ce
|
@ -20,7 +20,7 @@
|
|||
|
||||
namespace AudioServer {
|
||||
|
||||
Mixer::Mixer(NonnullRefPtr<Core::ConfigFile> config, NonnullOwnPtr<Core::File> device)
|
||||
Mixer::Mixer(NonnullRefPtr<Core::ConfigFile> config, OwnPtr<Core::File> device)
|
||||
: m_device(move(device))
|
||||
, m_sound_thread(Threading::Thread::construct(
|
||||
[this] {
|
||||
|
@ -95,7 +95,8 @@ void Mixer::mix()
|
|||
|
||||
// Even though it's not realistic, the user expects no sound at 0%.
|
||||
if (m_muted || m_main_volume < 0.01) {
|
||||
m_device->write_until_depleted(m_zero_filled_buffer).release_value_but_fixme_should_propagate_errors();
|
||||
if (m_device)
|
||||
m_device->write_until_depleted(m_zero_filled_buffer).release_value_but_fixme_should_propagate_errors();
|
||||
} else {
|
||||
FixedMemoryStream stream { m_stream_buffer.span() };
|
||||
|
||||
|
@ -113,8 +114,9 @@ void Mixer::mix()
|
|||
|
||||
auto buffered_bytes = MUST(stream.tell());
|
||||
VERIFY(buffered_bytes == m_stream_buffer.size());
|
||||
m_device->write_until_depleted({ m_stream_buffer.data(), buffered_bytes })
|
||||
.release_value_but_fixme_should_propagate_errors();
|
||||
if (m_device)
|
||||
m_device->write_until_depleted({ m_stream_buffer.data(), buffered_bytes })
|
||||
.release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,6 +154,9 @@ void Mixer::set_muted(bool muted)
|
|||
|
||||
int Mixer::audiodevice_set_sample_rate(u32 sample_rate)
|
||||
{
|
||||
if (!m_device)
|
||||
return ENOENT;
|
||||
|
||||
int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_SET_SAMPLE_RATE, sample_rate);
|
||||
if (code != 0)
|
||||
dbgln("Error while setting sample rate to {}: ioctl error: {}", sample_rate, strerror(errno));
|
||||
|
@ -165,6 +170,11 @@ u32 Mixer::audiodevice_get_sample_rate() const
|
|||
{
|
||||
if (m_cached_sample_rate.has_value())
|
||||
return m_cached_sample_rate.value();
|
||||
|
||||
// We pretend that a non-existent device has a common sample rate (instead of returning something like 0 that would break clients).
|
||||
if (!m_device)
|
||||
return 44100;
|
||||
|
||||
u32 sample_rate = 0;
|
||||
int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_GET_SAMPLE_RATE, &sample_rate);
|
||||
if (code != 0)
|
||||
|
|
|
@ -42,7 +42,12 @@ public:
|
|||
static ErrorOr<NonnullRefPtr<Mixer>> try_create(NonnullRefPtr<Core::ConfigFile> config)
|
||||
{
|
||||
// FIXME: Allow AudioServer to use other audio channels as well
|
||||
auto device = TRY(Core::File::open("/dev/audio/0"sv, Core::File::OpenMode::Write));
|
||||
auto maybe_device = Core::File::open("/dev/audio/0"sv, Core::File::OpenMode::Write);
|
||||
OwnPtr<Core::File> device;
|
||||
if (maybe_device.is_error())
|
||||
dbgln("Couldn't open first audio channel: {}", maybe_device.error());
|
||||
else
|
||||
device = maybe_device.release_value();
|
||||
return adopt_nonnull_ref_or_enomem(new (nothrow) Mixer(move(config), move(device)));
|
||||
}
|
||||
|
||||
|
@ -61,7 +66,7 @@ public:
|
|||
u32 audiodevice_get_sample_rate() const;
|
||||
|
||||
private:
|
||||
Mixer(NonnullRefPtr<Core::ConfigFile> config, NonnullOwnPtr<Core::File> device);
|
||||
Mixer(NonnullRefPtr<Core::ConfigFile> config, OwnPtr<Core::File> device);
|
||||
|
||||
void request_setting_sync();
|
||||
|
||||
|
@ -69,7 +74,7 @@ private:
|
|||
Threading::Mutex m_pending_mutex;
|
||||
Threading::ConditionVariable m_mixing_necessary { m_pending_mutex };
|
||||
|
||||
NonnullOwnPtr<Core::File> m_device;
|
||||
OwnPtr<Core::File> m_device;
|
||||
mutable Optional<u32> m_cached_sample_rate {};
|
||||
|
||||
NonnullRefPtr<Threading::Thread> m_sound_thread;
|
||||
|
|
|
@ -19,7 +19,12 @@ ErrorOr<int> serenity_main(Main::Arguments)
|
|||
|
||||
auto config = TRY(Core::ConfigFile::open_for_app("Audio", Core::ConfigFile::AllowWriting::Yes));
|
||||
TRY(Core::System::unveil(config->filename(), "rwc"sv));
|
||||
TRY(Core::System::unveil("/dev/audio", "wc"));
|
||||
|
||||
auto audio_unveil_result = Core::System::unveil("/dev/audio", "wc");
|
||||
// System may not have audio devices, which we handle gracefully.
|
||||
if (audio_unveil_result.is_error())
|
||||
dbgln("Couldn't unveil audio devices: {}", audio_unveil_result.error());
|
||||
|
||||
TRY(Core::System::unveil(nullptr, nullptr));
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
|
|
Loading…
Reference in a new issue