Serenity Operating System
at master 192 lines 6.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "Mixer.h" 9#include <AK/Array.h> 10#include <AK/Format.h> 11#include <AK/MemoryStream.h> 12#include <AK/NumericLimits.h> 13#include <AudioServer/ConnectionFromClient.h> 14#include <AudioServer/Mixer.h> 15#include <LibCore/ConfigFile.h> 16#include <LibCore/DeprecatedFile.h> 17#include <LibCore/Timer.h> 18#include <pthread.h> 19#include <sys/ioctl.h> 20 21namespace AudioServer { 22 23Mixer::Mixer(NonnullRefPtr<Core::ConfigFile> config) 24 // FIXME: Allow AudioServer to use other audio channels as well 25 : m_device(Core::DeprecatedFile::construct("/dev/audio/0", this)) 26 , m_sound_thread(Threading::Thread::construct( 27 [this] { 28 mix(); 29 return 0; 30 }, 31 "AudioServer[mixer]"sv)) 32 , m_config(move(config)) 33{ 34 if (!m_device->open(Core::OpenMode::WriteOnly)) { 35 dbgln("Can't open audio device: {}", m_device->error_string()); 36 return; 37 } 38 39 m_muted = m_config->read_bool_entry("Master", "Mute", false); 40 m_main_volume = static_cast<double>(m_config->read_num_entry("Master", "Volume", 100)) / 100.0; 41 42 m_sound_thread->start(); 43} 44 45NonnullRefPtr<ClientAudioStream> Mixer::create_queue(ConnectionFromClient& client) 46{ 47 auto queue = adopt_ref(*new ClientAudioStream(client)); 48 { 49 Threading::MutexLocker const locker(m_pending_mutex); 50 m_pending_mixing.append(*queue); 51 } 52 // Signal the mixer thread to start back up, in case nobody was connected before. 53 m_mixing_necessary.signal(); 54 55 return queue; 56} 57 58void Mixer::mix() 59{ 60 decltype(m_pending_mixing) active_mix_queues; 61 62 for (;;) { 63 { 64 Threading::MutexLocker const locker(m_pending_mutex); 65 // While we have nothing to mix, wait on the condition. 66 m_mixing_necessary.wait_while([this, &active_mix_queues]() { return m_pending_mixing.is_empty() && active_mix_queues.is_empty(); }); 67 if (!m_pending_mixing.is_empty()) { 68 active_mix_queues.extend(move(m_pending_mixing)); 69 m_pending_mixing.clear(); 70 } 71 } 72 73 active_mix_queues.remove_all_matching([&](auto& entry) { return !entry->is_connected(); }); 74 75 Array<Audio::Sample, HARDWARE_BUFFER_SIZE> mixed_buffer; 76 77 m_main_volume.advance_time(); 78 79 // Mix the buffers together into the output 80 for (auto& queue : active_mix_queues) { 81 if (!queue->client()) { 82 queue->clear(); 83 continue; 84 } 85 queue->volume().advance_time(); 86 87 for (auto& mixed_sample : mixed_buffer) { 88 Audio::Sample sample; 89 if (!queue->get_next_sample(sample)) 90 break; 91 if (queue->is_muted()) 92 continue; 93 sample.log_multiply(SAMPLE_HEADROOM); 94 sample.log_multiply(static_cast<float>(queue->volume())); 95 mixed_sample += sample; 96 } 97 } 98 99 // Even though it's not realistic, the user expects no sound at 0%. 100 if (m_muted || m_main_volume < 0.01) { 101 m_device->write(m_zero_filled_buffer.data(), static_cast<int>(m_zero_filled_buffer.size())); 102 } else { 103 FixedMemoryStream stream { m_stream_buffer.span() }; 104 105 for (auto& mixed_sample : mixed_buffer) { 106 mixed_sample.log_multiply(static_cast<float>(m_main_volume)); 107 mixed_sample.clip(); 108 109 LittleEndian<i16> out_sample; 110 out_sample = static_cast<i16>(mixed_sample.left * NumericLimits<i16>::max()); 111 MUST(stream.write_value(out_sample)); 112 113 out_sample = static_cast<i16>(mixed_sample.right * NumericLimits<i16>::max()); 114 MUST(stream.write_value(out_sample)); 115 } 116 117 auto buffered_bytes = MUST(stream.tell()); 118 VERIFY(buffered_bytes == m_stream_buffer.size()); 119 m_device->write(m_stream_buffer.data(), static_cast<int>(buffered_bytes)); 120 } 121 } 122} 123 124void Mixer::set_main_volume(double volume) 125{ 126 if (volume < 0) 127 m_main_volume = 0; 128 else if (volume > 2) 129 m_main_volume = 2; 130 else 131 m_main_volume = volume; 132 133 m_config->write_num_entry("Master", "Volume", static_cast<int>(volume * 100)); 134 request_setting_sync(); 135 136 ConnectionFromClient::for_each([&](ConnectionFromClient& client) { 137 client.did_change_main_mix_volume({}, main_volume()); 138 }); 139} 140 141void Mixer::set_muted(bool muted) 142{ 143 if (m_muted == muted) 144 return; 145 m_muted = muted; 146 147 m_config->write_bool_entry("Master", "Mute", m_muted); 148 request_setting_sync(); 149 150 ConnectionFromClient::for_each([muted](ConnectionFromClient& client) { 151 client.did_change_main_mix_muted_state({}, muted); 152 }); 153} 154 155int Mixer::audiodevice_set_sample_rate(u32 sample_rate) 156{ 157 int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_SET_SAMPLE_RATE, sample_rate); 158 if (code != 0) 159 dbgln("Error while setting sample rate to {}: ioctl error: {}", sample_rate, strerror(errno)); 160 return code; 161} 162 163u32 Mixer::audiodevice_get_sample_rate() const 164{ 165 u32 sample_rate = 0; 166 int code = ioctl(m_device->fd(), SOUNDCARD_IOCTL_GET_SAMPLE_RATE, &sample_rate); 167 if (code != 0) 168 dbgln("Error while getting sample rate: ioctl error: {}", strerror(errno)); 169 return sample_rate; 170} 171 172void Mixer::request_setting_sync() 173{ 174 if (m_config_write_timer.is_null() || !m_config_write_timer->is_active()) { 175 m_config_write_timer = Core::Timer::create_single_shot( 176 AUDIO_CONFIG_WRITE_INTERVAL, 177 [this] { 178 if (auto result = m_config->sync(); result.is_error()) 179 dbgln("Failed to write audio mixer config: {}", result.error()); 180 }, 181 this) 182 .release_value_but_fixme_should_propagate_errors(); 183 m_config_write_timer->start(); 184 } 185} 186 187ClientAudioStream::ClientAudioStream(ConnectionFromClient& client) 188 : m_client(client) 189{ 190} 191 192}