From ffa8cb668faffd4c417cc7ff60215d732a088122 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Sat, 13 Jul 2019 19:42:03 +0200 Subject: [PATCH] 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...) --- Applications/Piano/Makefile | 2 +- Applications/Piano/main.cpp | 2 + Kernel/makeall.sh | 1 + Libraries/LibAudio/AClientConnection.cpp | 26 +++ Libraries/LibAudio/AClientConnection.h | 13 ++ Libraries/LibAudio/AWavFile.cpp | 2 + Libraries/LibAudio/AWavFile.h | 32 ++++ Libraries/LibAudio/AWavLoader.cpp | 166 ++++++++++++++++++ Libraries/LibAudio/AWavLoader.h | 14 ++ Libraries/LibAudio/Makefile | 32 ++++ Libraries/LibCore/CEventLoop.h | 2 +- Libraries/LibCore/CLocalSocket.cpp | 19 ++ Libraries/LibCore/CLocalSocket.h | 7 + Libraries/LibCore/CSocket.cpp | 23 ++- Libraries/LibCore/CSocket.h | 4 +- Libraries/LibCore/CSocketAddress.h | 11 ++ Libraries/LibCore/Makefile | 1 + Makefile.common | 2 +- Servers/AudioServer/Makefile | 2 +- Servers/AudioServer/main.cpp | 212 +++++++---------------- Servers/WindowServer/WSEventLoop.cpp | 15 +- Servers/WindowServer/WSEventLoop.h | 5 +- 22 files changed, 431 insertions(+), 162 deletions(-) create mode 100644 Libraries/LibAudio/AClientConnection.cpp create mode 100644 Libraries/LibAudio/AClientConnection.h create mode 100644 Libraries/LibAudio/AWavFile.cpp create mode 100644 Libraries/LibAudio/AWavFile.h create mode 100644 Libraries/LibAudio/AWavLoader.cpp create mode 100644 Libraries/LibAudio/AWavLoader.h create mode 100644 Libraries/LibAudio/Makefile create mode 100644 Libraries/LibCore/CLocalSocket.cpp create mode 100644 Libraries/LibCore/CLocalSocket.h diff --git a/Applications/Piano/Makefile b/Applications/Piano/Makefile index ed2c50af3f..1e362554df 100644 --- a/Applications/Piano/Makefile +++ b/Applications/Piano/Makefile @@ -11,7 +11,7 @@ DEFINES += -DUSERLAND all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -lcore -lc + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -lcore -lc -laudio .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Applications/Piano/main.cpp b/Applications/Piano/main.cpp index b6e927f565..d453f3b5ad 100644 --- a/Applications/Piano/main.cpp +++ b/Applications/Piano/main.cpp @@ -2,6 +2,7 @@ #include "PianoWidget.h" #include #include +#include #include #include #include @@ -10,6 +11,7 @@ static int s_pipefds[2]; int main(int argc, char** argv) { + AClientConnection audio_connection; GApplication app(argc, argv); pipe(s_pipefds); diff --git a/Kernel/makeall.sh b/Kernel/makeall.sh index 5dc946ca1a..a6b33c8b50 100755 --- a/Kernel/makeall.sh +++ b/Kernel/makeall.sh @@ -24,6 +24,7 @@ build_targets="$build_targets ../Servers/WindowServer" build_targets="$build_targets ../Servers/AudioServer" build_targets="$build_targets ../Libraries/LibGUI" build_targets="$build_targets ../Libraries/LibHTML" +build_targets="$build_targets ../Libraries/LibAudio" build_targets="$build_targets ../Userland" build_targets="$build_targets ../Applications/Terminal" build_targets="$build_targets ../Applications/FontEditor" diff --git a/Libraries/LibAudio/AClientConnection.cpp b/Libraries/LibAudio/AClientConnection.cpp new file mode 100644 index 0000000000..002cc22fca --- /dev/null +++ b/Libraries/LibAudio/AClientConnection.cpp @@ -0,0 +1,26 @@ +#include "AClientConnection.h" +#include +#include + +AClientConnection::AClientConnection() +{ + m_connection.on_connected = [this] { + m_notifier = make(m_connection.fd(), CNotifier::Read); + m_notifier->on_ready_to_read = [this] { printf("AudioServer said something to us"); }; + m_connection.write("Hello, friends"); + }; + + int retries = 1000; + while (retries) { + if (m_connection.connect(CSocketAddress::local("/tmp/asportal"))) { + break; + } + +#ifdef ACLIENT_DEBUG + dbgprintf("AClientConnection: connect failed: %d, %s\n", errno, strerror(errno)); +#endif + sleep(1); + --retries; + } +} + diff --git a/Libraries/LibAudio/AClientConnection.h b/Libraries/LibAudio/AClientConnection.h new file mode 100644 index 0000000000..7df832babc --- /dev/null +++ b/Libraries/LibAudio/AClientConnection.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class AClientConnection { +public: + AClientConnection(); + +private: + CLocalSocket m_connection; + OwnPtr m_notifier; +}; diff --git a/Libraries/LibAudio/AWavFile.cpp b/Libraries/LibAudio/AWavFile.cpp new file mode 100644 index 0000000000..ef9c77fa77 --- /dev/null +++ b/Libraries/LibAudio/AWavFile.cpp @@ -0,0 +1,2 @@ +#include "AWavFile.h" + diff --git a/Libraries/LibAudio/AWavFile.h b/Libraries/LibAudio/AWavFile.h new file mode 100644 index 0000000000..6321179363 --- /dev/null +++ b/Libraries/LibAudio/AWavFile.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +class AWavFile : public RefCounted { +public: + enum class Format { + Invalid, + PCM, + }; + + Format format() const { return m_format; } + u16 channel_count() const { return m_channel_count; } + u32 sample_rate_per_second() const { return m_sample_rate; } + u32 average_byte_rate_per_second() const { return m_byte_rate; } + u16 block_align() const { return m_block_align; } + u16 bits_per_sample() const { return m_bits_per_sample; } + const ByteBuffer& sample_data() const { return m_sample_data; } + +private: + Format m_format = Format::Invalid; + u16 m_channel_count = 0; + u32 m_sample_rate = 0; + u32 m_byte_rate = 0; + u16 m_block_align = 0; + u16 m_bits_per_sample = 0; + ByteBuffer m_sample_data; + + friend class AWavLoader; +}; diff --git a/Libraries/LibAudio/AWavLoader.cpp b/Libraries/LibAudio/AWavLoader.cpp new file mode 100644 index 0000000000..16a0a73607 --- /dev/null +++ b/Libraries/LibAudio/AWavLoader.cpp @@ -0,0 +1,166 @@ +#include + +#include "AWavLoader.h" +#include "AWavFile.h" + +static u32 read_u32(const ByteBuffer& buf, u32& off) +{ + 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]; + + u32 ret = 0; + ret |= (u8(b3) << 24); + ret |= (u8(b2) << 16); + ret |= (u8(b1) << 8); + ret |= (u8(b0)); + + off += 4; + return ret; +} + +static 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; +} + +RefPtr AWavLoader::load_wav(const StringView& path) +{ + m_error_string = {}; + + CFile wav(path); + if (!wav.open(CIODevice::ReadOnly)) { + m_error_string = String::format("Can't open file: %s", wav.error_string()); + return nullptr; + } + + const auto& contents = wav.read_all(); + return parse_wav(contents); +} + +// TODO: A streaming parser might be better than forcing a ByteBuffer +RefPtr AWavLoader::parse_wav(const ByteBuffer& buffer) +{ + u32 off = 0; + + if (buffer.size() - off < 12) { + dbgprintf("WAV is too small (no header, %d bytes)\n", buffer.size()); + return {}; + } + + dbgprintf("Trying to parse %d bytes of wav\n", buffer.size()); + +#define CHECK_OK(msg) \ + do { \ + ASSERT(ok); \ + if (!ok) { \ + m_error_string = String::format("Parsing failed: %s", msg); \ + return {}; \ + } else { \ + dbgprintf("%s is OK!\n", msg); \ + } \ + } while (0); + + bool ok = true; + u32 riff = read_u32(buffer, off); + ok = ok && riff == 0x46464952; // "RIFF" + CHECK_OK("RIFF header"); + + u32 sz = read_u32(buffer, off); + ASSERT(sz < 1024 * 1024 * 42); + ok = ok && sz < 1024 * 1024 * 42; // arbitrary + CHECK_OK("File size"); + + u32 wave = read_u32(buffer, off); + ok = ok && wave == 0x45564157; // "WAVE" + CHECK_OK("WAVE header"); + + if (buffer.size() - off < 8) { + dbgprintf("WAV is too small (no fmt, %d bytes)\n", buffer.size()); + return {}; + } + + u32 fmt_id = read_u32(buffer, off); + ok = ok && fmt_id == 0x20746D66; // "FMT" + CHECK_OK("FMT header"); + + u32 fmt_size = read_u32(buffer, off); + ok = ok && fmt_size == 16; + ASSERT(fmt_size == 16); + CHECK_OK("FMT size"); + + if (buffer.size() - off < 16) { + dbgprintf("WAV is too small (fmt chunk, %d bytes)\n", buffer.size()); + return {}; + } + + auto ret = adopt(*new AWavFile); + u16 audio_format = read_u16(buffer, off); + ok = ok && audio_format == 1; // WAVE_FORMAT_PCM + ASSERT(audio_format == 1); + CHECK_OK("Audio format"); + ret->m_format = AWavFile::Format::PCM; + + u16 num_channels = read_u16(buffer, off); + CHECK_OK("Channel count"); + ret->m_channel_count = num_channels; + + u32 sample_rate = read_u32(buffer, off); + CHECK_OK("Sample rate"); + ret->m_sample_rate = sample_rate; + + u32 byte_rate = read_u32(buffer, off); + CHECK_OK("Byte rate"); + ret->m_byte_rate = byte_rate; + + u16 block_align = read_u16(buffer, off); + CHECK_OK("Block align"); + ret->m_block_align = block_align; + + u16 bits_per_sample = read_u16(buffer, 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"); + ret->m_bits_per_sample = bits_per_sample; + + // Read chunks until we find DATA + if (off >= u32(buffer.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(buffer.size()) - 8) { + u32 chunk_id = read_u32(buffer, off); + data_sz = read_u32(buffer, 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 <= (buffer.size() - off); + CHECK_OK("Bad DATA size"); + + ret->m_sample_data = buffer.slice(off, data_sz); + return ret; +} + diff --git a/Libraries/LibAudio/AWavLoader.h b/Libraries/LibAudio/AWavLoader.h new file mode 100644 index 0000000000..be4facab45 --- /dev/null +++ b/Libraries/LibAudio/AWavLoader.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class AWavFile; + +class AWavLoader { +public: + RefPtr load_wav(const StringView& path); + const char* error_string() { return m_error_string.characters(); } +private: + RefPtr parse_wav(const ByteBuffer& buffer); + String m_error_string; +}; diff --git a/Libraries/LibAudio/Makefile b/Libraries/LibAudio/Makefile new file mode 100644 index 0000000000..fa2c6507aa --- /dev/null +++ b/Libraries/LibAudio/Makefile @@ -0,0 +1,32 @@ +include ../../Makefile.common + +OBJS = \ + AClientConnection.o \ + AWavFile.o \ + AWavLoader.o + +LIBRARY = libaudio.a +DEFINES += -DUSERLAND + +all: $(LIBRARY) + +$(LIBRARY): $(OBJS) + @echo "LIB $@"; $(AR) rcs $@ $(OBJS) $(LIBS) + +.cpp.o: + @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< + +-include $(OBJS:%.o=%.d) + +clean: + @echo "CLEAN"; rm -f $(LIBRARY) $(OBJS) *.d + +install: $(LIBRARY) + mkdir -p ../../Root/usr/include/LibAudio + mkdir -p ../../Root/usr/include/AK + mkdir -p ../../Root/usr/lib + # Copy headers + rsync -r -a --include '*/' --include '*.h' --exclude '*' . ../../Root/usr/include/LibAudio + rsync -r -a --include '*/' --include '*.h' --exclude '*' ../../AK/ ../../Root/usr/include/AK + # Install the library + cp $(LIBRARY) ../../Root/usr/lib diff --git a/Libraries/LibCore/CEventLoop.h b/Libraries/LibCore/CEventLoop.h index 8cab0bbe99..7e3d5cb9e7 100644 --- a/Libraries/LibCore/CEventLoop.h +++ b/Libraries/LibCore/CEventLoop.h @@ -6,11 +6,11 @@ #include #include #include +#include #include #include #include -class CEvent; class CObject; class CNotifier; diff --git a/Libraries/LibCore/CLocalSocket.cpp b/Libraries/LibCore/CLocalSocket.cpp new file mode 100644 index 0000000000..20837a066f --- /dev/null +++ b/Libraries/LibCore/CLocalSocket.cpp @@ -0,0 +1,19 @@ +#include +#include + +CLocalSocket::CLocalSocket(CObject* parent) + : CSocket(CSocket::Type::Local, parent) +{ + int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + set_error(fd); + } else { + set_fd(fd); + set_mode(CIODevice::ReadWrite); + set_error(0); + } +} + +CLocalSocket::~CLocalSocket() +{ +} diff --git a/Libraries/LibCore/CLocalSocket.h b/Libraries/LibCore/CLocalSocket.h new file mode 100644 index 0000000000..cdb4eeba55 --- /dev/null +++ b/Libraries/LibCore/CLocalSocket.h @@ -0,0 +1,7 @@ +#include + +class CLocalSocket final : public CSocket { +public: + explicit CLocalSocket(CObject* parent = nullptr); + virtual ~CLocalSocket() override; +}; diff --git a/Libraries/LibCore/CSocket.cpp b/Libraries/LibCore/CSocket.cpp index 4479969c40..cbe2bd9dd4 100644 --- a/Libraries/LibCore/CSocket.cpp +++ b/Libraries/LibCore/CSocket.cpp @@ -35,6 +35,8 @@ bool CSocket::connect(const CSocketAddress& address, int port) { ASSERT(!is_connected()); ASSERT(address.type() == CSocketAddress::Type::IPv4); + dbgprintf("Connecting to %s...", address.to_string().characters()); + ASSERT(port > 0 && port <= 65535); struct sockaddr_in addr; @@ -47,7 +49,6 @@ bool CSocket::connect(const CSocketAddress& address, int port) m_destination_address = address; m_destination_port = port; - dbgprintf("Connecting to %s...", address.to_string().characters()); fflush(stdout); int rc = ::connect(fd(), (struct sockaddr*)&addr, sizeof(addr)); if (rc < 0) { @@ -71,6 +72,26 @@ bool CSocket::connect(const CSocketAddress& address, int port) return true; } +bool CSocket::connect(const CSocketAddress& address) +{ + ASSERT(!is_connected()); + ASSERT(address.type() == CSocketAddress::Type::Local); + dbgprintf("Connecting to %s...", address.to_string().characters()); + + sockaddr_un saddr; + saddr.sun_family = AF_LOCAL; + strcpy(saddr.sun_path, address.to_string().characters()); + + int rc = ::connect(fd(), (const sockaddr*)&saddr, sizeof(saddr)); + if (rc < 0) { + perror("connect"); + return false; + } + + m_connected = true; + return true; +} + ByteBuffer CSocket::receive(int max_size) { auto buffer = read(max_size); diff --git a/Libraries/LibCore/CSocket.h b/Libraries/LibCore/CSocket.h index 587f80f290..8521bb02f9 100644 --- a/Libraries/LibCore/CSocket.h +++ b/Libraries/LibCore/CSocket.h @@ -10,12 +10,14 @@ public: enum class Type { Invalid, TCP, - UDP + UDP, + Local, }; virtual ~CSocket() override; bool connect(const String& hostname, int port); bool connect(const CSocketAddress&, int port); + bool connect(const CSocketAddress&); ByteBuffer receive(int max_size); bool send(const ByteBuffer&); diff --git a/Libraries/LibCore/CSocketAddress.h b/Libraries/LibCore/CSocketAddress.h index 019a0869fb..b0333516bb 100644 --- a/Libraries/LibCore/CSocketAddress.h +++ b/Libraries/LibCore/CSocketAddress.h @@ -17,6 +17,14 @@ public: { } + static CSocketAddress local(const String& address) + { + CSocketAddress addr; + addr.m_type = Type::Local; + addr.m_local_address = address; + return addr; + } + Type type() const { return m_type; } bool is_valid() const { return m_type != Type::Invalid; } IPv4Address ipv4_address() const { return m_ipv4_address; } @@ -26,6 +34,8 @@ public: switch (m_type) { case Type::IPv4: return m_ipv4_address.to_string(); + case Type::Local: + return m_local_address; default: return "[CSocketAddress]"; } @@ -34,4 +44,5 @@ public: private: Type m_type { Type::Invalid }; IPv4Address m_ipv4_address; + String m_local_address; }; diff --git a/Libraries/LibCore/Makefile b/Libraries/LibCore/Makefile index e7b25671e4..2e119ee71f 100644 --- a/Libraries/LibCore/Makefile +++ b/Libraries/LibCore/Makefile @@ -5,6 +5,7 @@ OBJS = \ CIODevice.o \ CFile.o \ CSocket.o \ + CLocalSocket.o \ CTCPSocket.o \ CElapsedTimer.o \ CNotifier.o \ diff --git a/Makefile.common b/Makefile.common index af82b9c042..cbe8e38365 100644 --- a/Makefile.common +++ b/Makefile.common @@ -8,7 +8,7 @@ MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) SERENITY_BASE_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) INCLUDE_FLAGS = -I$(SERENITY_BASE_DIR) -I$(SERENITY_BASE_DIR)/Libraries -I. -I$(SERENITY_BASE_DIR)/Libraries/LibC -I$(SERENITY_BASE_DIR)/Servers -I$(SERENITY_BASE_DIR)/Libraries/LibM -LDFLAGS = -L$(SERENITY_BASE_DIR)/Libraries/LibC -L$(SERENITY_BASE_DIR)/Libraries/LibCore -L$(SERENITY_BASE_DIR)/Libraries/LibM -L$(SERENITY_BASE_DIR)/Libraries/LibGUI +LDFLAGS = -L$(SERENITY_BASE_DIR)/Libraries/LibC -L$(SERENITY_BASE_DIR)/Libraries/LibCore -L$(SERENITY_BASE_DIR)/Libraries/LibM -L$(SERENITY_BASE_DIR)/Libraries/LibGUI -L$(SERENITY_BASE_DIR)/Libraries/LibAudio CLANG_FLAGS = -Wconsumed -m32 -ffreestanding -march=i686 #SUGGEST_FLAGS = -Wsuggest-final-types -Wsuggest-final-methods -Wsuggest-override #-Wsuggest-attribute=noreturn CXXFLAGS = -MMD -MP $(WARNING_FLAGS) $(OPTIMIZATION_FLAGS) $(FLAVOR_FLAGS) $(ARCH_FLAGS) $(STANDARD_FLAGS) $(SUGGEST_FLAGS) $(INCLUDE_FLAGS) $(DEFINES) diff --git a/Servers/AudioServer/Makefile b/Servers/AudioServer/Makefile index 3e36a18acd..74b7b9c22d 100644 --- a/Servers/AudioServer/Makefile +++ b/Servers/AudioServer/Makefile @@ -11,7 +11,7 @@ DEFINES += -DUSERLAND all: $(APP) $(APP): $(OBJS) - $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lc -lcore + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lc -lcore -laudio .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Servers/AudioServer/main.cpp b/Servers/AudioServer/main.cpp index 43cae47a15..915aad33f5 100644 --- a/Servers/AudioServer/main.cpp +++ b/Servers/AudioServer/main.cpp @@ -1,160 +1,45 @@ #include +#include +#include +#include +#include +#include -u32 read_u32(const ByteBuffer& buf, u32& off) +#include +#include +#include + +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 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(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(); } diff --git a/Servers/WindowServer/WSEventLoop.cpp b/Servers/WindowServer/WSEventLoop.cpp index 781695af91..58819829ac 100644 --- a/Servers/WindowServer/WSEventLoop.cpp +++ b/Servers/WindowServer/WSEventLoop.cpp @@ -26,18 +26,20 @@ WSEventLoop::WSEventLoop() unlink("/tmp/wsportal"); - m_server_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT(m_server_fd >= 0); sockaddr_un address; address.sun_family = AF_LOCAL; strcpy(address.sun_path, "/tmp/wsportal"); - int rc = bind(m_server_fd, (const sockaddr*)&address, sizeof(address)); + int rc = bind(m_server_sock.fd(), (const sockaddr*)&address, sizeof(address)); ASSERT(rc == 0); - rc = listen(m_server_fd, 5); + rc = listen(m_server_sock.fd(), 5); ASSERT(rc == 0); + ASSERT(m_server_sock.fd() >= 0); ASSERT(m_keyboard_fd >= 0); ASSERT(m_mouse_fd >= 0); + + m_server_notifier = make(m_server_sock.fd(), CNotifier::Read); + m_server_notifier->on_ready_to_read = [this] { drain_server(); }; } WSEventLoop::~WSEventLoop() @@ -48,7 +50,7 @@ void WSEventLoop::drain_server() { sockaddr_un address; socklen_t address_size = sizeof(address); - int client_fd = accept(m_server_fd, (sockaddr*)&address, &address_size); + 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 { @@ -333,7 +335,6 @@ void WSEventLoop::add_file_descriptors_for_select(fd_set& fds, int& max_fd_added }; add_fd_to_set(m_keyboard_fd, fds); add_fd_to_set(m_mouse_fd, fds); - add_fd_to_set(m_server_fd, fds); WSClientConnection::for_each_client([&](WSClientConnection& client) { add_fd_to_set(client.fd(), fds); }); @@ -341,8 +342,6 @@ void WSEventLoop::add_file_descriptors_for_select(fd_set& fds, int& max_fd_added void WSEventLoop::process_file_descriptors_after_select(const fd_set& fds) { - if (FD_ISSET(m_server_fd, &fds)) - drain_server(); if (FD_ISSET(m_keyboard_fd, &fds)) drain_keyboard(); if (FD_ISSET(m_mouse_fd, &fds)) diff --git a/Servers/WindowServer/WSEventLoop.h b/Servers/WindowServer/WSEventLoop.h index 8c76a17efb..8935dd0264 100644 --- a/Servers/WindowServer/WSEventLoop.h +++ b/Servers/WindowServer/WSEventLoop.h @@ -2,6 +2,8 @@ #include #include +#include +#include class WSClientConnection; struct WSAPI_ClientMessage; @@ -25,5 +27,6 @@ private: int m_keyboard_fd { -1 }; int m_mouse_fd { -1 }; - int m_server_fd { -1 }; + CLocalSocket m_server_sock; + OwnPtr m_server_notifier; };