parent
3d6517e02f
commit
5ba83cad95
@ -1,2 +0,0 @@ |
|||||||
*** Minifantasy - Tiny Overworld v1.0 *** - Designed by Krishna Palacio |
|
||||||
ERA OF FANTASY - GRASSLANDS - by @Namatnieks <https://aamatniekss.itch.io/grasslands-era-of-fantasy-pixelart-asset-pack> |
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,477 @@ |
|||||||
|
#pragma once |
||||||
|
/*
|
||||||
|
olcPGEX_MiniAudio.h |
||||||
|
|
||||||
|
+-------------------------------------------------------------+ |
||||||
|
| OneLoneCoder Pixel Game Engine Extension | |
||||||
|
| MiniAudio v1.5 | |
||||||
|
+-------------------------------------------------------------+ |
||||||
|
|
||||||
|
NOTE: UNDER ACTIVE DEVELOPMENT - THERE MAY BE BUGS/GLITCHES |
||||||
|
|
||||||
|
What is this? |
||||||
|
~~~~~~~~~~~~~ |
||||||
|
This extension abstracts the very robust and powerful miniaudio |
||||||
|
library. It provides simple loading and playback of WAV and MP3 |
||||||
|
files. Because it's built on top of miniaudio, it requires next |
||||||
|
to no addictional build configurations in order to be built |
||||||
|
for cross-platform. |
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2023 Moros Smith <moros1138@gmail.com> |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, |
||||||
|
are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce the above |
||||||
|
copyright notice. This list of conditions and the following disclaimer must be |
||||||
|
reproduced in the documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may |
||||||
|
be used to endorse or promote products derived from this software without specific |
||||||
|
prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||||
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||||
|
SUCH DAMAGE. |
||||||
|
|
||||||
|
Links |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/@Moros1138
|
||||||
|
GitHub: https://www.github.com/Moros1138
|
||||||
|
Homepage: https://www.moros1138.com
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
#include <exception> |
||||||
|
|
||||||
|
#ifdef OLC_PGEX_MINIAUDIO |
||||||
|
#define MINIAUDIO_IMPLEMENTATION |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "miniaudio.h" |
||||||
|
|
||||||
|
namespace olc |
||||||
|
{ |
||||||
|
class MiniAudio : public olc::PGEX |
||||||
|
{ |
||||||
|
public: |
||||||
|
std::string name = "olcPGEX_MiniAudio v1.5"; |
||||||
|
|
||||||
|
public: |
||||||
|
MiniAudio(); |
||||||
|
~MiniAudio(); |
||||||
|
virtual bool OnBeforeUserUpdate(float& fElapsedTime) override; |
||||||
|
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); |
||||||
|
static bool backgroundPlay; |
||||||
|
|
||||||
|
public: // CONFIGURATION
|
||||||
|
// set whether audio will continue playing when the app has lost focus
|
||||||
|
void SetBackgroundPlay(bool state); |
||||||
|
|
||||||
|
public: // LOADING ROUTINES
|
||||||
|
const int LoadSound(const std::string& path); |
||||||
|
void UnloadSound(const int id); |
||||||
|
|
||||||
|
public: // PLAYBACK CONTROLS
|
||||||
|
// plays a sample, can be set to loop
|
||||||
|
void Play(const int id, const bool loop = false); |
||||||
|
// plays a sound file, as a one off, and automatically unloads it
|
||||||
|
void Play(const std::string& path); |
||||||
|
// stops a sample, rewinds to beginning
|
||||||
|
void Stop(const int id); |
||||||
|
// pauses a sample, does not change position
|
||||||
|
void Pause(const int id); |
||||||
|
// toggle between play and pause
|
||||||
|
void Toggle(const int id, bool rewind = false); |
||||||
|
|
||||||
|
public: // SEEKING CONTROLS
|
||||||
|
// seek to the provided position in the sound, by milliseconds
|
||||||
|
void Seek(const int id, const unsigned long long milliseconds); |
||||||
|
// seek to the provided position in the sound, by float 0.f is beginning, 1.0f is end
|
||||||
|
void Seek(const int id, const float& location); |
||||||
|
// seek forward from current position by the provided time
|
||||||
|
void Forward(const int id, const unsigned long long milliseconds); |
||||||
|
// seek forward from current position by the provided time
|
||||||
|
void Rewind(const int id, const unsigned long long milliseconds); |
||||||
|
|
||||||
|
public: // MISC CONTROLS
|
||||||
|
// set volume of a sound, 0.0f is mute, 1.0f is full
|
||||||
|
void SetVolume(const int id, const float& volume); |
||||||
|
// set pan of a sound, -1.0f is left, 1.0f is right, 0.0f is center
|
||||||
|
void SetPan(const int id, const float& pan); |
||||||
|
// set pitch of a sound, 1.0f is normal
|
||||||
|
void SetPitch(const int id, const float& pitch); |
||||||
|
|
||||||
|
public: // MISC INFORMATION
|
||||||
|
// determine if a sound is playing
|
||||||
|
bool IsPlaying(const int id); |
||||||
|
// gets the current position in the sound, in milliseconds
|
||||||
|
unsigned long long GetCursorMilliseconds(const int id); |
||||||
|
// gets the current position in the sound, as a float between 0.0f and 1.0f
|
||||||
|
float GetCursorFloat(const int id); |
||||||
|
|
||||||
|
public: // ADVANCED FEATURES for those who want to use more of miniaudio
|
||||||
|
// gets the currently loaded persistent sounds
|
||||||
|
const std::vector<ma_sound*>& GetSounds() const; |
||||||
|
// gets the currently loaded one-off sounds
|
||||||
|
const std::vector<ma_sound*>& GetOneOffSounds() const; |
||||||
|
// gets a pointer to the ma_device
|
||||||
|
ma_device* GetDevice(); |
||||||
|
// gets a pointer to the ma_engine
|
||||||
|
ma_engine* GetEngine(); |
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/*
|
||||||
|
Soooo, i'm not going to spend a whole lot of time |
||||||
|
documenting miniaudio features, if you want to |
||||||
|
know more I invite you to visit their very very |
||||||
|
nicely documented webiste at: |
||||||
|
|
||||||
|
https://miniaud.io/docs/manual/index.html
|
||||||
|
*/ |
||||||
|
|
||||||
|
ma_device device; |
||||||
|
ma_engine engine; |
||||||
|
ma_resource_manager resourceManager; |
||||||
|
|
||||||
|
// sample rate for the device and engine
|
||||||
|
int sampleRate; |
||||||
|
// this is where the sounds are kept
|
||||||
|
std::vector<ma_sound*> vecSounds; |
||||||
|
std::vector<ma_sound*> vecOneOffSounds; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* EXCEPTIONS, long story short. I needed to be able |
||||||
|
* to construct the PGEX in a state where it could |
||||||
|
* fail at runtime. If you have a better way of |
||||||
|
* accomplishing the PGEX pattern without using |
||||||
|
* exceptions, I'm open to suggestions! |
||||||
|
*/ |
||||||
|
struct MiniAudioDeviceException : public std::exception |
||||||
|
{ |
||||||
|
const char* what() const throw() |
||||||
|
{ |
||||||
|
return "Failed to initialize a device."; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct MiniAudioResourceManagerException : public std::exception |
||||||
|
{ |
||||||
|
const char* what() const throw() |
||||||
|
{ |
||||||
|
return "Failed to initialize the resource manager."; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct MiniAudioEngineException : public std::exception |
||||||
|
{ |
||||||
|
const char* what() const throw() |
||||||
|
{ |
||||||
|
return "Failed to initialize the audio engine."; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct MiniAudioSoundException : public std::exception |
||||||
|
{ |
||||||
|
const char* what() const throw() |
||||||
|
{ |
||||||
|
return "Failed to initialize a sound."; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#ifdef OLC_PGEX_MINIAUDIO |
||||||
|
#undef OLC_PGEX_MINIAUDIO |
||||||
|
|
||||||
|
namespace olc |
||||||
|
{ |
||||||
|
bool MiniAudio::backgroundPlay = false; |
||||||
|
|
||||||
|
MiniAudio::MiniAudio() : olc::PGEX(true) |
||||||
|
{ |
||||||
|
sampleRate = 48000; |
||||||
|
|
||||||
|
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback); |
||||||
|
deviceConfig.playback.format = ma_format_f32; |
||||||
|
deviceConfig.playback.channels = 2; |
||||||
|
deviceConfig.sampleRate = sampleRate; |
||||||
|
deviceConfig.dataCallback = MiniAudio::data_callback; |
||||||
|
deviceConfig.pUserData = &engine; |
||||||
|
|
||||||
|
if(ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) |
||||||
|
throw MiniAudioDeviceException(); |
||||||
|
|
||||||
|
ma_resource_manager_config resourceManagerConfig = ma_resource_manager_config_init(); |
||||||
|
resourceManagerConfig.decodedFormat = ma_format_f32; |
||||||
|
resourceManagerConfig.decodedChannels = 0; |
||||||
|
resourceManagerConfig.decodedSampleRate = sampleRate; |
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__ |
||||||
|
resourceManagerConfig.jobThreadCount = 0;
|
||||||
|
resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; |
||||||
|
resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; |
||||||
|
#endif |
||||||
|
|
||||||
|
if(ma_resource_manager_init(&resourceManagerConfig, &resourceManager) != MA_SUCCESS) |
||||||
|
throw MiniAudioResourceManagerException(); |
||||||
|
|
||||||
|
ma_engine_config engineConfig = ma_engine_config_init(); |
||||||
|
engineConfig.pDevice = &device; |
||||||
|
engineConfig.pResourceManager = &resourceManager;
|
||||||
|
|
||||||
|
if(ma_engine_init(&engineConfig, &engine) != MA_SUCCESS) |
||||||
|
throw MiniAudioEngineException(); |
||||||
|
|
||||||
|
MiniAudio::backgroundPlay = false;
|
||||||
|
} |
||||||
|
|
||||||
|
MiniAudio::~MiniAudio() |
||||||
|
{ |
||||||
|
for(auto sound : vecSounds) |
||||||
|
{ |
||||||
|
if(sound != nullptr) |
||||||
|
{ |
||||||
|
ma_sound_uninit(sound); |
||||||
|
delete sound; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ma_resource_manager_uninit(&resourceManager); |
||||||
|
|
||||||
|
ma_engine_uninit(&engine);
|
||||||
|
} |
||||||
|
|
||||||
|
bool MiniAudio::OnBeforeUserUpdate(float& fElapsedTime) |
||||||
|
{ |
||||||
|
#ifdef __EMSCRIPTEN__ |
||||||
|
ma_resource_manager_process_next_job(&resourceManager); |
||||||
|
#endif |
||||||
|
|
||||||
|
for(int i = 0; i < vecOneOffSounds.size(); i++) |
||||||
|
{ |
||||||
|
if(!ma_sound_is_playing(vecOneOffSounds.at(i))) |
||||||
|
{ |
||||||
|
ma_sound_uninit(vecOneOffSounds.at(i)); |
||||||
|
vecOneOffSounds.erase(vecOneOffSounds.begin() + i); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) |
||||||
|
{ |
||||||
|
if(!MiniAudio::backgroundPlay && !pge->IsFocused()) |
||||||
|
return; |
||||||
|
|
||||||
|
ma_engine_read_pcm_frames((ma_engine*)(pDevice->pUserData), pOutput, frameCount, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::SetBackgroundPlay(bool state) |
||||||
|
{ |
||||||
|
MiniAudio::backgroundPlay = state; |
||||||
|
} |
||||||
|
|
||||||
|
const int MiniAudio::LoadSound(const std::string& path) |
||||||
|
{ |
||||||
|
// create the sound
|
||||||
|
ma_sound* sound = new ma_sound(); |
||||||
|
|
||||||
|
// load it from the file and decode it
|
||||||
|
if(ma_sound_init_from_file(&engine, path.c_str(), MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, NULL, NULL, sound) != MA_SUCCESS) |
||||||
|
throw MiniAudioSoundException(); |
||||||
|
|
||||||
|
// attempt to re-use an empty slot
|
||||||
|
for(int i = 0; i < vecSounds.size(); i++) |
||||||
|
{ |
||||||
|
if(vecSounds.at(i) == nullptr) |
||||||
|
{ |
||||||
|
vecSounds.at(i) = sound; |
||||||
|
return i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// no empty slots, make more room!
|
||||||
|
const int id = vecSounds.size(); |
||||||
|
vecSounds.push_back(sound); |
||||||
|
|
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::UnloadSound(const int id) |
||||||
|
{ |
||||||
|
ma_sound_uninit(vecSounds.at(id)); |
||||||
|
delete vecSounds.at(id); |
||||||
|
vecSounds.at(id) = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Play(const int id, const bool loop) |
||||||
|
{ |
||||||
|
if(ma_sound_is_playing(vecSounds.at(id))) |
||||||
|
{ |
||||||
|
ma_sound_seek_to_pcm_frame(vecSounds.at(id), 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
ma_sound_set_looping(vecSounds.at(id), loop); |
||||||
|
ma_sound_start(vecSounds.at(id)); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Play(const std::string& path) |
||||||
|
{ |
||||||
|
// create the sound
|
||||||
|
ma_sound* sound = new ma_sound(); |
||||||
|
|
||||||
|
// load it from the file and decode it
|
||||||
|
if(ma_sound_init_from_file(&engine, path.c_str(), MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, NULL, NULL, sound) != MA_SUCCESS) |
||||||
|
throw MiniAudioSoundException(); |
||||||
|
|
||||||
|
ma_sound_start(sound); |
||||||
|
vecOneOffSounds.push_back(sound); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Stop(const int id) |
||||||
|
{ |
||||||
|
ma_sound_seek_to_pcm_frame(vecSounds.at(id), 0); |
||||||
|
ma_sound_stop(vecSounds.at(id)); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Pause(const int id) |
||||||
|
{ |
||||||
|
auto it = vecSounds.begin() + id; |
||||||
|
ma_sound_stop(vecSounds.at(id)); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Toggle(const int id, bool rewind) |
||||||
|
{ |
||||||
|
if(ma_sound_is_playing(vecSounds.at(id))) |
||||||
|
{ |
||||||
|
ma_sound_stop(vecSounds.at(id)); |
||||||
|
|
||||||
|
if(rewind) |
||||||
|
ma_sound_seek_to_pcm_frame(vecSounds.at(id), 0); |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
ma_sound_start(vecSounds.at(id)); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Seek(const int id, const unsigned long long milliseconds) |
||||||
|
{ |
||||||
|
unsigned long long frame = (milliseconds * engine.sampleRate) / 1000; |
||||||
|
|
||||||
|
ma_sound_seek_to_pcm_frame(vecSounds.at(id), frame); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Seek(const int id, const float& location) |
||||||
|
{ |
||||||
|
unsigned long long length; |
||||||
|
ma_sound_get_length_in_pcm_frames(vecSounds.at(id), &length); |
||||||
|
|
||||||
|
unsigned long long frame = length * location; |
||||||
|
|
||||||
|
ma_sound_seek_to_pcm_frame(vecSounds.at(id), frame); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Forward(const int id, const unsigned long long milliseconds) |
||||||
|
{ |
||||||
|
unsigned long long cursor; |
||||||
|
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||||
|
|
||||||
|
unsigned long long frame = (milliseconds * engine.sampleRate) / 1000; |
||||||
|
ma_sound_seek_to_pcm_frame(vecSounds.at(id), cursor + frame); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::Rewind(const int id, const unsigned long long milliseconds) |
||||||
|
{ |
||||||
|
unsigned long long cursor; |
||||||
|
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||||
|
|
||||||
|
unsigned long long frame = (milliseconds * engine.sampleRate) / 1000; |
||||||
|
ma_sound_seek_to_pcm_frame(vecSounds.at(id), cursor - frame); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::SetVolume(const int id, const float& volume) |
||||||
|
{ |
||||||
|
ma_sound_set_volume(vecSounds.at(id), volume); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::SetPan(const int id, const float& pan) |
||||||
|
{ |
||||||
|
ma_sound_set_pan(vecSounds.at(id), pan); |
||||||
|
} |
||||||
|
|
||||||
|
void MiniAudio::SetPitch(const int id, const float& pitch) |
||||||
|
{ |
||||||
|
ma_sound_set_pitch(vecSounds.at(id), pitch); |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long MiniAudio::GetCursorMilliseconds(const int id) |
||||||
|
{ |
||||||
|
unsigned long long cursor; |
||||||
|
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||||
|
|
||||||
|
cursor /= sampleRate; |
||||||
|
cursor /= 1000; |
||||||
|
|
||||||
|
return cursor; |
||||||
|
} |
||||||
|
|
||||||
|
bool MiniAudio::IsPlaying(const int id) |
||||||
|
{ |
||||||
|
return ma_sound_is_playing(vecSounds.at(id)); |
||||||
|
} |
||||||
|
|
||||||
|
float MiniAudio::GetCursorFloat(const int id) |
||||||
|
{ |
||||||
|
unsigned long long cursor; |
||||||
|
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||||
|
|
||||||
|
unsigned long long length; |
||||||
|
ma_sound_get_length_in_pcm_frames(vecSounds.at(id), &length); |
||||||
|
|
||||||
|
return (float)cursor / length; |
||||||
|
} |
||||||
|
|
||||||
|
const std::vector<ma_sound*>& MiniAudio::GetSounds() const |
||||||
|
{ |
||||||
|
return vecSounds; |
||||||
|
} |
||||||
|
|
||||||
|
const std::vector<ma_sound*>& MiniAudio::GetOneOffSounds() const |
||||||
|
{ |
||||||
|
return vecOneOffSounds; |
||||||
|
} |
||||||
|
|
||||||
|
ma_device* MiniAudio::GetDevice() |
||||||
|
{ |
||||||
|
return &device; |
||||||
|
} |
||||||
|
|
||||||
|
ma_engine* MiniAudio::GetEngine() |
||||||
|
{ |
||||||
|
return &engine; |
||||||
|
} |
||||||
|
|
||||||
|
} // olc
|
||||||
|
|
||||||
|
#endif |
Before Width: | Height: | Size: 8.9 MiB After Width: | Height: | Size: 8.9 MiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
Loading…
Reference in new issue