generated from sigonasr2/CPlusPlusProjectTemplate
Use new olcSoundWaveEngine
Co-authored-by: sigonasr2 <sigonasr2@gmail.com>
This commit is contained in:
parent
92ab6d3a9c
commit
8fabcf624f
Binary file not shown.
2
sig
2
sig
@ -3,7 +3,7 @@ export AUTO_UPDATE=false
|
||||
source utils/define.sh
|
||||
|
||||
define PROJECT_NAME "C++ProjectTemplate"
|
||||
define CUSTOM_PARAMS "-lX11 -lGL -lpthread -lpng -lstdc++fs -lasound"
|
||||
define CUSTOM_PARAMS "-std=c++17 -lX11 -lGL -lpthread -lpng -lstdc++fs -lpulse -lpulse-simple"
|
||||
define LANGUAGE "C++"
|
||||
|
||||
source utils/main.sh
|
281
soundExtension.h
281
soundExtension.h
@ -1,6 +1,6 @@
|
||||
/*
|
||||
+-------------------------------------------------------------+
|
||||
| OneLoneCoder Sound Wave Engine v0.01 |
|
||||
| OneLoneCoder Sound Wave Engine v0.02 |
|
||||
| "You wanted noise? Well is this loud enough?" - javidx9 |
|
||||
+-------------------------------------------------------------+
|
||||
|
||||
@ -64,40 +64,74 @@
|
||||
Homepage: https://www.onelonecoder.com
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
|
||||
Compiling in Linux
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
When compiling, you will need to include ALSA to your list of library flags.
|
||||
Add: -lasound
|
||||
|
||||
Your command should look something like:
|
||||
|
||||
g++ -o YourProgName YourSource.cpp -lX11 -lGL -lpthread -lpng -lstdc++fs -lasound -std=c++17
|
||||
|
||||
|
||||
Compiling with Emscripten
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
When compiling with Emscripten, you will include SDL mixer in your setting flags:
|
||||
Add: -s USE_SDL_MIXER=2
|
||||
|
||||
Your command should look something like:
|
||||
|
||||
em++ -std=c++17 -O2 -s ALLOW_MEMORY_GROWTH=1 -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_SDL_MIXER=2 -s USE_LIBPNG=1 ./YourSource.cpp -o pge.html
|
||||
|
||||
Thanks
|
||||
~~~~~~
|
||||
Slavka - OpenAL and ALSA
|
||||
MaGetzUb - XAudio
|
||||
Gorbit99 - Testing, Bug Fixes
|
||||
Cyberdroid - Testing, Bug Fixes
|
||||
Dragoneye - Testing
|
||||
Puol - Testing
|
||||
Gorbit99, Dragoneye, Puol
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2019, 2020, 2021, 2022
|
||||
Authors
|
||||
~~~~~~~
|
||||
slavka, MaGetzUb, cstd, Moros1138 & javidx9
|
||||
|
||||
(c)OneLoneCoder 2019, 2020, 2021, 2022
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Using & Installing On Microsoft Windows
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Microsoft Visual Studio
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1) Include the header file "olcSoundWaveEngine.h" from a .cpp file in your project.
|
||||
2) That's it!
|
||||
|
||||
|
||||
Code::Blocks
|
||||
~~~~~~~~~~~~
|
||||
1) Make sure your compiler toolchain is NOT the default one installed with Code::Blocks. That
|
||||
one is old, out of date, and a general mess. Instead, use MSYS2 to install a recent and
|
||||
decent GCC toolchain, then configure Code::Blocks to use it
|
||||
|
||||
Guide for installing recent GCC for Windows:
|
||||
https://www.msys2.org/
|
||||
Guide for configuring code::blocks:
|
||||
https://solarianprogrammer.com/2019/11/05/install-gcc-windows/
|
||||
https://solarianprogrammer.com/2019/11/16/install-codeblocks-gcc-windows-build-c-cpp-fortran-programs/
|
||||
|
||||
2) Include the header file "olcSoundWaveEngine.h" from a .cpp file in your project.
|
||||
3) Add these libraries to "Linker Options": user32 winmm
|
||||
4) Set this "Compiler Option": -std=c++17
|
||||
*/
|
||||
|
||||
/*
|
||||
Using & Installing On Linux
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
GNU Compiler Collection (GCC)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1) Include the header file "olcSoundWaveEngine.h" from a .cpp file in your project.
|
||||
2) Build with the following command:
|
||||
|
||||
g++ olcSoundWaveEngineExample.cpp -o olcSoundWaveEngineExample -lpulse -lpulse-simple -std=c++17
|
||||
|
||||
3) That's it!
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Using in multiple-file projects
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
If you intend to use olcSoundWaveEngine across multiple files, it's important to only have one
|
||||
instance of the implementation. This is done using the compiler preprocessor definition: OLC_SOUNDWAVE
|
||||
|
||||
This is defined typically before the header is included in teh translation unit you wish the implementation
|
||||
to be associated with. To avoid things getting messy I recommend you create a file "olcSoundWaveEngine.cpp"
|
||||
and that file includes ONLY the following code:
|
||||
|
||||
#define OLC_SOUNDWAVE
|
||||
#include "olcSoundWaveEngine.h"
|
||||
*/
|
||||
|
||||
/*
|
||||
0.01: olcPGEX_Sound.h reworked
|
||||
+Changed timekeeping to double, added accuracy fix - Thanks scripticuk
|
||||
@ -107,6 +141,15 @@
|
||||
+Loading form WAV files
|
||||
+LERPed sampling from all buffers
|
||||
+Multi-channel audio support
|
||||
0.02: +Support multi-channel wave files
|
||||
+Support for 24-bit wave files
|
||||
+Wave files are now sample rate invariant
|
||||
+Linux PulseAudio Updated
|
||||
+Linux ALSA Updated
|
||||
+WinMM Updated
|
||||
+CMake Compatibility
|
||||
=Fix wave format durations preventing playback
|
||||
=Various bug fixes
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@ -131,13 +174,14 @@
|
||||
// Compiler/System Sensitivity
|
||||
#if !defined(SOUNDWAVE_USING_WINMM) && !defined(SOUNDWAVE_USING_WASAPI) && \
|
||||
!defined(SOUNDWAVE_USING_XAUDIO) && !defined(SOUNDWAVE_USING_OPENAL) && \
|
||||
!defined(SOUNDWAVE_USING_ALSA) && !defined(SOUNDWAVE_USING_SDLMIXER) \
|
||||
!defined(SOUNDWAVE_USING_ALSA) && !defined(SOUNDWAVE_USING_SDLMIXER) && \
|
||||
!defined(SOUNDWAVE_USING_PULSE) \
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define SOUNDWAVE_USING_WINMM
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
#define SOUNDWAVE_USING_ALSA
|
||||
#define SOUNDWAVE_USING_PULSE
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
#define SOUNDWAVE_USING_SDLMIXER
|
||||
@ -167,6 +211,8 @@ namespace olc::sound
|
||||
m_nSampleSize = nSampleSize;
|
||||
m_nSamples = nSamples;
|
||||
m_nSampleRate = nSampleRate;
|
||||
m_dDuration = double(m_nSamples) / double(m_nSampleRate);
|
||||
m_dDurationInSamples = double(m_nSamples);
|
||||
|
||||
m_pRawData = std::make_unique<T[]>(m_nSamples * m_nChannels);
|
||||
}
|
||||
@ -287,6 +333,15 @@ namespace olc::sound
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // 24-bit
|
||||
{
|
||||
int32_t s = 0;
|
||||
ifs.read((char*)&s, 3);
|
||||
if (s & (1 << 23)) s |= 0xFF000000;
|
||||
*pSample = T(s) / T(std::pow(2, 23)-1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
int32_t s = 0;
|
||||
@ -722,7 +777,11 @@ namespace olc::sound::driver
|
||||
|
||||
#if defined(SOUNDWAVE_USING_SDLMIXER)
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
#else
|
||||
#include <SDL_mixer.h>
|
||||
#endif
|
||||
|
||||
namespace olc::sound::driver
|
||||
{
|
||||
@ -767,8 +826,12 @@ namespace olc::sound::driver
|
||||
class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer(unsigned int bufnum, unsigned int buflen): m_vBuffers(bufnum)
|
||||
RingBuffer()
|
||||
{ }
|
||||
|
||||
void Resize(unsigned int bufnum = 0, unsigned int buflen = 0)
|
||||
{
|
||||
m_vBuffers.resize(bufnum);
|
||||
for (auto &vBuffer : m_vBuffers)
|
||||
vBuffer.resize(buflen);
|
||||
}
|
||||
@ -835,6 +898,33 @@ namespace olc::sound::driver
|
||||
}
|
||||
#endif // SOUNDWAVE_USING_ALSA
|
||||
|
||||
#if defined(SOUNDWAVE_USING_PULSE)
|
||||
#include <pulse/simple.h>
|
||||
|
||||
namespace olc::sound::driver
|
||||
{
|
||||
class PulseAudio : public Base
|
||||
{
|
||||
public:
|
||||
PulseAudio(WaveEngine* pHost);
|
||||
~PulseAudio();
|
||||
|
||||
protected:
|
||||
bool Open(const std::string& sOutputDevice, const std::string& sInputDevice) override;
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
void Close() override;
|
||||
|
||||
private:
|
||||
void DriverLoop();
|
||||
|
||||
pa_simple *m_pPA;
|
||||
std::atomic<bool> m_bDriverLoopActive{ false };
|
||||
std::thread m_thDriverLoop;
|
||||
};
|
||||
}
|
||||
#endif // SOUNDWAVE_USING_PULSE
|
||||
|
||||
#ifdef OLC_SOUNDWAVE
|
||||
#undef OLC_SOUNDWAVE
|
||||
|
||||
@ -868,6 +958,10 @@ namespace olc::sound
|
||||
#if defined(SOUNDWAVE_USING_SDLMIXER)
|
||||
m_driver = std::make_unique<driver::SDLMixer>(this);
|
||||
#endif
|
||||
|
||||
#if defined(SOUNDWAVE_USING_PULSE)
|
||||
m_driver = std::make_unique<driver::PulseAudio>(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
WaveEngine::~WaveEngine()
|
||||
@ -938,8 +1032,8 @@ namespace olc::sound
|
||||
WaveInstance wi;
|
||||
wi.bLoop = bLoop;
|
||||
wi.pWave = pWave;
|
||||
wi.dSpeedModifier = dSpeed;
|
||||
wi.dDuration = double(pWave->file.samples()) / double(pWave->file.samplerate()) / wi.dSpeedModifier;
|
||||
wi.dSpeedModifier = dSpeed * double(pWave->file.samplerate()) / m_dSamplePerTime;
|
||||
wi.dDuration = pWave->file.duration() / dSpeed;
|
||||
wi.dInstanceTime = m_dGlobalTime;
|
||||
m_listWaves.push_back(wi);
|
||||
return std::prev(m_listWaves.end());
|
||||
@ -1006,8 +1100,8 @@ namespace olc::sound
|
||||
}
|
||||
else
|
||||
{
|
||||
// OR, sample the waveform form the correct channel
|
||||
fSample += float(wave.pWave->vChannelView[nChannel].GetSample(dTimeOffset * m_dSamplePerTime * wave.dSpeedModifier));
|
||||
// OR, sample the waveform from the correct channel
|
||||
fSample += float(wave.pWave->vChannelView[nChannel % wave.pWave->file.channels()].GetSample(dTimeOffset * m_dSamplePerTime * wave.dSpeedModifier));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1288,9 +1382,9 @@ namespace olc::sound
|
||||
random_seed += 0xe120fc15;
|
||||
uint64_t tmp;
|
||||
tmp = (uint64_t)random_seed * 0x4a39b70d;
|
||||
uint32_t m1 = (tmp >> 32) ^ tmp;
|
||||
uint32_t m1 = uint32_t(((tmp >> 32) ^ tmp) & 0xFFFFFFFF);
|
||||
tmp = (uint64_t)m1 * 0x12fad5c9;
|
||||
uint32_t m2 = (tmp >> 32) ^ tmp;
|
||||
uint32_t m2 = uint32_t(((tmp >> 32) ^ tmp) & 0xFFFFFFFF);
|
||||
return m2;
|
||||
}
|
||||
}
|
||||
@ -1490,15 +1584,15 @@ bool SDLMixer::Open(const std::string& sOutputDevice, const std::string& sInputD
|
||||
{
|
||||
case AUDIO_F32:
|
||||
case AUDIO_S32:
|
||||
bufferSize = m_pHost->GetBlockSampleCount() * 4;
|
||||
bufferSize = m_pHost->GetBlockSampleCount() * m_pHost->GetChannels() * 4;
|
||||
break;
|
||||
case AUDIO_S16:
|
||||
case AUDIO_U16:
|
||||
bufferSize = m_pHost->GetBlockSampleCount() * 2;
|
||||
bufferSize = m_pHost->GetBlockSampleCount() * m_pHost->GetChannels() * 2;
|
||||
break;
|
||||
case AUDIO_S8:
|
||||
case AUDIO_U8:
|
||||
bufferSize = m_pHost->GetBlockSampleCount() * 1;
|
||||
bufferSize = m_pHost->GetBlockSampleCount() * m_pHost->GetChannels() * 1;
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Audio format of device '" << sOutputDevice << "' is not supported" << std::endl;
|
||||
@ -1557,7 +1651,12 @@ void SDLMixer::FillChunkBuffer(const std::vector<float>& userData) const
|
||||
|
||||
void SDLMixer::SDLMixerCallback(int channel)
|
||||
{
|
||||
static std::vector<float> userData(instance->m_pHost->GetBlockSampleCount());
|
||||
static std::vector<float> userData(instance->m_pHost->GetBlockSampleCount() * instance->m_pHost->GetChannels());
|
||||
|
||||
if (channel != 0)
|
||||
{
|
||||
std::cerr << "Unexpected channel number" << std::endl;
|
||||
}
|
||||
|
||||
// Don't add another chunk if we should not keep running
|
||||
if (!instance->m_keepRunning)
|
||||
@ -1566,7 +1665,7 @@ void SDLMixer::SDLMixerCallback(int channel)
|
||||
instance->GetFullOutputBlock(userData);
|
||||
instance->FillChunkBuffer(userData);
|
||||
|
||||
if (Mix_PlayChannel(channel, &instance->audioChunk, 0) == -1)
|
||||
if (Mix_PlayChannel(0, &instance->audioChunk, 0) == -1)
|
||||
{
|
||||
std::cerr << "Error while playing Chunk" << std::endl;
|
||||
}
|
||||
@ -1610,7 +1709,7 @@ void SDLMixer::Close()
|
||||
// ALSA Driver Implementation
|
||||
namespace olc::sound::driver
|
||||
{
|
||||
ALSA::ALSA(WaveEngine* pHost) : Base(pHost), m_rBuffers(pHost->GetBlocks(), pHost->GetBlockSampleCount())
|
||||
ALSA::ALSA(WaveEngine* pHost) : Base(pHost)
|
||||
{ }
|
||||
|
||||
ALSA::~ALSA()
|
||||
@ -1644,7 +1743,7 @@ namespace olc::sound::driver
|
||||
snd_pcm_hw_params_set_format(m_pPCM, params, SND_PCM_FORMAT_FLOAT);
|
||||
snd_pcm_hw_params_set_rate(m_pPCM, params, m_pHost->GetSampleRate(), 0);
|
||||
snd_pcm_hw_params_set_channels(m_pPCM, params, m_pHost->GetChannels());
|
||||
snd_pcm_hw_params_set_period_size(m_pPCM, params, m_pHost->GetBlockSampleCount() / m_pHost->GetChannels(), 0);
|
||||
snd_pcm_hw_params_set_period_size(m_pPCM, params, m_pHost->GetBlockSampleCount(), 0);
|
||||
snd_pcm_hw_params_set_periods(m_pPCM, params, m_pHost->GetBlocks(), 0);
|
||||
|
||||
// Save these parameters
|
||||
@ -1658,10 +1757,12 @@ namespace olc::sound::driver
|
||||
bool ALSA::Start()
|
||||
{
|
||||
// Unsure if really needed, helped prevent underrun on my setup
|
||||
std::vector<float> vSilence(m_pHost->GetBlockSampleCount(), 0.0f);
|
||||
std::vector<float> vSilence(m_pHost->GetBlockSampleCount() * m_pHost->GetChannels(), 0.0f);
|
||||
snd_pcm_start(m_pPCM);
|
||||
for (unsigned int i = 0; i < m_pHost->GetBlocks(); i++)
|
||||
snd_pcm_writei(m_pPCM, vSilence.data(), m_pHost->GetBlockSampleCount() / m_pHost->GetChannels());
|
||||
snd_pcm_writei(m_pPCM, vSilence.data(), m_pHost->GetBlockSampleCount());
|
||||
|
||||
m_rBuffers.Resize(m_pHost->GetBlocks(), m_pHost->GetBlockSampleCount() * m_pHost->GetChannels());
|
||||
|
||||
snd_pcm_start(m_pPCM);
|
||||
m_bDriverLoopActive = true;
|
||||
@ -1679,7 +1780,8 @@ namespace olc::sound::driver
|
||||
if (m_thDriverLoop.joinable())
|
||||
m_thDriverLoop.join();
|
||||
|
||||
snd_pcm_drop(m_pPCM);
|
||||
if (m_pPCM != nullptr)
|
||||
snd_pcm_drop(m_pPCM);
|
||||
}
|
||||
|
||||
void ALSA::Close()
|
||||
@ -1695,7 +1797,7 @@ namespace olc::sound::driver
|
||||
|
||||
void ALSA::DriverLoop()
|
||||
{
|
||||
const uint32_t nFrames = m_pHost->GetBlockSampleCount() / m_pHost->GetChannels();
|
||||
const uint32_t nFrames = m_pHost->GetBlockSampleCount();
|
||||
|
||||
int err;
|
||||
std::vector<pollfd> vFDs;
|
||||
@ -1774,6 +1876,87 @@ namespace olc::sound::driver
|
||||
}
|
||||
} // ALSA Driver Implementation
|
||||
#endif
|
||||
#if defined(SOUNDWAVE_USING_PULSE)
|
||||
// PULSE Driver Implementation
|
||||
#include <pulse/error.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace olc::sound::driver
|
||||
{
|
||||
PulseAudio::PulseAudio(WaveEngine* pHost) : Base(pHost)
|
||||
{ }
|
||||
|
||||
PulseAudio::~PulseAudio()
|
||||
{
|
||||
Stop();
|
||||
Close();
|
||||
}
|
||||
|
||||
bool PulseAudio::Open(const std::string& sOutputDevice, const std::string& sInputDevice)
|
||||
{
|
||||
pa_sample_spec ss {
|
||||
PA_SAMPLE_FLOAT32, m_pHost->GetSampleRate(), (uint8_t)m_pHost->GetChannels()
|
||||
};
|
||||
|
||||
m_pPA = pa_simple_new(NULL, "olcSoundWaveEngine", PA_STREAM_PLAYBACK, NULL,
|
||||
"Output Stream", &ss, NULL, NULL, NULL);
|
||||
|
||||
if (m_pPA == NULL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PulseAudio::Start()
|
||||
{
|
||||
m_bDriverLoopActive = true;
|
||||
m_thDriverLoop = std::thread(&PulseAudio::DriverLoop, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PulseAudio::Stop()
|
||||
{
|
||||
// Signal the driver loop to exit
|
||||
m_bDriverLoopActive = false;
|
||||
|
||||
// Wait for driver thread to exit gracefully
|
||||
if (m_thDriverLoop.joinable())
|
||||
m_thDriverLoop.join();
|
||||
}
|
||||
|
||||
void PulseAudio::Close()
|
||||
{
|
||||
if (m_pPA != nullptr)
|
||||
{
|
||||
pa_simple_free(m_pPA);
|
||||
m_pPA = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PulseAudio::DriverLoop()
|
||||
{
|
||||
// We will be using this vector to transfer to the host for filling, with
|
||||
// user sound data (float32, -1.0 --> +1.0)
|
||||
std::vector<float> vFloatBuffer(m_pHost->GetBlockSampleCount() * m_pHost->GetChannels(), 0.0f);
|
||||
|
||||
// While the system is active, start requesting audio data
|
||||
while (m_bDriverLoopActive)
|
||||
{
|
||||
// Grab audio data from user
|
||||
GetFullOutputBlock(vFloatBuffer);
|
||||
|
||||
// Fill PulseAudio data buffer
|
||||
int error;
|
||||
if (pa_simple_write(m_pPA, vFloatBuffer.data(),
|
||||
vFloatBuffer.size() * sizeof(float), &error) < 0)
|
||||
{
|
||||
std::cerr << "Failed to feed data to PulseAudio: " << pa_strerror(error) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} // PulseAudio Driver Implementation
|
||||
#endif
|
||||
|
||||
#endif // OLC_SOUNDWAVE IMPLEMENTATION
|
||||
#endif // OLC_SOUNDWAVE_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user