diff --git a/olcPGEX_Sound.h b/olcPGEX_Sound.h index 059696d..c6f3b44 100644 --- a/olcPGEX_Sound.h +++ b/olcPGEX_Sound.h @@ -69,6 +69,16 @@ #undef min #undef max +typedef struct { + unsigned short wFormatTag; + unsigned short nChannels; + unsigned long nSamplesPerSec; + unsigned long nAvgBytesPerSec; + unsigned short nBlockAlign; + unsigned short wBitsPerSample; + unsigned short cbSize; +} OLC_WAVEFORMATEX; + namespace olc { // Container class for Advanced 2D Drawing functions @@ -84,7 +94,7 @@ namespace olc olc::rcode LoadFromFile(std::string sWavFile, olc::ResourcePack *pack = nullptr); public: - WAVEFORMATEX wavHeader; + OLC_WAVEFORMATEX wavHeader; float *fSample = nullptr; long nSamples = 0; int nChannels = 0; @@ -115,10 +125,10 @@ namespace olc static void StopAll(); static float GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep); -#ifdef WIN32 + private: - static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2); - static void AudioThread(); +#ifdef WIN32 // Windows specific sound management + static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwParam1, DWORD dwParam2); static unsigned int m_nSampleRate; static unsigned int m_nChannels; static unsigned int m_nBlockCount; @@ -126,16 +136,19 @@ namespace olc static unsigned int m_nBlockCurrent; static short* m_pBlockMemory; static WAVEHDR *m_pWaveHeaders; - static HWAVEOUT m_hwDevice; - static std::thread m_AudioThread; - static std::atomic m_bAudioThreadActive; + static HWAVEOUT m_hwDevice; static std::atomic m_nBlockFree; static std::condition_variable m_cvBlockNotZero; static std::mutex m_muxBlockNotZero; +#endif + + static void AudioThread(); + static std::thread m_AudioThread; + static std::atomic m_bAudioThreadActive; static std::atomic m_fGlobalTime; static std::function funcUserSynth; static std::function funcUserFilter; -#endif + }; } @@ -143,14 +156,11 @@ namespace olc #ifdef WIN32 #pragma comment(lib, "winmm.lib") + namespace olc { SOUND::AudioSample::AudioSample() - { - - - - } + { } SOUND::AudioSample::AudioSample(std::string sWavFile, olc::ResourcePack *pack) { @@ -506,8 +516,162 @@ namespace olc std::function SOUND::funcUserSynth = nullptr; std::function SOUND::funcUserFilter = nullptr; } + +#else // Non Windows +namespace olc +{ + SOUND::AudioSample::AudioSample() + {} + + SOUND::AudioSample::AudioSample(std::string sWavFile, olc::ResourcePack *pack) + { + LoadFromFile(sWavFile, pack); + } + + olc::rcode SOUND::AudioSample::LoadFromFile(std::string sWavFile, olc::ResourcePack *pack) + { + return olc::OK; + } + + bool SOUND::InitialiseAudio(unsigned int nSampleRate, unsigned int nChannels, unsigned int nBlocks, unsigned int nBlockSamples) + { + return true; + } + + // Stop and clean up audio system + bool SOUND::DestroyAudio() + { + return false; + } + + + // Audio thread. This loop responds to requests from the soundcard to fill 'blocks' + // with audio data. If no requests are available it goes dormant until the sound + // card is ready for more data. The block is fille by the "user" in some manner + // and then issued to the soundcard. + void SOUND::AudioThread() + { + + } + + // This vector holds all loaded sound samples in memory + std::vector vecAudioSamples; + + // This structure represents a sound that is currently playing. It only + // holds the sound ID and where this instance of it is up to for its + // current playback + + void SOUND::SetUserSynthFunction(std::function func) + { + funcUserSynth = func; + } + + void SOUND::SetUserFilterFunction(std::function func) + { + funcUserFilter = func; + } + + // Load a 16-bit WAVE file @ 44100Hz ONLY into memory. A sample ID + // number is returned if successful, otherwise -1 + unsigned int SOUND::LoadAudioSample(std::string sWavFile, olc::ResourcePack *pack) + { + olc::SOUND::AudioSample a(sWavFile, pack); + if (a.bSampleValid) + { + vecAudioSamples.push_back(a); + return vecAudioSamples.size(); + } + else + return -1; + } + + // Add sample 'id' to the mixers sounds to play list + void SOUND::PlaySample(int id, bool bLoop) + { + olc::SOUND::sCurrentlyPlayingSample a; + a.nAudioSampleID = id; + a.nSamplePosition = 0; + a.bFinished = false; + a.bFlagForStop = false; + a.bLoop = bLoop; + SOUND::listActiveSamples.push_back(a); + } + + void SOUND::StopSample(int id) + { + // Find first occurence of sample id + auto s = std::find_if(listActiveSamples.begin(), listActiveSamples.end(), [&](const olc::SOUND::sCurrentlyPlayingSample &s) { return s.nAudioSampleID == id; }); + if (s != listActiveSamples.end()) + s->bFlagForStop = true; + } + + void SOUND::StopAll() + { + for (auto &s : listActiveSamples) + { + s.bFlagForStop = true; + } + } + + float SOUND::GetMixerOutput(int nChannel, float fGlobalTime, float fTimeStep) + { + // Accumulate sample for this channel + float fMixerSample = 0.0f; + + for (auto &s : listActiveSamples) + { + if (m_bAudioThreadActive) + { + if (s.bFlagForStop) + { + s.bLoop = false; + s.bFinished = true; + } + else + { + // Calculate sample position + s.nSamplePosition += (long)((float)vecAudioSamples[s.nAudioSampleID - 1].wavHeader.nSamplesPerSec * fTimeStep); + + // If sample position is valid add to the mix + if (s.nSamplePosition < vecAudioSamples[s.nAudioSampleID - 1].nSamples) + fMixerSample += vecAudioSamples[s.nAudioSampleID - 1].fSample[(s.nSamplePosition * vecAudioSamples[s.nAudioSampleID - 1].nChannels) + nChannel]; + else + { + if (s.bLoop) + { + s.nSamplePosition = 0; + } + else + s.bFinished = true; // Else sound has completed + } + } + } + else + return 0.0f; + } + + // If sounds have completed then remove them + listActiveSamples.remove_if([](const sCurrentlyPlayingSample &s) {return s.bFinished; }); + + // The users application might be generating sound, so grab that if it exists + if (funcUserSynth != nullptr) + fMixerSample += funcUserSynth(nChannel, fGlobalTime, fTimeStep); + + // Return the sample via an optional user override to filter the sound + if (funcUserFilter != nullptr) + return funcUserFilter(nChannel, fGlobalTime, fMixerSample); + else + return fMixerSample; + } + + std::thread SOUND::m_AudioThread; + std::atomic SOUND::m_bAudioThreadActive{ false }; + std::atomic SOUND::m_fGlobalTime{ 0.0f }; + std::list SOUND::listActiveSamples; + std::function SOUND::funcUserSynth = nullptr; + std::function SOUND::funcUserFilter = nullptr; +} #endif -// Currently no Linux implementation so just go blank :( #endif \ No newline at end of file diff --git a/olcPixelGameEngine.h b/olcPixelGameEngine.h index af73f99..aa97394 100644 --- a/olcPixelGameEngine.h +++ b/olcPixelGameEngine.h @@ -2,11 +2,9 @@ olcPixelGameEngine.h +-------------------------------------------------------------+ - | OneLoneCoder Pixel Game Engine v1.11 | + | OneLoneCoder Pixel Game Engine v1.12 | | "Like the command prompt console one, but not..." - javidx9 | +-------------------------------------------------------------+ - - The Original & Best... :P What is this? ~~~~~~~~~~~~~ @@ -90,6 +88,7 @@ Twitch: https://www.twitch.tv/javidx9 GitHub: https://www.github.com/onelonecoder Homepage: https://www.onelonecoder.com + Patreon: https://www.patreon.com/javidx9 Relevant Videos ~~~~~~~~~~~~~~~ @@ -121,17 +120,20 @@ ~~~~~~ I'd like to extend thanks to Eremiell, slavka, gurkanctn, Phantim, JackOJC, KrossX, Huhlig, Dragoneye, Appa, JustinRichardsMusic, SliceNDice - & MagetzUb for advice, ideas and testing, and I'd like to extend - my appreciation to the 14K YouTube followers and 1K Discord server + Ralakus, Gorbit99, raoul & MagetzUb for advice, ideas and testing, and I'd like + to extend my appreciation to the 23K YouTube followers and 1.5K Discord server members who give me the motivation to keep going with all this :D Special thanks to those who bring gifts! GnarGnarHead.......Domina Gorbit99...........Bastion + Special thanks to my Patreons too - I wont name you on here, but I've + certainly enjoyed my tea and flapjacks :D + Author ~~~~~~ - David Barr, aka javidx9, ©OneLoneCoder 2018 + David Barr, aka javidx9, ©OneLoneCoder 2018, 2019 */ ////////////////////////////////////////////////////////////////////////////////////////// @@ -279,7 +281,8 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace ResourcePack(); ~ResourcePack(); struct sEntry : public std::streambuf { - uint32_t nID, nFileOffset, nFileSize; uint8_t* data; void _config() { this->setg((char*)data, (char*)data, (char*)(data + nFileSize)); }}; + uint32_t nID, nFileOffset, nFileSize; uint8_t* data; void _config() { this->setg((char*)data, (char*)data, (char*)(data + nFileSize)); } + }; public: olc::rcode AddToPack(std::string sFile); @@ -346,7 +349,9 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, UP, DOWN, LEFT, RIGHT, SPACE, TAB, SHIFT, CTRL, INS, DEL, HOME, END, PGUP, PGDN, - BACK, ESCAPE, ENTER, PAUSE, SCROLL, + BACK, ESCAPE, RETURN, ENTER, PAUSE, SCROLL, + NP0, NP1, NP2, NP3, NP4, NP5, NP6, NP7, NP8, NP9, + NP_MUL, NP_DIV, NP_ADD, NP_SUB, NP_DECIMAL, }; @@ -402,6 +407,7 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace // olc::Pixel::MASK = Transparent if alpha is < 255 // olc::Pixel::ALPHA = Full transparency void SetPixelMode(Pixel::Mode m); + Pixel::Mode GetPixelMode(); // Use a custom blend function void SetPixelMode(std::function pixelMode); // Change the blend factor form between 0.0f to 1.0f; @@ -647,7 +653,8 @@ namespace olc } else { - std::istream is(&(pack->GetStreamBuffer(sImageFile))); + auto streamBuffer = pack->GetStreamBuffer(sImageFile); + std::istream is(&streamBuffer); ReadData(is); } @@ -852,10 +859,10 @@ namespace olc // Create entry sEntry e; e.data = nullptr; - e.nFileSize = p; + e.nFileSize = (uint32_t)p; // Read file into memory - e.data = new uint8_t[e.nFileSize]; + e.data = new uint8_t[(uint32_t)e.nFileSize]; ifs.read((char*)e.data, e.nFileSize); ifs.close(); @@ -886,7 +893,7 @@ namespace olc std::streampos offset = ofs.tellp(); for (auto &e : mapFiles) { - e.second.nFileOffset = offset; + e.second.nFileOffset = (uint32_t)offset; ofs.write((char*)e.second.data, e.second.nFileSize); offset += e.second.nFileSize; } @@ -936,12 +943,10 @@ namespace olc // 2) Read Data for (auto &e : mapFiles) { - e.second.data = new uint8_t[e.second.nFileSize]; + e.second.data = new uint8_t[(uint32_t)e.second.nFileSize]; ifs.seekg(e.second.nFileOffset); ifs.read((char*)e.second.data, e.second.nFileSize); - //e.second.setg e.second._config(); - //e.second.pubsetbuf((char*)e.second.data, e.second.nFileSize); } ifs.close(); @@ -1542,6 +1547,11 @@ namespace olc nPixelMode = m; } + Pixel::Mode PixelGameEngine::GetPixelMode() + { + return nPixelMode; + } + void PixelGameEngine::SetPixelMode(std::function pixelMode) { funcPixelMode = pixelMode; @@ -1884,6 +1894,7 @@ namespace olc mapKeys[VK_F9] = Key::F9; mapKeys[VK_F10] = Key::F10; mapKeys[VK_F11] = Key::F11; mapKeys[VK_F12] = Key::F12; mapKeys[VK_DOWN] = Key::DOWN; mapKeys[VK_LEFT] = Key::LEFT; mapKeys[VK_RIGHT] = Key::RIGHT; mapKeys[VK_UP] = Key::UP; + mapKeys[VK_RETURN] = Key::ENTER; //mapKeys[VK_RETURN] = Key::RETURN; mapKeys[VK_BACK] = Key::BACK; mapKeys[VK_ESCAPE] = Key::ESCAPE; mapKeys[VK_RETURN] = Key::ENTER; mapKeys[VK_PAUSE] = Key::PAUSE; mapKeys[VK_SCROLL] = Key::SCROLL; mapKeys[VK_TAB] = Key::TAB; mapKeys[VK_DELETE] = Key::DEL; mapKeys[VK_HOME] = Key::HOME; @@ -1894,6 +1905,10 @@ namespace olc mapKeys[0x30] = Key::K0; mapKeys[0x31] = Key::K1; mapKeys[0x32] = Key::K2; mapKeys[0x33] = Key::K3; mapKeys[0x34] = Key::K4; mapKeys[0x35] = Key::K5; mapKeys[0x36] = Key::K6; mapKeys[0x37] = Key::K7; mapKeys[0x38] = Key::K8; mapKeys[0x39] = Key::K9; + mapKeys[VK_NUMPAD0] = Key::NP0; mapKeys[VK_NUMPAD1] = Key::NP1; mapKeys[VK_NUMPAD2] = Key::NP2; mapKeys[VK_NUMPAD3] = Key::NP3; mapKeys[VK_NUMPAD4] = Key::NP4; + mapKeys[VK_NUMPAD5] = Key::NP5; mapKeys[VK_NUMPAD6] = Key::NP6; mapKeys[VK_NUMPAD7] = Key::NP7; mapKeys[VK_NUMPAD8] = Key::NP8; mapKeys[VK_NUMPAD9] = Key::NP9; + mapKeys[VK_MULTIPLY] = Key::NP_MUL; mapKeys[VK_ADD] = Key::NP_ADD; mapKeys[VK_DIVIDE] = Key::NP_DIV; mapKeys[VK_SUBTRACT] = Key::NP_SUB; mapKeys[VK_DECIMAL] = Key::NP_DECIMAL; + return olc_hWnd; } @@ -1995,6 +2010,7 @@ namespace olc mapKeys[XK_F9] = Key::F9; mapKeys[XK_F10] = Key::F10; mapKeys[XK_F11] = Key::F11; mapKeys[XK_F12] = Key::F12; mapKeys[XK_Down] = Key::DOWN; mapKeys[XK_Left] = Key::LEFT; mapKeys[XK_Right] = Key::RIGHT; mapKeys[XK_Up] = Key::UP; + mapKeys[XK_KP_Enter] = Key::ENTER; mapKeys[XK_Return] = Key::ENTER; mapKeys[XK_BackSpace] = Key::BACK; mapKeys[XK_Escape] = Key::ESCAPE; mapKeys[XK_Linefeed] = Key::ENTER; mapKeys[XK_Pause] = Key::PAUSE; mapKeys[XK_Scroll_Lock] = Key::SCROLL; mapKeys[XK_Tab] = Key::TAB; mapKeys[XK_Delete] = Key::DEL; mapKeys[XK_Home] = Key::HOME; @@ -2005,6 +2021,10 @@ namespace olc mapKeys[XK_0] = Key::K0; mapKeys[XK_1] = Key::K1; mapKeys[XK_2] = Key::K2; mapKeys[XK_3] = Key::K3; mapKeys[XK_4] = Key::K4; mapKeys[XK_5] = Key::K5; mapKeys[XK_6] = Key::K6; mapKeys[XK_7] = Key::K7; mapKeys[XK_8] = Key::K8; mapKeys[XK_9] = Key::K9; + mapKeys[XK_KP_0] = Key::NP0; mapKeys[XK_KP_1] = Key::NP1; mapKeys[XK_KP_2] = Key::NP2; mapKeys[XK_KP_3] = Key::NP3; mapKeys[XK_KP_4] = Key::NP4; + mapKeys[XK_KP_5] = Key::NP5; mapKeys[XK_KP_6] = Key::NP6; mapKeys[XK_KP_7] = Key::NP7; mapKeys[XK_KP_8] = Key::NP8; mapKeys[XK_KP_9] = Key::NP9; + mapKeys[XK_KP_Multiply] = Key::NP_MUL; mapKeys[XK_KP_Add] = Key::NP_ADD; mapKeys[XK_KP_Divide] = Key::NP_DIV; mapKeys[XK_KP_Subtract] = Key::NP_SUB; mapKeys[XK_KP_Decimal] = Key::NP_DECIMAL; + return olc_Display; } @@ -2044,4 +2064,4 @@ namespace olc //============================================================= } -#endif +#endif \ No newline at end of file