71#ifndef OLC_PGEX_SOUND_H
72#define OLC_PGEX_SOUND_H
77#include <condition_variable>
86#if !defined(USE_ALSA) && !defined(USE_OPENAL) && !defined(USE_WINDOWS)
102#define ALSA_PCM_NEW_HW_PARAMS_API
103#include <alsa/asoundlib.h>
158 static bool InitialiseAudio(
unsigned int nSampleRate = 44100,
unsigned int nChannels = 1,
unsigned int nBlocks = 8,
unsigned int nBlockSamples = 512);
173 static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2);
174 static unsigned int m_nSampleRate;
175 static unsigned int m_nChannels;
176 static unsigned int m_nBlockCount;
177 static unsigned int m_nBlockSamples;
178 static unsigned int m_nBlockCurrent;
179 static short* m_pBlockMemory;
180 static WAVEHDR *m_pWaveHeaders;
181 static HWAVEOUT m_hwDevice;
182 static std::atomic<unsigned int> m_nBlockFree;
183 static std::condition_variable m_cvBlockNotZero;
184 static std::mutex m_muxBlockNotZero;
188 static snd_pcm_t *m_pPCM;
189 static unsigned int m_nSampleRate;
190 static unsigned int m_nChannels;
191 static unsigned int m_nBlockSamples;
192 static short* m_pBlockMemory;
196 static std::queue<ALuint> m_qAvailableBuffers;
197 static ALuint *m_pBuffers;
198 static ALuint m_nSource;
199 static ALCdevice *m_pDevice;
200 static ALCcontext *m_pContext;
201 static unsigned int m_nSampleRate;
202 static unsigned int m_nChannels;
203 static unsigned int m_nBlockCount;
204 static unsigned int m_nBlockSamples;
205 static short* m_pBlockMemory;
208 static void AudioThread();
209 static std::thread m_AudioThread;
210 static std::atomic<bool> m_bAudioThreadActive;
211 static std::atomic<float> m_fGlobalTime;
212 static std::function<float(
int,
float,
float)> funcUserSynth;
213 static std::function<float(
int,
float,
float)> funcUserFilter;
230 LoadFromFile(sWavFile, pack);
235 auto ReadWave = [&](std::istream &is)
238 is.read(dump,
sizeof(
char) * 4);
239 if (strncmp(dump,
"RIFF", 4) != 0)
return olc::FAIL;
240 is.read(dump,
sizeof(
char) * 4);
241 is.read(dump,
sizeof(
char) * 4);
242 if (strncmp(dump,
"WAVE", 4) != 0)
return olc::FAIL;
245 is.read(dump,
sizeof(
char) * 4);
246 unsigned int nHeaderSize = 0;
247 is.read((
char*)&nHeaderSize,
sizeof(
unsigned int));
248 is.read((
char*)&wavHeader, nHeaderSize);
253 if (wavHeader.wBitsPerSample != 16 || wavHeader.nSamplesPerSec != 44100)
257 uint32_t nChunksize = 0;
258 is.read(dump,
sizeof(
char) * 4);
259 is.read((
char*)&nChunksize,
sizeof(uint32_t));
260 while (strncmp(dump,
"data", 4) != 0)
264 is.seekg(nChunksize, std::istream::cur);
265 is.read(dump,
sizeof(
char) * 4);
266 is.read((
char*)&nChunksize,
sizeof(uint32_t));
270 nSamples = nChunksize / (wavHeader.nChannels * (wavHeader.wBitsPerSample >> 3));
271 nChannels = wavHeader.nChannels;
274 fSample =
new float[nSamples * nChannels];
275 float *pSample = fSample;
278 for (
long i = 0; i < nSamples; i++)
280 for (
int c = 0; c < nChannels; c++)
285 is.read((
char*)&s,
sizeof(short));
287 *pSample = (float)s / (
float)(SHRT_MAX);
301 std::istream is(&rb);
307 std::ifstream ifs(sWavFile, std::ifstream::binary);
310 return ReadWave(ifs);
318 std::vector<olc::SOUND::AudioSample> vecAudioSamples;
326 funcUserSynth = func;
331 funcUserFilter = func;
342 vecAudioSamples.push_back(a);
343 return (
unsigned int)vecAudioSamples.size();
366 s->bFlagForStop =
true;
373 s.bFlagForStop =
true;
380 float fMixerSample = 0.0f;
384 if (m_bAudioThreadActive)
394 s.nSamplePosition += roundf((
float)vecAudioSamples[s.nAudioSampleID - 1].wavHeader.nSamplesPerSec * fTimeStep);
397 if (s.nSamplePosition < vecAudioSamples[s.nAudioSampleID - 1].nSamples)
398 fMixerSample += vecAudioSamples[s.nAudioSampleID - 1].fSample[(s.nSamplePosition * vecAudioSamples[s.nAudioSampleID - 1].nChannels) + nChannel];
403 s.nSamplePosition = 0;
415 listActiveSamples.remove_if([](
const sCurrentlyPlayingSample &s) {
return s.bFinished; });
418 if (funcUserSynth !=
nullptr)
419 fMixerSample += funcUserSynth(nChannel, fGlobalTime, fTimeStep);
422 if (funcUserFilter !=
nullptr)
423 return funcUserFilter(nChannel, fGlobalTime, fMixerSample);
428 std::thread SOUND::m_AudioThread;
429 std::atomic<bool> SOUND::m_bAudioThreadActive{
false };
430 std::atomic<float> SOUND::m_fGlobalTime{ 0.0f };
432 std::function<float(
int,
float,
float)> SOUND::funcUserSynth =
nullptr;
433 std::function<float(
int,
float,
float)> SOUND::funcUserFilter =
nullptr;
438#pragma comment(lib, "winmm.lib")
442 bool SOUND::InitialiseAudio(
unsigned int nSampleRate,
unsigned int nChannels,
unsigned int nBlocks,
unsigned int nBlockSamples)
445 m_bAudioThreadActive =
false;
446 m_nSampleRate = nSampleRate;
447 m_nChannels = nChannels;
448 m_nBlockCount = nBlocks;
449 m_nBlockSamples = nBlockSamples;
450 m_nBlockFree = m_nBlockCount;
452 m_pBlockMemory =
nullptr;
453 m_pWaveHeaders =
nullptr;
456 WAVEFORMATEX waveFormat;
457 waveFormat.wFormatTag = WAVE_FORMAT_PCM;
458 waveFormat.nSamplesPerSec = m_nSampleRate;
459 waveFormat.wBitsPerSample =
sizeof(short) * 8;
460 waveFormat.nChannels = m_nChannels;
461 waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
462 waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
463 waveFormat.cbSize = 0;
468 if (waveOutOpen(&m_hwDevice, WAVE_MAPPER, &waveFormat, (DWORD_PTR)SOUND::waveOutProc, (DWORD_PTR)0, CALLBACK_FUNCTION) != S_OK)
472 m_pBlockMemory =
new short[m_nBlockCount * m_nBlockSamples];
473 if (m_pBlockMemory ==
nullptr)
475 ZeroMemory(m_pBlockMemory,
sizeof(
short) * m_nBlockCount * m_nBlockSamples);
477 m_pWaveHeaders =
new WAVEHDR[m_nBlockCount];
478 if (m_pWaveHeaders ==
nullptr)
480 ZeroMemory(m_pWaveHeaders,
sizeof(WAVEHDR) * m_nBlockCount);
483 for (
unsigned int n = 0; n < m_nBlockCount; n++)
485 m_pWaveHeaders[n].dwBufferLength = m_nBlockSamples *
sizeof(short);
486 m_pWaveHeaders[n].lpData = (LPSTR)(m_pBlockMemory + (n * m_nBlockSamples));
489 m_bAudioThreadActive =
true;
490 m_AudioThread = std::thread(&SOUND::AudioThread);
493 std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
494 m_cvBlockNotZero.notify_one();
501 m_bAudioThreadActive =
false;
502 if(m_AudioThread.joinable())
503 m_AudioThread.join();
508 void CALLBACK SOUND::waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2)
510 if (uMsg != WOM_DONE)
return;
512 std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
513 m_cvBlockNotZero.notify_one();
520 void SOUND::AudioThread()
522 m_fGlobalTime = 0.0f;
523 static float fTimeStep = 1.0f / (float)m_nSampleRate;
526 short nMaxSample = (short)pow(2, (
sizeof(
short) * 8) - 1) - 1;
527 float fMaxSample = (float)nMaxSample;
528 short nPreviousSample = 0;
530 auto tp1 = std::chrono::system_clock::now();
531 auto tp2 = std::chrono::system_clock::now();
533 while (m_bAudioThreadActive)
536 if (m_nBlockFree == 0)
538 std::unique_lock<std::mutex> lm(m_muxBlockNotZero);
539 while (m_nBlockFree == 0)
540 m_cvBlockNotZero.wait(lm);
547 if (m_pWaveHeaders[m_nBlockCurrent].dwFlags & WHDR_PREPARED)
548 waveOutUnprepareHeader(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent],
sizeof(WAVEHDR));
550 short nNewSample = 0;
551 int nCurrentBlock = m_nBlockCurrent * m_nBlockSamples;
553 auto clip = [](
float fSample,
float fMax)
556 return fmin(fSample, fMax);
558 return fmax(fSample, -fMax);
561 tp2 = std::chrono::system_clock::now();
562 std::chrono::duration<float> elapsedTime = tp2 - tp1;
566 float fElapsedTime = elapsedTime.count();
568 for (
unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
571 for (
unsigned int c = 0; c < m_nChannels; c++)
573 nNewSample = (short)(clip(
GetMixerOutput(c, m_fGlobalTime + fTimeStep * (
float)n, fTimeStep), 1.0) * fMaxSample);
574 m_pBlockMemory[nCurrentBlock + n + c] = nNewSample;
575 nPreviousSample = nNewSample;
579 m_fGlobalTime = m_fGlobalTime + fTimeStep * (float)m_nBlockSamples;
582 waveOutPrepareHeader(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent],
sizeof(WAVEHDR));
583 waveOutWrite(m_hwDevice, &m_pWaveHeaders[m_nBlockCurrent],
sizeof(WAVEHDR));
585 m_nBlockCurrent %= m_nBlockCount;
589 unsigned int SOUND::m_nSampleRate = 0;
590 unsigned int SOUND::m_nChannels = 0;
591 unsigned int SOUND::m_nBlockCount = 0;
592 unsigned int SOUND::m_nBlockSamples = 0;
593 unsigned int SOUND::m_nBlockCurrent = 0;
594 short* SOUND::m_pBlockMemory =
nullptr;
595 WAVEHDR *SOUND::m_pWaveHeaders =
nullptr;
596 HWAVEOUT SOUND::m_hwDevice;
597 std::atomic<unsigned int> SOUND::m_nBlockFree = 0;
598 std::condition_variable SOUND::m_cvBlockNotZero;
599 std::mutex SOUND::m_muxBlockNotZero;
602#elif defined(USE_ALSA)
606 bool SOUND::InitialiseAudio(
unsigned int nSampleRate,
unsigned int nChannels,
unsigned int nBlocks,
unsigned int nBlockSamples)
609 m_bAudioThreadActive =
false;
610 m_nSampleRate = nSampleRate;
611 m_nChannels = nChannels;
612 m_nBlockSamples = nBlockSamples;
613 m_pBlockMemory =
nullptr;
616 int rc = snd_pcm_open(&m_pPCM,
"default", SND_PCM_STREAM_PLAYBACK, 0);
622 snd_pcm_hw_params_t *params;
623 snd_pcm_hw_params_alloca(¶ms);
624 snd_pcm_hw_params_any(m_pPCM, params);
627 snd_pcm_hw_params_set_access(m_pPCM, params, SND_PCM_ACCESS_RW_INTERLEAVED);
628 snd_pcm_hw_params_set_format(m_pPCM, params, SND_PCM_FORMAT_S16_LE);
629 snd_pcm_hw_params_set_rate(m_pPCM, params, m_nSampleRate, 0);
630 snd_pcm_hw_params_set_channels(m_pPCM, params, m_nChannels);
631 snd_pcm_hw_params_set_period_size(m_pPCM, params, m_nBlockSamples, 0);
632 snd_pcm_hw_params_set_periods(m_pPCM, params, nBlocks, 0);
635 rc = snd_pcm_hw_params(m_pPCM, params);
642 m_pBlockMemory =
new short[m_nBlockSamples];
643 if (m_pBlockMemory ==
nullptr)
645 std::fill(m_pBlockMemory, m_pBlockMemory + m_nBlockSamples, 0);
648 snd_pcm_start(m_pPCM);
649 for (
unsigned int i = 0; i < nBlocks; i++)
650 rc = snd_pcm_writei(m_pPCM, m_pBlockMemory, 512);
652 snd_pcm_start(m_pPCM);
653 m_bAudioThreadActive =
true;
654 m_AudioThread = std::thread(&SOUND::AudioThread);
662 m_bAudioThreadActive =
false;
663 if(m_AudioThread.joinable())
664 m_AudioThread.join();
665 snd_pcm_drain(m_pPCM);
666 snd_pcm_close(m_pPCM);
675 void SOUND::AudioThread()
677 m_fGlobalTime = 0.0f;
678 static float fTimeStep = 1.0f / (float)m_nSampleRate;
681 short nMaxSample = (short)pow(2, (
sizeof(
short) * 8) - 1) - 1;
682 float fMaxSample = (float)nMaxSample;
683 short nPreviousSample = 0;
685 while (m_bAudioThreadActive)
687 short nNewSample = 0;
689 auto clip = [](
float fSample,
float fMax)
692 return fmin(fSample, fMax);
694 return fmax(fSample, -fMax);
697 for (
unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
700 for (
unsigned int c = 0; c < m_nChannels; c++)
702 nNewSample = (short)(
GetMixerOutput(c, m_fGlobalTime + fTimeStep * (
float)n, fTimeStep), 1.0) * fMaxSample;
703 m_pBlockMemory[n + c] = nNewSample;
704 nPreviousSample = nNewSample;
708 m_fGlobalTime = m_fGlobalTime + fTimeStep * (float)m_nBlockSamples;
711 snd_pcm_uframes_t nLeft = m_nBlockSamples;
712 short *pBlockPos = m_pBlockMemory;
715 int rc = snd_pcm_writei(m_pPCM, pBlockPos, nLeft);
718 pBlockPos += rc * m_nChannels;
721 if (rc == -EAGAIN)
continue;
723 snd_pcm_prepare(m_pPCM);
728 snd_pcm_t* SOUND::m_pPCM =
nullptr;
729 unsigned int SOUND::m_nSampleRate = 0;
730 unsigned int SOUND::m_nChannels = 0;
731 unsigned int SOUND::m_nBlockSamples = 0;
732 short* SOUND::m_pBlockMemory =
nullptr;
735#elif defined(USE_OPENAL)
739 bool SOUND::InitialiseAudio(
unsigned int nSampleRate,
unsigned int nChannels,
unsigned int nBlocks,
unsigned int nBlockSamples)
742 m_bAudioThreadActive =
false;
743 m_nSampleRate = nSampleRate;
744 m_nChannels = nChannels;
745 m_nBlockCount = nBlocks;
746 m_nBlockSamples = nBlockSamples;
747 m_pBlockMemory =
nullptr;
750 m_pDevice = alcOpenDevice(NULL);
753 m_pContext = alcCreateContext(m_pDevice, NULL);
754 alcMakeContextCurrent(m_pContext);
761 m_pBuffers =
new ALuint[m_nBlockCount];
762 alGenBuffers(m_nBlockCount, m_pBuffers);
763 alGenSources(1, &m_nSource);
765 for (
unsigned int i = 0; i < m_nBlockCount; i++)
766 m_qAvailableBuffers.push(m_pBuffers[i]);
771 m_pBlockMemory =
new short[m_nBlockSamples];
772 if (m_pBlockMemory ==
nullptr)
774 std::fill(m_pBlockMemory, m_pBlockMemory + m_nBlockSamples, 0);
776 m_bAudioThreadActive =
true;
777 m_AudioThread = std::thread(&SOUND::AudioThread);
784 m_bAudioThreadActive =
false;
785 if(m_AudioThread.joinable())
786 m_AudioThread.join();
788 alDeleteBuffers(m_nBlockCount, m_pBuffers);
790 alDeleteSources(1, &m_nSource);
792 alcMakeContextCurrent(NULL);
793 alcDestroyContext(m_pContext);
794 alcCloseDevice(m_pDevice);
803 void SOUND::AudioThread()
805 m_fGlobalTime = 0.0f;
806 static float fTimeStep = 1.0f / (float)m_nSampleRate;
809 short nMaxSample = (short)pow(2, (
sizeof(
short) * 8) - 1) - 1;
810 float fMaxSample = (float)nMaxSample;
811 short nPreviousSample = 0;
813 std::vector<ALuint> vProcessed;
815 while (m_bAudioThreadActive)
817 ALint nState, nProcessed;
818 alGetSourcei(m_nSource, AL_SOURCE_STATE, &nState);
819 alGetSourcei(m_nSource, AL_BUFFERS_PROCESSED, &nProcessed);
822 vProcessed.resize(nProcessed);
823 alSourceUnqueueBuffers(m_nSource, nProcessed, vProcessed.data());
824 for (ALint nBuf : vProcessed) m_qAvailableBuffers.push(nBuf);
827 if (m_qAvailableBuffers.empty())
continue;
829 short nNewSample = 0;
831 auto clip = [](
float fSample,
float fMax)
834 return fmin(fSample, fMax);
836 return fmax(fSample, -fMax);
839 for (
unsigned int n = 0; n < m_nBlockSamples; n += m_nChannels)
842 for (
unsigned int c = 0; c < m_nChannels; c++)
844 nNewSample = (short)(clip(
GetMixerOutput(c, m_fGlobalTime, fTimeStep), 1.0) * fMaxSample);
845 m_pBlockMemory[n + c] = nNewSample;
846 nPreviousSample = nNewSample;
849 m_fGlobalTime = m_fGlobalTime + fTimeStep;
854 m_qAvailableBuffers.front(),
855 m_nChannels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
861 alSourceQueueBuffers(m_nSource, 1, &m_qAvailableBuffers.front());
863 m_qAvailableBuffers.pop();
866 if (nState != AL_PLAYING)
867 alSourcePlay(m_nSource);
871 std::queue<ALuint> SOUND::m_qAvailableBuffers;
872 ALuint *SOUND::m_pBuffers =
nullptr;
873 ALuint SOUND::m_nSource = 0;
874 ALCdevice *SOUND::m_pDevice =
nullptr;
875 ALCcontext *SOUND::m_pContext =
nullptr;
876 unsigned int SOUND::m_nSampleRate = 0;
877 unsigned int SOUND::m_nChannels = 0;
878 unsigned int SOUND::m_nBlockCount = 0;
879 unsigned int SOUND::m_nBlockSamples = 0;
880 short* SOUND::m_pBlockMemory =
nullptr;
887 bool SOUND::InitialiseAudio(
unsigned int nSampleRate,
unsigned int nChannels,
unsigned int nBlocks,
unsigned int nBlockSamples)
903 void SOUND::AudioThread()
Definition olcPixelGameEngine.h:1615
Definition olcPixelGameEngine.h:1015
ResourceBuffer GetFileBuffer(const std::string &sFile)
Definition olcPGEX_Sound.h:132
AudioSample(std::string sWavFile, olc::ResourcePack *pack=nullptr)
long nSamples
Definition olcPGEX_Sound.h:141
bool bSampleValid
Definition olcPGEX_Sound.h:143
olc::rcode LoadFromFile(std::string sWavFile, olc::ResourcePack *pack=nullptr)
float * fSample
Definition olcPGEX_Sound.h:140
int nChannels
Definition olcPGEX_Sound.h:142
OLC_WAVEFORMATEX wavHeader
Definition olcPGEX_Sound.h:139
Definition olcPGEX_Sound.h:128
static void PlaySample(int id, bool bLoop=false)
static bool InitialiseAudio(unsigned int nSampleRate=44100, unsigned int nChannels=1, unsigned int nBlocks=8, unsigned int nBlockSamples=512)
static bool DestroyAudio()
static void SetUserFilterFunction(std::function< float(int, float, float)> func)
static int LoadAudioSample(std::string sWavFile, olc::ResourcePack *pack=nullptr)
static std::list< sCurrentlyPlayingSample > listActiveSamples
Definition olcPGEX_Sound.h:155
static void StopSample(int id)
static void SetUserSynthFunction(std::function< float(int, float, float)> func)
static float GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep)
Definition olcPixelGameEngine.h:593
rcode
Definition olcPixelGameEngine.h:917
@ OK
Definition olcPixelGameEngine.h:917
@ FAIL
Definition olcPixelGameEngine.h:917
Definition olcPixelGameEngine.h:1009
Definition olcPGEX_Sound.h:147
bool bFlagForStop
Definition olcPGEX_Sound.h:152
bool bLoop
Definition olcPGEX_Sound.h:151
bool bFinished
Definition olcPGEX_Sound.h:150
long nSamplePosition
Definition olcPGEX_Sound.h:149
int nAudioSampleID
Definition olcPGEX_Sound.h:148