Functioning High-level ReMixer API (First sound played!!)

This commit is contained in:
2024-08-28 14:55:53 -04:00
parent b0da5f4297
commit bdbb5aff13
18 changed files with 456 additions and 419 deletions

View File

@@ -38,7 +38,8 @@ endif()
include_directories("include" ${uuid_SOURCE_DIR}/include) include_directories("include" ${uuid_SOURCE_DIR}/include)
add_library(ReMixer ${SOURCES} add_library(ReMixer ${SOURCES}
src/linux/PulseSubsystem.cpp) src/linux/PulseSubsystem.cpp
src/linux/SoundSubsystem.cpp)
add_executable(ReMixer-Test main.cpp) add_executable(ReMixer-Test main.cpp)
set_target_properties(ReMixer PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(ReMixer PROPERTIES LINKER_LANGUAGE CXX)

Binary file not shown.

BIN
cmake-build-debug/click.ogg Normal file

Binary file not shown.

Binary file not shown.

BIN
cmake-build-debug/wind.ogg Normal file

Binary file not shown.

BIN
cmake-build-debug/wind.raw Normal file

Binary file not shown.

View File

@@ -13,194 +13,220 @@
#include <format> #include <format>
#include <map> #include <map>
namespace ReMixer
/// A PulseAudio implementation of the SoundSubsystem.
class PulseAudioSubsystem : public SoundSubsystem
{ {
struct pa_impl /// A PulseAudio implementation of the SoundSubsystem.
class PulseAudioSubsystem : public SoundSubsystem
{ {
pa_threaded_mainloop* mainloop; struct pa_impl
pa_context* context;
pa_stream_direction_t* direction;
pa_mainloop_api* api;
const void* read_data;
size_t read_index, read_length;
int operation_success;
};
std::map<std::string, PulseStream> streams;
public:
/// Constructs a new PulseAudio context with the given name.
/// @param name The context name. All streams created with this context will be grouped together by this name.
PulseAudioSubsystem(const std::string& context_name) : SoundSubsystem()
{
const char *server = NULL;
pa = pa_xnew(pa_impl, 1);
int error = PA_ERR_INTERNAL;
if (!(pa->mainloop = pa_threaded_mainloop_new()))
{ {
throw std::runtime_error("Failed to create a PulseAudio mainloop."); pa_threaded_mainloop* mainloop;
} pa_context* context;
pa_stream_direction_t* direction;
pa_mainloop_api* api;
pa->api = pa_threaded_mainloop_get_api(pa->mainloop); const void* read_data;
size_t read_index, read_length;
if (!(pa->context = pa_context_new(pa->api, context_name.c_str()))) int operation_success;
};
std::map<std::string, PulseStream> streams;
public:
/// Constructs a new PulseAudio context with the given name.
/// @param name The context name. All streams created with this context will be grouped together by this name.
PulseAudioSubsystem(const std::string& context_name) : SoundSubsystem()
{ {
throw std::runtime_error("Failed to create a PulseAudio context."); const char *server = NULL;
} pa = pa_xnew(pa_impl, 1);
pa_context_flags flags = pa_context_flags::PA_CONTEXT_NOAUTOSPAWN; int error = PA_ERR_INTERNAL;
pa_context_set_state_callback(pa->context, OnCtxStateChange, pa); if (!(pa->mainloop = pa_threaded_mainloop_new()))
{
throw std::runtime_error("Failed to create a PulseAudio mainloop.");
if (pa_context_connect(pa->context, server, flags, NULL) < 0)
{
error = pa_context_errno(pa->context);
throw std::runtime_error(std::format("Failed to connect to context {}", error));
} else{
ctx_connected = true;
}
pa_threaded_mainloop_lock(pa->mainloop);
if (pa_threaded_mainloop_start(pa->mainloop) < 0) {
throw std::runtime_error("Failed to start thread mainloop.");
}
for (;;) {
pa_context_state_t state;
state = pa_context_get_state(pa->context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state)) {
error = pa_context_errno(pa->context);
throw std::runtime_error(std::format("Context was not good: {}", error));
} }
// Wait until the context is ready. pa->api = pa_threaded_mainloop_get_api(pa->mainloop);
pa_threaded_mainloop_wait(pa->mainloop);
}
pa_threaded_mainloop_unlock(pa->mainloop);
std::cout << "PulseAudio context is ready." << std::endl;
}
bool Connected() if (!(pa->context = pa_context_new(pa->api, context_name.c_str())))
{ {
return ctx_connected; throw std::runtime_error("Failed to create a PulseAudio context.");
} }
pa_context_flags flags = pa_context_flags::PA_CONTEXT_NOAUTOSPAWN;
pa_context_set_state_callback(pa->context, OnCtxStateChange, pa);
PulseStream CreateStream(const std::string& stream_name) if (pa_context_connect(pa->context, server, flags, NULL) < 0)
{ {
error = pa_context_errno(pa->context);
throw std::runtime_error(std::format("Failed to connect to context {}", error));
} else{
ctx_connected = true;
}
// Assumed defaults pa_threaded_mainloop_lock(pa->mainloop);
// TODO: Expose to the API later
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
const pa_cvolume* volume = NULL; if (pa_threaded_mainloop_start(pa->mainloop) < 0) {
pa_channel_map* map = NULL; throw std::runtime_error("Failed to start thread mainloop.");
const char* dev = NULL; }
pa_buffer_attr* attr = NULL;
PulseStream new_stream = PulseStream(); for (;;) {
pa_threaded_mainloop_lock(pa->mainloop); pa_context_state_t state;
state = pa_context_get_state(pa->context);
if (!(new_stream.stream = pa_stream_new(pa->context, stream_name.c_str(), &ss, map))) if (state == PA_CONTEXT_READY)
{ break;
throw std::runtime_error("Failed to create a new PulseAudio stream.");
if (!PA_CONTEXT_IS_GOOD(state)) {
error = pa_context_errno(pa->context);
throw std::runtime_error(std::format("Context was not good: {}", error));
}
// Wait until the context is ready.
pa_threaded_mainloop_wait(pa->mainloop);
}
pa_threaded_mainloop_unlock(pa->mainloop);
std::cout << "PulseAudio context is ready." << std::endl;
} }
pa_stream_set_state_callback(new_stream.stream, PulseStream::OnStreamStateChanged, pa->mainloop); void Play(const PulseStream& stream, const Sound& sound)
pa_stream_set_read_callback(new_stream.stream, PulseStream::OnStreamRequest, pa->mainloop);
pa_stream_set_write_callback(new_stream.stream, PulseStream::OnStreamRequest, pa->mainloop);
pa_stream_set_latency_update_callback(new_stream.stream, PulseStream::OnLatencyUpdate, pa->mainloop);
pa_stream_flags flags = static_cast<pa_stream_flags>(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
PA_STREAM_AUTO_TIMING_UPDATE);
// TODO: Support Playback and Recording streams
int r = pa_stream_connect_playback(new_stream.stream, dev, attr, flags, volume, NULL);
if (r < 0)
{ {
int error = pa_context_errno(pa->context); pa_threaded_mainloop_lock(pa->mainloop);
throw std::runtime_error(std::format("Failed to connect the stream: {}", error));
size_t length = sound.DataSize();
const void* data = sound.ptr();
while(length > 0)
{
size_t l;
int r;
while(!(l = pa_stream_writable_size(stream.stream))) {
pa_threaded_mainloop_wait(pa->mainloop);
}
if (l > length)
l = length;
r = pa_stream_write(stream.stream, data, l, nullptr, 0LL, PA_SEEK_RELATIVE);
data = (const char*) data + l;
length -= l;
}
pa_threaded_mainloop_unlock(pa->mainloop);
} }
for (;;) bool Connected()
{ {
pa_stream_state_t state; return ctx_connected;
state = pa_stream_get_state(new_stream.stream); }
if (state == PA_STREAM_READY)
break;
if (!PA_STREAM_IS_GOOD(state)) PulseStream CreateStream(const std::string& stream_name)
{
// Assumed defaults
// TODO: Expose to the API later
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
const pa_cvolume* volume = NULL;
pa_channel_map* map = NULL;
const char* dev = NULL;
pa_buffer_attr* attr = NULL;
PulseStream new_stream = PulseStream();
pa_threaded_mainloop_lock(pa->mainloop);
if (!(new_stream.stream = pa_stream_new(pa->context, stream_name.c_str(), &ss, map)))
{
throw std::runtime_error("Failed to create a new PulseAudio stream.");
}
pa_stream_set_state_callback(new_stream.stream, PulseStream::OnStreamStateChanged, pa->mainloop);
pa_stream_set_read_callback(new_stream.stream, PulseStream::OnStreamRequest, pa->mainloop);
pa_stream_set_write_callback(new_stream.stream, PulseStream::OnStreamRequest, pa->mainloop);
pa_stream_set_latency_update_callback(new_stream.stream, PulseStream::OnLatencyUpdate, pa->mainloop);
pa_stream_flags flags = static_cast<pa_stream_flags>(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
PA_STREAM_AUTO_TIMING_UPDATE);
// TODO: Support Playback and Recording streams
int r = pa_stream_connect_playback(new_stream.stream, dev, attr, flags, volume, NULL);
if (r < 0)
{ {
int error = pa_context_errno(pa->context); int error = pa_context_errno(pa->context);
throw std::runtime_error(std::format("Stream state is bad: {}", error)); throw std::runtime_error(std::format("Failed to connect the stream: {}", error));
} }
pa_threaded_mainloop_wait(pa->mainloop); for (;;)
{
pa_stream_state_t state;
state = pa_stream_get_state(new_stream.stream);
if (state == PA_STREAM_READY)
break;
if (!PA_STREAM_IS_GOOD(state))
{
int error = pa_context_errno(pa->context);
throw std::runtime_error(std::format("Stream state is bad: {}", error));
}
pa_threaded_mainloop_wait(pa->mainloop);
}
pa_threaded_mainloop_unlock(pa->mainloop);
streams.insert({stream_name, new_stream});
return new_stream;
} }
protected:
private:
static void OnCtxStateChange(pa_context* c, void* userdata)
{
std::cout << "context state callback: ";
pa_threaded_mainloop_unlock(pa->mainloop); auto *p = static_cast<pa_impl *>(userdata);
switch(pa_context_get_state(c)) {
streams.insert({stream_name, new_stream}); case PA_CONTEXT_READY:
std::cout << "Context Ready" << std::endl;
return new_stream; pa_threaded_mainloop_signal(p->mainloop, 0);
} break;
protected: case PA_CONTEXT_TERMINATED:
private: std::cout << "Context Terminated" << std::endl;
static void OnCtxStateChange(pa_context* c, void* userdata) pa_threaded_mainloop_signal(p->mainloop, 0);
{ break;
std::cout << "context state callback: "; case PA_CONTEXT_FAILED:
std::cout << "Context Failed" << std::endl;
auto *p = static_cast<pa_impl *>(userdata); pa_threaded_mainloop_signal(p->mainloop, 0);
switch(pa_context_get_state(c)) { break;
case PA_CONTEXT_READY: case PA_CONTEXT_UNCONNECTED:
std::cout << "Context Ready" << std::endl; std::cout << "Context Unconnected" << std::endl;
pa_threaded_mainloop_signal(p->mainloop, 0); break;
break; case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_TERMINATED: std::cout << "Context Connecting" << std::endl;
std::cout << "Context Terminated" << std::endl; break;
pa_threaded_mainloop_signal(p->mainloop, 0); case PA_CONTEXT_AUTHORIZING:
break; std::cout << "Context Authorizing" << std::endl;
case PA_CONTEXT_FAILED: break;
std::cout << "Context Failed" << std::endl; case PA_CONTEXT_SETTING_NAME:
pa_threaded_mainloop_signal(p->mainloop, 0); std::cout << "Context Setting Name" << std::endl;
break; break;
case PA_CONTEXT_UNCONNECTED: default:
std::cout << "Context Unconnected" << std::endl; std::cerr << "The fuck?" << std::endl;
break; break;
case PA_CONTEXT_CONNECTING: }
std::cout << "Context Connecting" << std::endl;
break;
case PA_CONTEXT_AUTHORIZING:
std::cout << "Context Authorizing" << std::endl;
break;
case PA_CONTEXT_SETTING_NAME:
std::cout << "Context Setting Name" << std::endl;
break;
default:
std::cerr << "The fuck?" << std::endl;
break;
} }
} pa_impl* pa;
pa_impl* pa; bool ctx_connected = false;
bool ctx_connected = false;
};
};
}

View File

@@ -1,11 +1,18 @@
#pragma once #pragma once
#include <ReMixer/stream.h>
#include <ReMixer/sound.h>
namespace ReMixer { namespace ReMixer {
enum class StreamDirection {
PLAYBACK,
RECORD,
};
enum class StreamMode : bool {
MONO = false,
STEREO = true
};
unsigned operator ""_Hz(unsigned long long int frequency);
unsigned operator ""_kHz(long double frequency);
uint16_t createStream(const std::string& application_name, const std::string& stream_name, SampleRate sample_rate, StreamMode mode);
SFX* loadSFX(const std::string& file);
} }

View File

@@ -2,110 +2,114 @@
#include <Event.h> #include <Event.h>
/// A managed sound handler object. namespace ReMixer
class NuSound
{ {
public: /// A managed sound handler object.
virtual bool IsPlaying() = 0; class NuSound
virtual bool IsPaused() = 0; {
virtual bool IsFinished() = 0; public:
float PlaybackLoudness(); virtual bool IsPlaying() = 0;
int ChannelCount(); virtual bool IsPaused() = 0;
bool Looped(); virtual bool IsFinished() = 0;
float PlaybackSpeed(); float PlaybackLoudness();
float TimeLength(); int ChannelCount();
float TimePosition(); bool Looped();
float Volume(); float PlaybackSpeed();
float TimeLength();
float TimePosition();
float Volume();
void SetTimePosition(float timePos); void SetTimePosition(float timePos);
void SetVolume(float vol); void SetVolume(float vol);
void SetPlaybackSpeed(float speed); void SetPlaybackSpeed(float speed);
virtual void Pause() = 0; virtual void Pause() = 0;
virtual void Play() = 0; virtual void Play() = 0;
void Resume(); void Resume();
void Stop(); void Stop();
Event<int> DidLoop; Event<int> DidLoop;
Event<> Ended; Event<> Ended;
Event<> Paused; Event<> Paused;
Event<> Played; Event<> Played;
Event<> Resumed; Event<> Resumed;
protected: protected:
private: private:
}; };
/// TODO: Equalizer Effect /// TODO: Equalizer Effect
/// TODO: Compressor Effect /// TODO: Compressor Effect
/// TODO: Reverb Effect /// TODO: Reverb Effect
/// TODO: Chorus Effect /// TODO: Chorus Effect
/// TODO: Distortion Effect /// TODO: Distortion Effect
/// TODO: Echo Effect /// TODO: Echo Effect
/// TODO: Flange Effect /// TODO: Flange Effect
/// TODO: Pitch Shift Effect /// TODO: Pitch Shift Effect
/// TODO: Tremolo Effect /// TODO: Tremolo Effect
/// TODO: Positional Sound Object? /// TODO: Positional Sound Object?
/// Physical representation of Pulse-Code-Modulated sound data. /// Physical representation of Pulse-Code-Modulated sound data.
class PCM class PCM
{ {
}; };
class Decibels class Decibels
{ {
}; };
enum class SoundApi enum class SoundApi
{ {
PulseAudio, /// Currently supported. PulseAudio, /// Currently supported.
ALSA, /// Not yet supported. ALSA, /// Not yet supported.
PipeWire, /// Not yet supported. PipeWire, /// Not yet supported.
WASAPI, /// Work-in-progress support. WASAPI, /// Work-in-progress support.
OSS, /// Not yet supported. OSS, /// Not yet supported.
CoreAudio /// Support not planned. CoreAudio /// Support not planned.
}; };
class AudioDevice {}; class AudioDevice {};
class PlaybackDevice : public AudioDevice {}; class PlaybackDevice : public AudioDevice {};
class RecordingDevice : public AudioDevice {}; class RecordingDevice : public AudioDevice {};
/// An abstract class that defines the SoundSubsystem which will be used by the higher-level ReMixer API. /// An abstract class that defines the SoundSubsystem which will be used by the higher-level ReMixer API.
/// Since it is common for sound APIs to operate on a separate (or even multiple) threads, /// Since it is common for sound APIs to operate on a separate (or even multiple) threads,
/// The subsystem also functions as a "controller" that manages these separate threads. /// The subsystem also functions as a "controller" that manages these separate threads.
/// Therefore, it is safe to use in a single-threaded application. /// Therefore, it is safe to use in a single-threaded application.
class SoundSubsystem class SoundSubsystem
{ {
public: public:
std::vector<AudioDevice> GetAudioDevices(); std::vector<AudioDevice> GetAudioDevices();
std::vector<PlaybackDevice> GetPlaybackDevices(); std::vector<PlaybackDevice> GetPlaybackDevices();
std::vector<RecordingDevice> GetRecordingDevices(); std::vector<RecordingDevice> GetRecordingDevices();
std::string GetPlaybackDevice(); std::string GetPlaybackDevice();
std::string GetRecordingDevice(); std::string GetRecordingDevice();
void SetPlaybackDevice(); void SetPlaybackDevice();
void SetRecordingDevice(); void SetRecordingDevice();
void SetMasterVolume(Decibels db); void SetMasterVolume(Decibels db);
protected: protected:
private: private:
}; };
/// A Windows Sound API implementation of the SoundSubsystem. /// A Windows Sound API implementation of the SoundSubsystem.
class WASAPISubsystem : public SoundSubsystem class WASAPISubsystem : public SoundSubsystem
{ {
public: public:
protected: protected:
private: private:
}; };
}

View File

@@ -17,20 +17,20 @@ class TakenStreamIDException : public std::exception
}; };
class StreamManager { //class StreamManager {
public: //public:
static std::map<uint16_t, Stream *> streamList; //static std::map<uint16_t, Stream *> streamList;
//StreamManager(); //StreamManager();
//StreamManager(const StreamManager&); //StreamManager(const StreamManager&);
// Get stream using handle // Get stream using handle
static Stream *GetStream(uint16_t handle) //static Stream *GetStream(uint16_t handle)
{ //{
return streamList.at(handle); // return streamList.at(handle);
} //}
// Return Stream handle // Return Stream handle
// TODO: // TODO:
static Stream* CreateStream() /*static Stream* CreateStream()
{ {
// Can we create the stream here and assign the handle on our side? // Can we create the stream here and assign the handle on our side?
@@ -45,4 +45,4 @@ public:
auto begin() { return streamList.begin(); }; auto begin() { return streamList.begin(); };
auto end() { return streamList.end(); }; auto end() { return streamList.end(); };
}; };*/

View File

@@ -4,39 +4,42 @@
#include <pulse/simple.h> #include <pulse/simple.h>
#include <filesystem> #include <filesystem>
#include <ReMixer/stream.h> #include <ReMixer/stream.h>
#include <ReMixer/ReMixer.h>
#include <iterator>
#include <vorbis/vorbisfile.h>
namespace ReMixer
{
class Sound {
private:
std::vector<char> audio_data;
//SampleRate sample_rate;
StreamMode num_channels;
public:
static Sound FromOGGVorbisFile(const std::filesystem::path& file_name);
static Sound FromPCMFile(const std::filesystem::path& file_name);
static Sound FromPCMBuffer(std::vector<char> buffer);
void LoadOGGVorbisFile(const std::filesystem::path& file_name);
void LoadPCMFile(const std::filesystem::path& file_name);
void LoadPCMBuffer(std::vector<char> buffer);
unsigned int DataSize() const { return audio_data.size();};
class Sound { char* ptr() { return audio_data.data();}
private: [[nodiscard]] const char* ptr() const { return audio_data.data();}
std::vector<char> audio_data; Sound() = default;
SampleRate sample_rate; };
StreamMode num_channels;
public:
static Sound FromOGGVorbisFile(const std::filesystem::path& file_name)
{
return Sound{};
}
std::vector<char> getAudioData();
void setAudioData(const std::vector<char>& audioData);
void setNumberOfChannels(StreamMode mode);
void setSampleRate(SampleRate rate);
SampleRate getSampleRate();
[[nodiscard]] unsigned int getNumberOfChannels() const;
char* ptr() { return audio_data.data();}
[[nodiscard]] const char* ptr() const { return audio_data.data();}
Sound() = default;
};
/// Class that holds a short sound that can fit in memory and requires no latency. (i.e. footsteps or gun shots). /// Class that holds a short sound that can fit in memory and requires no latency. (i.e. footsteps or gun shots).
class SFX { class SFX {
}; };
/// Music class is for longer sounds that should be streamed in. /// Music class is for longer sounds that should be streamed in.
class Music { class Music {
};
}
};

View File

@@ -1,6 +1,6 @@
/// ReMixer /// ReMixer
/// A Public Domain C++ Audio Playback Library /// A Public Domain C++ Audio Playback Library
//By william @ RedactedSoftware. Thanks to Dawsh & Maxine. /// By william @ RedactedSoftware. Thanks to Dawsh & Maxine.
/// (c) 2024 Redacted Software /// (c) 2024 Redacted Software
/// This work is explicitly dedicated to the public domain, for the hopeful betterment of the software industry. /// This work is explicitly dedicated to the public domain, for the hopeful betterment of the software industry.
@@ -14,107 +14,87 @@
#include <vector> #include <vector>
#include <pulse/stream.h> #include <pulse/stream.h>
#include <pulse/thread-mainloop.h> #include <pulse/thread-mainloop.h>
#include <iostream>
#include "sound.h"
#include <ReMixer/ReMixer.h>
enum class StreamMode : bool {
MONO = false,
STEREO = true
};
unsigned operator ""_Hz(unsigned long long int frequency) namespace ReMixer
{ {
return frequency; class Stream {
} private:
unsigned operator ""_kHz(long double frequency) std::string name;
{ std::string parent_name;
return frequency * 1000; unsigned int channel_count;
} unsigned int sample_rate;
StreamDirection dir;
enum class StreamDirection { std::vector<char> buffer;
PLAYBACK,
RECORD,
};
class Stream {
private:
std::string name;
std::string parent_name;
unsigned int channel_count;
unsigned int sample_rate;
StreamDirection dir;
std::vector<char> buffer;
#ifdef __linux__
pa_stream* stream = nullptr;
#endif
#ifdef _WIN32
#endif
public:
size_t bufferSize;
uint16_t handle;
//Stream(const std::string& application_name, const std::string& stream_name, SampleRate sample_rate, StreamMode mode);
unsigned int numChannels();
void test_play(const void *data, size_t size, int *err); public:
}; size_t bufferSize;
uint16_t handle;
//Stream(const std::string& application_name, const std::string& stream_name, SampleRate sample_rate, StreamMode mode);
};
class PulseStream : public Stream class PulseStream : public Stream
{
public:
pa_stream* stream;
static void OnStreamStateChanged(pa_stream* s, void* userdata)
{ {
std::cout << "stream state callback: "; public:
pa_stream* stream;
auto *p = static_cast<pa_threaded_mainloop *>(userdata);
auto state = pa_stream_get_state(s);
switch(state) { static void OnStreamStateChanged(pa_stream* s, void* userdata)
case PA_STREAM_READY: {
std::cout << "Stream Ready" << std::endl; std::cout << "stream state callback: ";
pa_threaded_mainloop_signal(p, 0);
break; auto *p = static_cast<pa_threaded_mainloop *>(userdata);
case PA_STREAM_FAILED:
std::cout << "Stream Failed" << std::endl; auto state = pa_stream_get_state(s);
pa_threaded_mainloop_signal(p, 0); switch(state) {
break; case PA_STREAM_READY:
case PA_STREAM_TERMINATED: std::cout << "Stream Ready" << std::endl;
std::cout << "Stream Terminated" << std::endl; pa_threaded_mainloop_signal(p, 0);
pa_threaded_mainloop_signal(p, 0); break;
break; case PA_STREAM_FAILED:
case PA_STREAM_UNCONNECTED: std::cout << "Stream Failed" << std::endl;
std::cout << "Stream Unconnected" << std::endl; pa_threaded_mainloop_signal(p, 0);
break; break;
case PA_STREAM_CREATING: case PA_STREAM_TERMINATED:
std::cout << "Stream Creating" << std::endl; std::cout << "Stream Terminated" << std::endl;
break; pa_threaded_mainloop_signal(p, 0);
break;
case PA_STREAM_UNCONNECTED:
std::cout << "Stream Unconnected" << std::endl;
break;
case PA_STREAM_CREATING:
std::cout << "Stream Creating" << std::endl;
break;
}
} }
}
static void OnStreamRequest(pa_stream* s, size_t length, void* userdata) static void OnStreamRequest(pa_stream* s, size_t length, void* userdata)
{ {
std::cout << "Received stream request callback: " << length << std::endl; std::cout << "Received stream request callback: " << length << std::endl;
auto *p = static_cast<pa_threaded_mainloop *>(userdata); auto *p = static_cast<pa_threaded_mainloop *>(userdata);
pa_threaded_mainloop_signal(p, 0); pa_threaded_mainloop_signal(p, 0);
} }
static void OnLatencyUpdate(pa_stream* s, void* userdata) static void OnLatencyUpdate(pa_stream* s, void* userdata)
{ {
std::cout << "Received stream latency update callback" << std::endl; std::cout << "Received stream latency update callback" << std::endl;
auto *p = static_cast<pa_threaded_mainloop *>(userdata); auto *p = static_cast<pa_threaded_mainloop *>(userdata);
pa_threaded_mainloop_signal(p, 0); pa_threaded_mainloop_signal(p, 0);
} }
protected: protected:
private: private:
};
}
};

View File

@@ -9,27 +9,36 @@
/// @edit 2024-08-06 /// @edit 2024-08-06
#include <iostream> #include <iostream>
#include <ReMixer/stream.h>
#include <ReMixer/sound.h>
#include <ReMixer/PulseSubsystem.h>
#include <vorbis/codec.h> #include <vorbis/codec.h>
#include <vorbis/vorbisfile.h> #include <vorbis/vorbisfile.h>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include <ReMixer/stream.h>
#include <ReMixer/sound.h>
#include <ReMixer/PulseSubsystem.h>
#include <ReMixer/ReMixer.h>
[[noreturn]] int main() { [[noreturn]] int main() {
using namespace ReMixer;
using namespace std::chrono_literals; using namespace std::chrono_literals;
PulseAudioSubsystem test = PulseAudioSubsystem("PulseAudio Test"); PulseAudioSubsystem test = PulseAudioSubsystem("PulseAudio Test");
PulseStream test_stream = test.CreateStream("Test Stream"); PulseStream test_stream = test.CreateStream("Test Stream");
PulseStream test_stream2 = test.CreateStream("Another Test Stream"); PulseStream test_stream2 = test.CreateStream("Another Test Stream");
Sound test_sound = Sound::FromPCMFile("output.raw");
Sound ogg_test_sound = Sound::FromOGGVorbisFile("song.ogg");
test.Play(test_stream, test_sound);
//test.Play(test_stream2, ogg_test_sound);
while(true) while(true)
{ {
std::this_thread::sleep_for(25ms); //std::this_thread::sleep_for(25ms);
} }
return 0; return 0;

View File

@@ -1,11 +1,10 @@
#include <ReMixer/ReMixer.h> #include <ReMixer/ReMixer.h>
uint16_t ReMixer::createStream(const std::string& application_name, const std::string& stream_name, SampleRate sample_rate, StreamMode mode) {
auto s = Stream(application_name, stream_name, sample_rate, mode);
return s.handle;
unsigned ReMixer::operator ""_Hz(unsigned long long int frequency) {
return frequency;
} }
SFX* ReMixer::loadSFX(const std::string& file) { unsigned ReMixer::operator ""_kHz(long double frequency) {
//return new SFX(file); return frequency * 1000;
} }

View File

@@ -0,0 +1,3 @@
//
// Created by dawsh on 8/28/24.
//

View File

@@ -1,64 +1,69 @@
#include <ReMixer/sound.h> #include <ReMixer/sound.h>
#include <vorbis/codec.h> #include <vorbis/codec.h>
#include <vorbis/vorbisfile.h> #include <vorbis/vorbisfile.h>
#include <fstream>
std::vector<char> Sound::getAudioData() {
return audio_data;
ReMixer::Sound ReMixer::Sound::FromOGGVorbisFile(const std::filesystem::path &file_name) {
Sound sound;
sound.LoadOGGVorbisFile(file_name);
return sound;
} }
void Sound::setAudioData(const std::vector<char>& audioData) { ReMixer::Sound ReMixer::Sound::FromPCMFile(const std::filesystem::path &file_name) {
audio_data = audioData; Sound sound;
sound.LoadPCMFile(file_name);
return sound;
} }
unsigned int Sound::getNumberOfChannels() const { ReMixer::Sound ReMixer::Sound::FromPCMBuffer(std::vector<char> buffer) {
return (unsigned int) num_channels + 1; Sound sound;
sound.LoadPCMBuffer(buffer);
return sound;
} }
SampleRate Sound::getSampleRate() { void ReMixer::Sound::LoadPCMFile(const std::filesystem::path &file_name) {
return sample_rate; std::ifstream file(file_name, std::ios::binary | std::ios::ate);
file.unsetf(std::ios::skipws);
file.seekg(0, std::ios::end);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
audio_data.reserve(size);
audio_data.insert(audio_data.begin(),
std::istream_iterator<char>(file),
std::istream_iterator<char>());
} }
void Sound::setNumberOfChannels(StreamMode mode) { void ReMixer::Sound::LoadPCMBuffer(std::vector<char> buffer) {
num_channels = mode; audio_data = buffer;
} }
void Sound::setSampleRate(SampleRate rate) { void ReMixer::Sound::LoadOGGVorbisFile(const std::filesystem::path &file_name) {
sample_rate = rate;
}
/*
* SFX::SFX(const std::string& sfx_file_path) {
OggVorbis_File vf; OggVorbis_File vf;
FILE* inFile = fopen(sfx_file_path.c_str(), "rb"); FILE* inFile = fopen(file_name.c_str(), "rb");
if (!inFile) {
std::cerr << "Error opening input file." << std::endl;
return;
}
if (!inFile) if (ov_open(inFile, &vf, NULL, 0) < 0) {
throw std::runtime_error(std::string("Couldn't open the input file " + sfx_file_path)); std::cerr << "Error opening Ogg Vorbis file." << std::endl;
fclose(inFile);
if (ov_open(inFile, &vf, nullptr, 0) < 0) return;
throw std::runtime_error(std::string("libVorbis couldn't open the input file " + sfx_file_path)); }
vorbis_info* vi = ov_info(&vf, -1); vorbis_info* vi = ov_info(&vf, -1);
char pcmout[4096]; char pcmout[4096];
std::vector<char> raw_audio;
int current_section; int current_section;
long bytes; long bytes;
while ((bytes = ov_read(&vf, pcmout, sizeof(pcmout), 0, 2, 1, &current_section)) > 0) while ((bytes = ov_read(&vf, pcmout, sizeof(pcmout), 0, 2, 1, &current_section)) > 0) {
raw_audio.insert(raw_audio.end(), pcmout, pcmout + bytes); audio_data.insert(audio_data.end(), pcmout, pcmout + bytes);
}
}
if (vi->channels == 1)
setNumberOfChannels(StreamMode::MONO);
else if (vi->channels == 2)
setNumberOfChannels(StreamMode::STEREO);
if (vi->rate == 44100)
setSampleRate(SampleRate::STANDARD);
else if (vi->rate == 48000)
setSampleRate(SampleRate::HIGH);
ov_clear(&vf);
}
*/

BIN
test-sounds/output.raw Normal file

Binary file not shown.

BIN
test-sounds/wind.raw Normal file

Binary file not shown.