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,11 +13,11 @@
#include <format>
#include <map>
/// A PulseAudio implementation of the SoundSubsystem.
class PulseAudioSubsystem : public SoundSubsystem
namespace ReMixer
{
/// A PulseAudio implementation of the SoundSubsystem.
class PulseAudioSubsystem : public SoundSubsystem
{
struct pa_impl
{
pa_threaded_mainloop* mainloop;
@@ -31,7 +31,7 @@ class PulseAudioSubsystem : public SoundSubsystem
int operation_success;
};
std::map<std::string, PulseStream> streams;
public:
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()
@@ -92,6 +92,33 @@ public:
std::cout << "PulseAudio context is ready." << std::endl;
}
void Play(const PulseStream& stream, const Sound& sound)
{
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);
}
bool Connected()
{
return ctx_connected;
@@ -161,8 +188,8 @@ public:
return new_stream;
}
protected:
private:
protected:
private:
static void OnCtxStateChange(pa_context* c, void* userdata)
{
std::cout << "context state callback: ";
@@ -201,6 +228,5 @@ private:
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,10 +2,12 @@
#include <Event.h>
/// A managed sound handler object.
class NuSound
namespace ReMixer
{
public:
/// A managed sound handler object.
class NuSound
{
public:
virtual bool IsPlaying() = 0;
virtual bool IsPaused() = 0;
virtual bool IsFinished() = 0;
@@ -33,55 +35,55 @@ public:
Event<> Paused;
Event<> Played;
Event<> Resumed;
protected:
private:
};
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
{
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:
/// 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();
@@ -94,18 +96,20 @@ public:
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>
class Sound {
private:
namespace ReMixer
{
class Sound {
private:
std::vector<char> audio_data;
SampleRate sample_rate;
//SampleRate sample_rate;
StreamMode num_channels;
public:
public:
static Sound FromOGGVorbisFile(const std::filesystem::path& file_name)
{
return Sound{};
}
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);
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;
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();};
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,29 +14,15 @@
#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;
}
enum class StreamDirection {
PLAYBACK,
RECORD,
};
class Stream {
private:
class Stream {
private:
std::string name;
std::string parent_name;
unsigned int channel_count;
@@ -44,29 +30,21 @@ private:
StreamDirection dir;
std::vector<char> buffer;
#ifdef __linux__
pa_stream* stream = nullptr;
#endif
#ifdef _WIN32
#endif
public:
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);
};
class PulseStream : public Stream
{
public:
};
class PulseStream : public Stream
{
public:
pa_stream* stream;
static void OnStreamStateChanged(pa_stream* s, void* userdata)
{
std::cout << "stream state callback: ";
@@ -112,9 +90,11 @@ public:
pa_threaded_mainloop_signal(p, 0);
}
protected:
private:
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.