mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 22:07:35 +00:00
Services: Renamed from Servers
It didn't feel right to have a "DHCPClient" in a "Servers" directory. Rename this to Services to better reflect the type of programs we'll be putting in there.
This commit is contained in:
parent
042b1f6814
commit
cf3b58fbe8
119 changed files with 31 additions and 31 deletions
161
Services/AudioServer/ASClientConnection.cpp
Normal file
161
Services/AudioServer/ASClientConnection.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "ASClientConnection.h"
|
||||
#include "ASMixer.h"
|
||||
#include "AudioClientEndpoint.h"
|
||||
#include <AK/SharedBuffer.h>
|
||||
#include <LibAudio/Buffer.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static HashMap<int, RefPtr<ASClientConnection>> s_connections;
|
||||
|
||||
void ASClientConnection::for_each(Function<void(ASClientConnection&)> callback)
|
||||
{
|
||||
NonnullRefPtrVector<ASClientConnection> connections;
|
||||
for (auto& it : s_connections)
|
||||
connections.append(*it.value);
|
||||
for (auto& connection : connections)
|
||||
callback(connection);
|
||||
}
|
||||
|
||||
ASClientConnection::ASClientConnection(Core::LocalSocket& client_socket, int client_id, ASMixer& mixer)
|
||||
: IPC::ClientConnection<AudioServerEndpoint>(*this, client_socket, client_id)
|
||||
, m_mixer(mixer)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
ASClientConnection::~ASClientConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void ASClientConnection::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
}
|
||||
|
||||
void ASClientConnection::did_finish_playing_buffer(Badge<ASBufferQueue>, int buffer_id)
|
||||
{
|
||||
post_message(Messages::AudioClient::FinishedPlayingBuffer(buffer_id));
|
||||
}
|
||||
|
||||
void ASClientConnection::did_change_muted_state(Badge<ASMixer>, bool muted)
|
||||
{
|
||||
post_message(Messages::AudioClient::MutedStateChanged(muted));
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GreetResponse> ASClientConnection::handle(const Messages::AudioServer::Greet&)
|
||||
{
|
||||
return make<Messages::AudioServer::GreetResponse>(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetMainMixVolumeResponse> ASClientConnection::handle(const Messages::AudioServer::GetMainMixVolume&)
|
||||
{
|
||||
return make<Messages::AudioServer::GetMainMixVolumeResponse>(m_mixer.main_volume());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::SetMainMixVolumeResponse> ASClientConnection::handle(const Messages::AudioServer::SetMainMixVolume& message)
|
||||
{
|
||||
m_mixer.set_main_volume(message.volume());
|
||||
return make<Messages::AudioServer::SetMainMixVolumeResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::EnqueueBufferResponse> ASClientConnection::handle(const Messages::AudioServer::EnqueueBuffer& message)
|
||||
{
|
||||
auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.buffer_id());
|
||||
if (!shared_buffer) {
|
||||
// FIXME: The shared buffer should have been retrieved for us already.
|
||||
// We don't want to do IPC error checking at this layer.
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (!m_queue)
|
||||
m_queue = m_mixer.create_queue(*this);
|
||||
|
||||
if (m_queue->is_full())
|
||||
return make<Messages::AudioServer::EnqueueBufferResponse>(false);
|
||||
|
||||
m_queue->enqueue(Audio::Buffer::create_with_shared_buffer(*shared_buffer, message.sample_count()));
|
||||
return make<Messages::AudioServer::EnqueueBufferResponse>(true);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetRemainingSamplesResponse> ASClientConnection::handle(const Messages::AudioServer::GetRemainingSamples&)
|
||||
{
|
||||
int remaining = 0;
|
||||
if (m_queue)
|
||||
remaining = m_queue->get_remaining_samples();
|
||||
|
||||
return make<Messages::AudioServer::GetRemainingSamplesResponse>(remaining);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetPlayedSamplesResponse> ASClientConnection::handle(const Messages::AudioServer::GetPlayedSamples&)
|
||||
{
|
||||
int played = 0;
|
||||
if (m_queue)
|
||||
played = m_queue->get_played_samples();
|
||||
|
||||
return make<Messages::AudioServer::GetPlayedSamplesResponse>(played);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::SetPausedResponse> ASClientConnection::handle(const Messages::AudioServer::SetPaused& message)
|
||||
{
|
||||
if (m_queue)
|
||||
m_queue->set_paused(message.paused());
|
||||
return make<Messages::AudioServer::SetPausedResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::ClearBufferResponse> ASClientConnection::handle(const Messages::AudioServer::ClearBuffer& message)
|
||||
{
|
||||
if (m_queue)
|
||||
m_queue->clear(message.paused());
|
||||
return make<Messages::AudioServer::ClearBufferResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetPlayingBufferResponse> ASClientConnection::handle(const Messages::AudioServer::GetPlayingBuffer&)
|
||||
{
|
||||
int id = -1;
|
||||
if (m_queue)
|
||||
id = m_queue->get_playing_buffer();
|
||||
return make<Messages::AudioServer::GetPlayingBufferResponse>(id);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetMutedResponse> ASClientConnection::handle(const Messages::AudioServer::GetMuted&)
|
||||
{
|
||||
return make<Messages::AudioServer::GetMutedResponse>(m_mixer.is_muted());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::SetMutedResponse> ASClientConnection::handle(const Messages::AudioServer::SetMuted& message)
|
||||
{
|
||||
m_mixer.set_muted(message.muted());
|
||||
return make<Messages::AudioServer::SetMutedResponse>();
|
||||
}
|
69
Services/AudioServer/ASClientConnection.h
Normal file
69
Services/AudioServer/ASClientConnection.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AudioServer/AudioServerEndpoint.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
|
||||
namespace Audio {
|
||||
class Buffer;
|
||||
}
|
||||
|
||||
class ASBufferQueue;
|
||||
class ASMixer;
|
||||
|
||||
class ASClientConnection final : public IPC::ClientConnection<AudioServerEndpoint>
|
||||
, public AudioServerEndpoint {
|
||||
C_OBJECT(ASClientConnection)
|
||||
public:
|
||||
explicit ASClientConnection(Core::LocalSocket&, int client_id, ASMixer& mixer);
|
||||
~ASClientConnection() override;
|
||||
|
||||
void did_finish_playing_buffer(Badge<ASBufferQueue>, int buffer_id);
|
||||
void did_change_muted_state(Badge<ASMixer>, bool muted);
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
static void for_each(Function<void(ASClientConnection&)>);
|
||||
|
||||
private:
|
||||
virtual OwnPtr<Messages::AudioServer::GreetResponse> handle(const Messages::AudioServer::Greet&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetMainMixVolumeResponse> handle(const Messages::AudioServer::GetMainMixVolume&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::SetMainMixVolumeResponse> handle(const Messages::AudioServer::SetMainMixVolume&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::EnqueueBufferResponse> handle(const Messages::AudioServer::EnqueueBuffer&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetRemainingSamplesResponse> handle(const Messages::AudioServer::GetRemainingSamples&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetPlayedSamplesResponse> handle(const Messages::AudioServer::GetPlayedSamples&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::SetPausedResponse> handle(const Messages::AudioServer::SetPaused&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::ClearBufferResponse> handle(const Messages::AudioServer::ClearBuffer&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetPlayingBufferResponse> handle(const Messages::AudioServer::GetPlayingBuffer&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetMutedResponse> handle(const Messages::AudioServer::GetMuted&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::SetMutedResponse> handle(const Messages::AudioServer::SetMuted&) override;
|
||||
|
||||
ASMixer& m_mixer;
|
||||
RefPtr<ASBufferQueue> m_queue;
|
||||
};
|
152
Services/AudioServer/ASMixer.cpp
Normal file
152
Services/AudioServer/ASMixer.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/BufferStream.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AudioServer/ASClientConnection.h>
|
||||
#include <AudioServer/ASMixer.h>
|
||||
#include <pthread.h>
|
||||
|
||||
ASMixer::ASMixer()
|
||||
: m_device(Core::File::construct("/dev/audio", this))
|
||||
, m_sound_thread(
|
||||
[this] {
|
||||
mix();
|
||||
return 0;
|
||||
},
|
||||
"AudioServer[mixer]")
|
||||
{
|
||||
if (!m_device->open(Core::IODevice::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(); });
|
||||
|
||||
Audio::Sample mixed_buffer[1024];
|
||||
auto mixed_buffer_length = (int)(sizeof(mixed_buffer) / sizeof(Audio::Sample));
|
||||
|
||||
// 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];
|
||||
Audio::Sample 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 * NumericLimits<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 * NumericLimits<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<Audio::Buffer>&& buffer)
|
||||
{
|
||||
m_remaining_samples += buffer->sample_count();
|
||||
m_queue.enqueue(move(buffer));
|
||||
}
|
139
Services/AudioServer/ASMixer.h
Normal file
139
Services/AudioServer/ASMixer.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ASClientConnection.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 <LibThread/Lock.h>
|
||||
#include <LibThread/Thread.h>
|
||||
|
||||
class ASClientConnection;
|
||||
|
||||
class ASBufferQueue : public RefCounted<ASBufferQueue> {
|
||||
public:
|
||||
explicit ASBufferQueue(ASClientConnection&);
|
||||
~ASBufferQueue() {}
|
||||
|
||||
bool is_full() const { return m_queue.size() >= 3; }
|
||||
void enqueue(NonnullRefPtr<Audio::Buffer>&&);
|
||||
|
||||
bool get_next_sample(Audio::Sample& 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->shbuf_id());
|
||||
m_current = nullptr;
|
||||
m_position = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ASClientConnection* 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->shbuf_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<ASClientConnection> m_client;
|
||||
};
|
||||
|
||||
class ASMixer : public Core::Object {
|
||||
C_OBJECT(ASMixer)
|
||||
public:
|
||||
ASMixer();
|
||||
virtual ~ASMixer() override;
|
||||
|
||||
NonnullRefPtr<ASBufferQueue> create_queue(ASClientConnection&);
|
||||
|
||||
int main_volume() const { return m_main_volume; }
|
||||
void set_main_volume(int volume) { m_main_volume = volume; }
|
||||
|
||||
bool is_muted() const { return m_muted; }
|
||||
void set_muted(bool);
|
||||
|
||||
private:
|
||||
Vector<NonnullRefPtr<ASBufferQueue>> m_pending_mixing;
|
||||
pthread_mutex_t m_pending_mutex;
|
||||
pthread_cond_t m_pending_cond;
|
||||
|
||||
RefPtr<Core::File> m_device;
|
||||
|
||||
LibThread::Thread m_sound_thread;
|
||||
|
||||
bool m_muted { false };
|
||||
int m_main_volume { 100 };
|
||||
|
||||
u8* m_zero_filled_buffer { nullptr };
|
||||
|
||||
void mix();
|
||||
};
|
5
Services/AudioServer/AudioClient.ipc
Normal file
5
Services/AudioServer/AudioClient.ipc
Normal file
|
@ -0,0 +1,5 @@
|
|||
endpoint AudioClient = 82
|
||||
{
|
||||
FinishedPlayingBuffer(i32 buffer_id) =|
|
||||
MutedStateChanged(bool muted) =|
|
||||
}
|
21
Services/AudioServer/AudioServer.ipc
Normal file
21
Services/AudioServer/AudioServer.ipc
Normal file
|
@ -0,0 +1,21 @@
|
|||
endpoint AudioServer = 85
|
||||
{
|
||||
// Basic protocol
|
||||
Greet() => (i32 client_id)
|
||||
|
||||
// Mixer functions
|
||||
SetMuted(bool muted) => ()
|
||||
GetMuted() => (bool muted)
|
||||
GetMainMixVolume() => (i32 volume)
|
||||
SetMainMixVolume(i32 volume) => ()
|
||||
|
||||
// Buffer playback
|
||||
EnqueueBuffer(i32 buffer_id, int sample_count) => (bool success)
|
||||
SetPaused(bool paused) => ()
|
||||
ClearBuffer(bool paused) => ()
|
||||
|
||||
//Buffer information
|
||||
GetRemainingSamples() => (int remaining_samples)
|
||||
GetPlayedSamples() => (int played_samples)
|
||||
GetPlayingBuffer() => (i32 buffer_id)
|
||||
}
|
24
Services/AudioServer/Makefile
Normal file
24
Services/AudioServer/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
OBJS = \
|
||||
main.o \
|
||||
ASMixer.o \
|
||||
ASClientConnection.o
|
||||
|
||||
PROGRAM = AudioServer
|
||||
|
||||
LIB_DEPS = Core IPC Thread Pthread
|
||||
|
||||
EXTRA_CLEAN = AudioServerEndpoint.h AudioClientEndpoint.h
|
||||
|
||||
*.cpp: AudioServerEndpoint.h AudioClientEndpoint.h
|
||||
|
||||
AudioServerEndpoint.h: AudioServer.ipc | IPCCOMPILER
|
||||
@echo "IPC $<"; $(IPCCOMPILER) $< > $@
|
||||
|
||||
AudioClientEndpoint.h: AudioClient.ipc | IPCCOMPILER
|
||||
@echo "IPC $<"; $(IPCCOMPILER) $< > $@
|
||||
|
||||
install:
|
||||
mkdir -p $(SERENITY_BASE_DIR)/Root/usr/include/AudioServer/
|
||||
cp *.h $(SERENITY_BASE_DIR)/Root/usr/include/AudioServer/
|
||||
|
||||
include ../../Makefile.common
|
61
Services/AudioServer/main.cpp
Normal file
61
Services/AudioServer/main.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "ASMixer.h"
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
if (pledge("stdio thread shared_buffer accept rpath wpath cpath unix fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
ASMixer mixer;
|
||||
|
||||
auto server = Core::LocalServer::construct();
|
||||
bool ok = server->take_over_from_system_server();
|
||||
ASSERT(ok);
|
||||
server->on_ready_to_accept = [&] {
|
||||
auto client_socket = server->accept();
|
||||
if (!client_socket) {
|
||||
dbg() << "AudioServer: accept failed.";
|
||||
return;
|
||||
}
|
||||
static int s_next_client_id = 0;
|
||||
int client_id = ++s_next_client_id;
|
||||
IPC::new_client_connection<ASClientConnection>(*client_socket, client_id, mixer);
|
||||
};
|
||||
|
||||
if (pledge("stdio thread shared_buffer accept", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue