mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 07:12:43 +00:00 
			
		
		
		
	 a089125d0f
			
		
	
	
		a089125d0f
		
	
	
	
	
		
			
			Use a pthread_cond_t to have the ASMixer thread wait until a client has connected and added a buffer queue to the "pending mixing" vector. This solves the long-standing issue of the system "idling" at ~8% CPU.
		
			
				
	
	
		
			126 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <AK/BufferStream.h>
 | |
| #include <AudioServer/ASClientConnection.h>
 | |
| #include <AudioServer/ASMixer.h>
 | |
| #include <limits>
 | |
| #include <pthread.h>
 | |
| 
 | |
| ASMixer::ASMixer()
 | |
|     : m_device(CFile::construct("/dev/audio", this))
 | |
|     , m_sound_thread(
 | |
|           [this] {
 | |
|               mix();
 | |
|               return 0;
 | |
|           },
 | |
|           "AudioServer[mixer]")
 | |
| {
 | |
|     if (!m_device->open(CIODevice::WriteOnly)) {
 | |
|         dbgprintf("Can't open audio device: %s\n", m_device->error_string());
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     pthread_mutex_init(&m_pending_mutex, nullptr);
 | |
|     pthread_cond_init(&m_pending_cond, nullptr);
 | |
| 
 | |
|     m_zero_filled_buffer = (u8*)malloc(4096);
 | |
|     bzero(m_zero_filled_buffer, 4096);
 | |
|     m_sound_thread.start();
 | |
| }
 | |
| 
 | |
| ASMixer::~ASMixer()
 | |
| {
 | |
| }
 | |
| 
 | |
| NonnullRefPtr<ASBufferQueue> ASMixer::create_queue(ASClientConnection& client)
 | |
| {
 | |
|     auto queue = adopt(*new ASBufferQueue(client));
 | |
|     pthread_mutex_lock(&m_pending_mutex);
 | |
|     m_pending_mixing.append(*queue);
 | |
|     pthread_cond_signal(&m_pending_cond);
 | |
|     pthread_mutex_unlock(&m_pending_mutex);
 | |
|     return queue;
 | |
| }
 | |
| 
 | |
| void ASMixer::mix()
 | |
| {
 | |
|     decltype(m_pending_mixing) active_mix_queues;
 | |
| 
 | |
|     for (;;) {
 | |
|         if (active_mix_queues.is_empty()) {
 | |
|             pthread_mutex_lock(&m_pending_mutex);
 | |
|             pthread_cond_wait(&m_pending_cond, &m_pending_mutex);
 | |
|             active_mix_queues.append(move(m_pending_mixing));
 | |
|             pthread_mutex_unlock(&m_pending_mutex);
 | |
|         }
 | |
| 
 | |
|         active_mix_queues.remove_all_matching([&](auto& entry) { return !entry->client(); });
 | |
| 
 | |
|         ASample mixed_buffer[1024];
 | |
|         auto mixed_buffer_length = (int)(sizeof(mixed_buffer) / sizeof(ASample));
 | |
| 
 | |
|         // Mix the buffers together into the output
 | |
|         for (auto& queue : active_mix_queues) {
 | |
|             if (!queue->client()) {
 | |
|                 queue->clear();
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             for (int i = 0; i < mixed_buffer_length; ++i) {
 | |
|                 auto& mixed_sample = mixed_buffer[i];
 | |
|                 ASample sample;
 | |
|                 if (!queue->get_next_sample(sample))
 | |
|                     break;
 | |
|                 mixed_sample += sample;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         bool muted = m_muted;
 | |
| 
 | |
|         // output the mixed stuff to the device
 | |
|         u8 raw_buffer[4096];
 | |
|         auto buffer = ByteBuffer::wrap(muted ? m_zero_filled_buffer : raw_buffer, sizeof(raw_buffer));
 | |
| 
 | |
|         BufferStream stream(buffer);
 | |
|         if (!muted) {
 | |
|             for (int i = 0; i < mixed_buffer_length; ++i) {
 | |
|                 auto& mixed_sample = mixed_buffer[i];
 | |
| 
 | |
|                 mixed_sample.scale(m_main_volume);
 | |
|                 mixed_sample.clip();
 | |
| 
 | |
|                 i16 out_sample;
 | |
|                 out_sample = mixed_sample.left * std::numeric_limits<i16>::max();
 | |
|                 stream << out_sample;
 | |
| 
 | |
|                 ASSERT(!stream.at_end()); // we should have enough space for both channels in one buffer!
 | |
|                 out_sample = mixed_sample.right * std::numeric_limits<i16>::max();
 | |
|                 stream << out_sample;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (stream.offset() != 0) {
 | |
|             buffer.trim(stream.offset());
 | |
|         }
 | |
|         m_device->write(buffer);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void ASMixer::set_muted(bool muted)
 | |
| {
 | |
|     if (m_muted == muted)
 | |
|         return;
 | |
|     m_muted = muted;
 | |
|     ASClientConnection::for_each([muted](ASClientConnection& client) {
 | |
|         client.did_change_muted_state({}, muted);
 | |
|     });
 | |
| }
 | |
| 
 | |
| ASBufferQueue::ASBufferQueue(ASClientConnection& client)
 | |
|     : m_client(client.make_weak_ptr())
 | |
| {
 | |
| }
 | |
| 
 | |
| void ASBufferQueue::enqueue(NonnullRefPtr<ABuffer>&& buffer)
 | |
| {
 | |
|     m_remaining_samples += buffer->sample_count();
 | |
|     m_queue.enqueue(move(buffer));
 | |
| }
 |