1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 03:17:35 +00:00

AudioServer: Assorted infrastructure work

* Add a LibAudio, and move WAV file parsing there (via AWavFile and AWavLoader)
* Add CLocalSocket, and CSocket::connect() variant for local address types.
  We make some small use of this in WindowServer (as that's where we
  modelled it from), but don't get too invasive as this PR is already
  quite large, and the WS I/O is a bit carefully done
* Add an AClientConnection which will eventually be used to talk to
  AudioServer (and make use of it in Piano, though right now it really
  doesn't do anything except connect, using our new CLocalSocket...)
This commit is contained in:
Robin Burchell 2019-07-13 19:42:03 +02:00 committed by Andreas Kling
parent 983245113a
commit ffa8cb668f
22 changed files with 431 additions and 162 deletions

View file

@ -1,160 +1,45 @@
#include <LibCore/CFile.h>
#include <LibCore/CEventLoop.h>
#include <LibCore/CLocalSocket.h>
#include <LibCore/CNotifier.h>
#include <LibAudio/AWavLoader.h>
#include <LibAudio/AWavFile.h>
u32 read_u32(const ByteBuffer& buf, u32& off)
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
class ASEventLoop
{
ASSERT(buf.size() - off >= 4);
u32 b0 = buf[off + 0];
u32 b1 = buf[off + 1];
u32 b2 = buf[off + 2];
u32 b3 = buf[off + 3];
public:
ASEventLoop();
int exec() { return m_event_loop.exec(); }
private:
CEventLoop m_event_loop;
CLocalSocket m_server_sock;
OwnPtr<CNotifier> m_server_notifier;
u32 ret = 0;
ret |= (u8(b3) << 24);
ret |= (u8(b2) << 16);
ret |= (u8(b1) << 8);
ret |= (u8(b0));
off += 4;
return ret;
}
u16 read_u16(const ByteBuffer& buf, u32& off)
{
ASSERT(buf.size() - off >= 2);
u16 b0 = buf[off + 0];
u16 b1 = buf[off + 1];
u16 ret = 0;
ret |= (u8(b1) << 8);
ret |= (u8(b0));
off += 2;
return ret;
}
ByteBuffer read_wav_data(const StringView& path)
{
CFile wav(path);
if (!wav.open(CIODevice::ReadOnly)) {
dbgprintf("Can't open wav to dump it to audio: %s", wav.error_string());
return {};
}
const auto& contents = wav.read_all();
u32 off = 0;
if (contents.size() - off < 12) {
dbgprintf("WAV is too small (no header, %d bytes)\n", contents.size());
return {};
}
dbgprintf("Trying to parse %d bytes of wav\n", contents.size());
#define CHECK_OK(msg) \
do { \
ASSERT(ok); \
if (!ok) { \
dbgprintf("%s failed\n", msg); \
return {}; \
} else { \
dbgprintf("%S is OK!\n", msg); \
} \
} while (0);
bool ok = true;
u32 riff = read_u32(contents, off);
ok = ok && riff == 0x46464952; // "RIFF"
CHECK_OK("RIFF header");
u32 sz = read_u32(contents, off);
ASSERT(sz < 1024 * 1024 * 42);
ok = ok && sz < 1024 * 1024 * 42; // arbitrary
CHECK_OK("File size");
u32 wave = read_u32(contents, off);
ok = ok && wave == 0x45564157; // "WAVE"
CHECK_OK("WAVE header");
if (contents.size() - off < 8) {
dbgprintf("WAV is too small (no fmt, %d bytes)\n", contents.size());
return {};
}
u32 fmt_id = read_u32(contents, off);
ok = ok && fmt_id == 0x20746D66; // "FMT"
CHECK_OK("FMT header");
u32 fmt_size = read_u32(contents, off);
ok = ok && fmt_size == 16;
ASSERT(fmt_size == 16);
CHECK_OK("FMT size");
if (contents.size() - off < 16) {
dbgprintf("WAV is too small (fmt chunk, %d bytes)\n", contents.size());
return {};
}
u16 audio_format = read_u16(contents, off);
ok = ok && audio_format == 1; // WAVE_FORMAT_PCM
ASSERT(audio_format == 1);
CHECK_OK("Audio format");
u16 num_channels = read_u16(contents, off);
ok = ok && num_channels == 1;
ASSERT(num_channels == 1);
CHECK_OK("Channel count");
u32 sample_rate = read_u32(contents, off);
CHECK_OK("Sample rate");
off += 4; // bytes per sec: we don't care.
off += 2; // block align: we don't care.
u16 bits_per_sample = read_u16(contents, off);
ok = ok && (bits_per_sample == 8 || bits_per_sample == 16);
ASSERT(bits_per_sample == 8 || bits_per_sample == 16);
CHECK_OK("Bits per sample");
dbgprintf("Read WAV of format %d with num_channels %d sample rate %d, bits per sample %d\n", audio_format, num_channels, sample_rate, bits_per_sample);
// Read chunks until we find DATA
if (off >= u32(contents.size()) - 8) {
ok = ok && false;
ASSERT_NOT_REACHED();
CHECK_OK("Premature EOF without DATA");
}
bool found_data = false;
u32 data_sz = 0;
while (off < u32(contents.size()) - 8) {
u32 chunk_id = read_u32(contents, off);
data_sz = read_u32(contents, off);
if (chunk_id == 0x61746164) { // DATA
found_data = true;
break;
}
off += data_sz;
}
ok = ok && found_data;
ASSERT(found_data);
CHECK_OK("Found no data chunk");
ok = ok && data_sz <= (contents.size() - off);
CHECK_OK("Bad DATA size");
return contents.slice(off, data_sz);
}
void drain_server();
};
void read_and_play_wav()
{
CFile audio("/dev/audio");
if (!audio.open(CIODevice::WriteOnly)) {
dbgprintf("Can't open audio device: %s", audio.error_string());
dbgprintf("Can't open audio device: %s\n", audio.error_string());
return;
}
const auto& contents = read_wav_data("/home/anon/tmp.wav");
AWavLoader loader;
const auto& file = loader.load_wav("/home/anon/tmp.wav");
if (!file) {
dbgprintf("Can't parse WAV: %s\n", loader.error_string());
return;
}
dbgprintf("Read WAV of format %d with num_channels %d sample rate %d, bits per sample %d\n", (u8)file->format(), file->channel_count(), file->sample_rate_per_second(), file->bits_per_sample());
auto contents = file->sample_data();
const int chunk_size = 4096;
int i = 0;
while (i < contents.size()) {
@ -164,8 +49,41 @@ void read_and_play_wav()
}
}
int main(int, char**)
ASEventLoop::ASEventLoop()
{
read_and_play_wav();
return 0;
unlink("/tmp/asportal");
sockaddr_un address;
address.sun_family = AF_LOCAL;
strcpy(address.sun_path, "/tmp/asportal");
int rc = bind(m_server_sock.fd(), (const sockaddr*)&address, sizeof(address));
ASSERT(rc == 0);
rc = listen(m_server_sock.fd(), 5);
ASSERT(rc == 0);
m_server_notifier = make<CNotifier>(m_server_sock.fd(), CNotifier::Read);
m_server_notifier->on_ready_to_read = [this] { drain_server(); };
}
void ASEventLoop::drain_server()
{
sockaddr_un address;
socklen_t address_size = sizeof(address);
int client_fd = accept(m_server_sock.fd(), (sockaddr*)&address, &address_size);
if (client_fd < 0) {
dbgprintf("WindowServer: accept() failed: %s\n", strerror(errno));
} else {
dbgprintf("AudioServer: accept()ed client %d\n", client_fd);
String s("hello, client!\n");
write(client_fd, s.characters(), s.length());
close(client_fd);
}
}
int main(int, char**)
{
ASEventLoop event_loop;
return event_loop.exec();
}