1
0
forked from Redacted/ReMixer

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)
add_library(ReMixer ${SOURCES}
src/linux/PulseSubsystem.cpp)
src/linux/PulseSubsystem.cpp
src/linux/SoundSubsystem.cpp)
add_executable(ReMixer-Test main.cpp)
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 <map>
/// A PulseAudio implementation of the SoundSubsystem.
class PulseAudioSubsystem : public SoundSubsystem
namespace ReMixer
{
struct pa_impl
/// A PulseAudio implementation of the SoundSubsystem.
class PulseAudioSubsystem : public SoundSubsystem
{
pa_threaded_mainloop* mainloop;
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()))
struct pa_impl
{
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_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));
if (!(pa->mainloop = pa_threaded_mainloop_new()))
{
throw std::runtime_error("Failed to create a PulseAudio mainloop.");
}
// 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->api = pa_threaded_mainloop_get_api(pa->mainloop);
bool Connected()
{
return ctx_connected;
}
if (!(pa->context = pa_context_new(pa->api, context_name.c_str())))
{
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
// TODO: Expose to the API later
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
pa_threaded_mainloop_lock(pa->mainloop);
const pa_cvolume* volume = NULL;
pa_channel_map* map = NULL;
const char* dev = NULL;
pa_buffer_attr* attr = NULL;
if (pa_threaded_mainloop_start(pa->mainloop) < 0) {
throw std::runtime_error("Failed to start thread mainloop.");
}
PulseStream new_stream = PulseStream();
pa_threaded_mainloop_lock(pa->mainloop);
for (;;) {
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)))
{
throw std::runtime_error("Failed to create a new PulseAudio stream.");
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_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);
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)
void Play(const PulseStream& stream, const Sound& sound)
{
int error = pa_context_errno(pa->context);
throw std::runtime_error(std::format("Failed to connect the stream: {}", error));
pa_threaded_mainloop_lock(pa->mainloop);
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;
state = pa_stream_get_state(new_stream.stream);
return ctx_connected;
}
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);
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);
streams.insert({stream_name, new_stream});
return new_stream;
}
protected:
private:
static void OnCtxStateChange(pa_context* c, void* userdata)
{
std::cout << "context state callback: ";
auto *p = static_cast<pa_impl *>(userdata);
switch(pa_context_get_state(c)) {
case PA_CONTEXT_READY:
std::cout << "Context Ready" << std::endl;
pa_threaded_mainloop_signal(p->mainloop, 0);
break;
case PA_CONTEXT_TERMINATED:
std::cout << "Context Terminated" << std::endl;
pa_threaded_mainloop_signal(p->mainloop, 0);
break;
case PA_CONTEXT_FAILED:
std::cout << "Context Failed" << std::endl;
pa_threaded_mainloop_signal(p->mainloop, 0);
break;
case PA_CONTEXT_UNCONNECTED:
std::cout << "Context Unconnected" << std::endl;
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;
auto *p = static_cast<pa_impl *>(userdata);
switch(pa_context_get_state(c)) {
case PA_CONTEXT_READY:
std::cout << "Context Ready" << std::endl;
pa_threaded_mainloop_signal(p->mainloop, 0);
break;
case PA_CONTEXT_TERMINATED:
std::cout << "Context Terminated" << std::endl;
pa_threaded_mainloop_signal(p->mainloop, 0);
break;
case PA_CONTEXT_FAILED:
std::cout << "Context Failed" << std::endl;
pa_threaded_mainloop_signal(p->mainloop, 0);
break;
case PA_CONTEXT_UNCONNECTED:
std::cout << "Context Unconnected" << std::endl;
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;
bool ctx_connected = false;
};
pa_impl* pa;
bool ctx_connected = false;
};
}

View File

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

View File

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

View File

@@ -4,39 +4,42 @@
#include <pulse/simple.h>
#include <filesystem>
#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);
class Sound {
private:
std::vector<char> audio_data;
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;
};
unsigned int DataSize() const { return audio_data.size();};
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 SFX {
class SFX {
};
};
/// Music class is for longer sounds that should be streamed in.
class Music {
class Music {
};
}
};

View File

@@ -1,6 +1,6 @@
/// ReMixer
/// 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
/// This work is explicitly dedicated to the public domain, for the hopeful betterment of the software industry.
@@ -14,107 +14,87 @@
#include <vector>
#include <pulse/stream.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;
}
unsigned operator ""_kHz(long double frequency)
{
return frequency * 1000;
}
class Stream {
private:
std::string name;
std::string parent_name;
unsigned int channel_count;
unsigned int sample_rate;
StreamDirection dir;
enum class StreamDirection {
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();
std::vector<char> buffer;
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
{
public:
pa_stream* stream;
static void OnStreamStateChanged(pa_stream* s, void* userdata)
class PulseStream : public Stream
{
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) {
case PA_STREAM_READY:
std::cout << "Stream Ready" << std::endl;
pa_threaded_mainloop_signal(p, 0);
break;
case PA_STREAM_FAILED:
std::cout << "Stream Failed" << std::endl;
pa_threaded_mainloop_signal(p, 0);
break;
case PA_STREAM_TERMINATED:
std::cout << "Stream Terminated" << std::endl;
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 OnStreamStateChanged(pa_stream* s, void* userdata)
{
std::cout << "stream state callback: ";
auto *p = static_cast<pa_threaded_mainloop *>(userdata);
auto state = pa_stream_get_state(s);
switch(state) {
case PA_STREAM_READY:
std::cout << "Stream Ready" << std::endl;
pa_threaded_mainloop_signal(p, 0);
break;
case PA_STREAM_FAILED:
std::cout << "Stream Failed" << std::endl;
pa_threaded_mainloop_signal(p, 0);
break;
case PA_STREAM_TERMINATED:
std::cout << "Stream Terminated" << std::endl;
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)
{
std::cout << "Received stream request callback: " << length << std::endl;
static void OnStreamRequest(pa_stream* s, size_t length, void* userdata)
{
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)
{
std::cout << "Received stream latency update callback" << std::endl;
auto *p = static_cast<pa_threaded_mainloop *>(userdata);
static void OnLatencyUpdate(pa_stream* s, void* userdata)
{
std::cout << "Received stream latency update callback" << std::endl;
auto *p = static_cast<pa_threaded_mainloop *>(userdata);
pa_threaded_mainloop_signal(p, 0);
}
protected:
private:
pa_threaded_mainloop_signal(p, 0);
}
protected:
private:
};
}
};

View File

@@ -9,27 +9,36 @@
/// @edit 2024-08-06
#include <iostream>
#include <ReMixer/stream.h>
#include <ReMixer/sound.h>
#include <ReMixer/PulseSubsystem.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#include <iostream>
#include <fstream>
#include <thread>
#include <ReMixer/stream.h>
#include <ReMixer/sound.h>
#include <ReMixer/PulseSubsystem.h>
#include <ReMixer/ReMixer.h>
[[noreturn]] int main() {
using namespace ReMixer;
using namespace std::chrono_literals;
PulseAudioSubsystem test = PulseAudioSubsystem("PulseAudio Test");
PulseStream test_stream = test.CreateStream("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)
{
std::this_thread::sleep_for(25ms);
//std::this_thread::sleep_for(25ms);
}
return 0;

View File

@@ -1,11 +1,10 @@
#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) {
//return new SFX(file);
unsigned ReMixer::operator ""_kHz(long double frequency) {
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 <vorbis/codec.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) {
audio_data = audioData;
ReMixer::Sound ReMixer::Sound::FromPCMFile(const std::filesystem::path &file_name) {
Sound sound;
sound.LoadPCMFile(file_name);
return sound;
}
unsigned int Sound::getNumberOfChannels() const {
return (unsigned int) num_channels + 1;
ReMixer::Sound ReMixer::Sound::FromPCMBuffer(std::vector<char> buffer) {
Sound sound;
sound.LoadPCMBuffer(buffer);
return sound;
}
SampleRate Sound::getSampleRate() {
return sample_rate;
void ReMixer::Sound::LoadPCMFile(const std::filesystem::path &file_name) {
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) {
num_channels = mode;
void ReMixer::Sound::LoadPCMBuffer(std::vector<char> buffer) {
audio_data = buffer;
}
void Sound::setSampleRate(SampleRate rate) {
sample_rate = rate;
}
/*
* SFX::SFX(const std::string& sfx_file_path) {
void ReMixer::Sound::LoadOGGVorbisFile(const std::filesystem::path &file_name) {
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)
throw std::runtime_error(std::string("Couldn't open the input file " + sfx_file_path));
if (ov_open(inFile, &vf, nullptr, 0) < 0)
throw std::runtime_error(std::string("libVorbis couldn't open the input file " + sfx_file_path));
if (ov_open(inFile, &vf, NULL, 0) < 0) {
std::cerr << "Error opening Ogg Vorbis file." << std::endl;
fclose(inFile);
return;
}
vorbis_info* vi = ov_info(&vf, -1);
char pcmout[4096];
std::vector<char> raw_audio;
int current_section;
long bytes;
while ((bytes = ov_read(&vf, pcmout, sizeof(pcmout), 0, 2, 1, &current_section)) > 0)
raw_audio.insert(raw_audio.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);
}
*/
while ((bytes = ov_read(&vf, pcmout, sizeof(pcmout), 0, 2, 1, &current_section)) > 0) {
audio_data.insert(audio_data.end(), pcmout, pcmout + bytes);
}
}

BIN
test-sounds/output.raw Normal file

Binary file not shown.

BIN
test-sounds/wind.raw Normal file

Binary file not shown.