1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-06 14:27:35 +00:00
serenity/Userland/Services/AudioServer/Mixer.h
Gunnar Beutner f589acaac9 AudioServer: Put the m_zero_filled_buffer variable into the .bss segment
This way we don't have to allocate this at runtime. I'm intentionally
not using static constexpr here because that would put the variable
into the .rodata segment and would therefore increase the binary by
4kB.

The old code also failed to free() the buffer in the destructor, however
that wasn't much of an issue because the Mixer object exists throughout
the program's entire lifetime.
2021-06-16 20:07:37 +02:00

124 lines
2.9 KiB
C++

/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "ClientConnection.h"
#include <AK/Atomic.h>
#include <AK/Badge.h>
#include <AK/ByteBuffer.h>
#include <AK/NonnullRefPtrVector.h>
#include <AK/Queue.h>
#include <AK/RefCounted.h>
#include <AK/WeakPtr.h>
#include <LibAudio/Buffer.h>
#include <LibCore/File.h>
#include <LibThreading/Lock.h>
#include <LibThreading/Thread.h>
namespace AudioServer {
class ClientConnection;
class BufferQueue : public RefCounted<BufferQueue> {
public:
explicit BufferQueue(ClientConnection&);
~BufferQueue() { }
bool is_full() const { return m_queue.size() >= 3; }
void enqueue(NonnullRefPtr<Audio::Buffer>&&);
bool get_next_sample(Audio::Frame& sample)
{
if (m_paused)
return false;
while (!m_current && !m_queue.is_empty())
m_current = m_queue.dequeue();
if (!m_current)
return false;
sample = m_current->samples()[m_position++];
--m_remaining_samples;
++m_played_samples;
if (m_position >= m_current->sample_count()) {
m_client->did_finish_playing_buffer({}, m_current->id());
m_current = nullptr;
m_position = 0;
}
return true;
}
ClientConnection* client() { return m_client.ptr(); }
void clear(bool paused = false)
{
m_queue.clear();
m_position = 0;
m_remaining_samples = 0;
m_played_samples = 0;
m_current = nullptr;
m_paused = paused;
}
void set_paused(bool paused)
{
m_paused = paused;
}
int get_remaining_samples() const { return m_remaining_samples; }
int get_played_samples() const { return m_played_samples; }
int get_playing_buffer() const
{
if (m_current)
return m_current->id();
return -1;
}
private:
RefPtr<Audio::Buffer> m_current;
Queue<NonnullRefPtr<Audio::Buffer>> m_queue;
int m_position { 0 };
int m_remaining_samples { 0 };
int m_played_samples { 0 };
bool m_paused { false };
WeakPtr<ClientConnection> m_client;
};
class Mixer : public Core::Object {
C_OBJECT(Mixer)
public:
Mixer();
virtual ~Mixer() override;
NonnullRefPtr<BufferQueue> create_queue(ClientConnection&);
int main_volume() const { return m_main_volume; }
void set_main_volume(int volume);
bool is_muted() const { return m_muted; }
void set_muted(bool);
private:
Vector<NonnullRefPtr<BufferQueue>> m_pending_mixing;
Atomic<bool> m_added_queue { false };
pthread_mutex_t m_pending_mutex;
pthread_cond_t m_pending_cond;
RefPtr<Core::File> m_device;
NonnullRefPtr<Threading::Thread> m_sound_thread;
bool m_muted { false };
int m_main_volume { 100 };
static u8 m_zero_filled_buffer[4096];
void mix();
};
}