You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
8.4 KiB
302 lines
8.4 KiB
/*
|
|
olcPGEX_AudioListener.h
|
|
|
|
+-------------------------------------------------------------+
|
|
| OneLoneCoder Pixel Game Engine Extension |
|
|
| AudioListener v1.0 |
|
|
+-------------------------------------------------------------+
|
|
|
|
What is this?
|
|
~~~~~~~~~~~~~
|
|
This is an extension to the olcPixelGameEngine v2.16 and above.
|
|
|
|
Its purpose is to allow audio integration into PGE which is
|
|
compatible with Emscripten for Web applications also.
|
|
|
|
Unlike my other extensions, this one requires some external
|
|
libraries and additional setup (refer to github for instructions).
|
|
|
|
In addition to the libraries the following files are required:
|
|
|
|
olcPGEX_AudioListener.h (this file)
|
|
olcPGEX_AudioSource.h
|
|
|
|
These can be found in the github repo as well...
|
|
|
|
|
|
What it can do:
|
|
~~~~~~~~~~~~~~~
|
|
Play Audio!
|
|
|
|
Using SDL2 as a backend and SoLoud as a frontend, this extension allows you
|
|
to easily load and manipulate sound in a way that is cross platform and also
|
|
has web support (using emscripten for PGE).
|
|
|
|
In addition to the standard PLAY, PAUSE, and STOP controls you can also adjust
|
|
VOLUME settings and MODULATION (playback speed).
|
|
|
|
WAV and OGG are supported on most backends, however MP3 is also supported by
|
|
SoLoud and SDL2 (even inside the web browser!).
|
|
|
|
|
|
Limitations:
|
|
~~~~~~~~~~~~
|
|
Requires SDL2, SDL2_Mixer, and SoLoud libraries be installed and linked
|
|
successfully (detailed instructions on the github repo).
|
|
|
|
|
|
How Does It Work?
|
|
~~~~~~~~~~~~~~~~~
|
|
Once you have followed the setup instructions on the github repo and
|
|
successfully compiled the test program you are ready to follow these
|
|
instructions...
|
|
|
|
Add the following defines / includes underneath your olcPixelGameEngine
|
|
include:
|
|
|
|
#define AUDIO_LISTENER_IMPLEMENTATION
|
|
#include "olcPGEX_AudioListener.h"
|
|
#define AUDIO_SOURCE_IMPLEMENTATION
|
|
#include "olcPGEX_AudioSource.h"
|
|
|
|
(Order matters here, they must be included exactly as above!)
|
|
|
|
In your declarations add exactly (1) AudioListener
|
|
|
|
olcPGEX_AudioListener AL{};
|
|
|
|
|
|
(Note: Currently, only 1 instance of an AudioListener is permitted)
|
|
|
|
Now add at least (1) AudioSource (you will eventually add many of these)
|
|
|
|
olcPGEX_AudioSource AS_Test{};
|
|
|
|
|
|
In the OnUserCreate function you must now initialise the AudioListener
|
|
|
|
AL.AudioSystemInit();
|
|
|
|
|
|
Next we can assign our AudioListener to our AudioSource and load an audio file
|
|
|
|
AS_Test.AL = &game.AL;
|
|
AS_Test.LoadAudioSample(1, "./assets/mus/Test.mp3");
|
|
|
|
|
|
This assumes you have an MP3 file called "Test.mp3" in the listed folder in your
|
|
project directory. Also note the ID is set to "1" in this example. It is recommended
|
|
that you assign labels to your audio files as IDs instead so you can more easily
|
|
keep track of them. For example
|
|
|
|
enum AUDIO
|
|
{
|
|
NULL_SND = 0, // used as a default case
|
|
TEST_SND = 1,
|
|
// add other sounds here
|
|
};
|
|
|
|
AS_Test.LoadAudioSample(TEST_SND, "./assets/mus/Test.mp3");
|
|
|
|
This way you can easily refer to your sound without having to remember the integer
|
|
value you assigned it in the beginning...
|
|
|
|
|
|
Now all that is left to do is play the sound...
|
|
|
|
In OnUserUpdate we can play / stop the sound using the SPACEBAR like so
|
|
|
|
if (GetKey(olc::Key::SPACE).bPressed)
|
|
{
|
|
if (AS_Test.bIsPlaying)
|
|
AS_Test.Stop();
|
|
else
|
|
AS_Test.Play();
|
|
|
|
}
|
|
|
|
|
|
Those are the basics... other features can be accessed in much the same way.
|
|
|
|
Enjoy!
|
|
|
|
|
|
|
|
|
|
License (OLC-3)
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Copyright 2018 - 2019 OneLoneCoder.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.
|
|
|
|
Author
|
|
~~~~~~
|
|
Justin Richards
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#ifndef OLC_PGEX_AUDIO_LISTENER
|
|
#define OLC_PGEX_AUDIO_LISTENER
|
|
|
|
#include "soloud.h"
|
|
#include "soloud_wav.h"
|
|
|
|
struct PlayingInstance{
|
|
vf2d pos;
|
|
float vol;
|
|
int handle;
|
|
};
|
|
|
|
class olcPGEX_AudioListener : public olc::PGEX
|
|
{
|
|
public:
|
|
// Struct to keep the Audio Sample data together
|
|
struct sAudioSample
|
|
{
|
|
sAudioSample(int ID, SoLoud::Wav* wavPtr)
|
|
{
|
|
nSampleID = ID;
|
|
wav = wavPtr;
|
|
}
|
|
|
|
int nSampleID;
|
|
SoLoud::Wav* wav;
|
|
};
|
|
|
|
// SoLoud Audio Engine Object
|
|
SoLoud::Soloud soloud;
|
|
|
|
// Position used for volume calculations
|
|
olc::vf2d vecPos = { 0.0f, 0.0f };
|
|
|
|
// Global volume settings
|
|
float fMusicVolume = 1.f;
|
|
float fSoundFXVolume = 1.f;
|
|
|
|
bool bMusicOn = true;
|
|
bool bSoundOn = true;
|
|
|
|
// Vector of Audio Samples
|
|
std::vector<sAudioSample> audioSamples;
|
|
std::vector<PlayingInstance> handles;
|
|
std::list<SoLoud::Wav> wavs;
|
|
|
|
|
|
|
|
// Initialise the Audio Engine, and Destroy it when done
|
|
void AudioSystemInit();
|
|
void AudioSystemDestroy();
|
|
|
|
// Load a file and store it in the list of wavs
|
|
void LoadAudioSample(int ID, const char* fileName);
|
|
|
|
// Identify a particular Audio Sample based on its ID
|
|
sAudioSample* GetAudioSampleByID(int ID);
|
|
|
|
// Update the spacial position of the Audio Listener
|
|
void UpdatePosition(olc::vf2d pos);
|
|
|
|
// Calculate distance between listener and source
|
|
float GetDistance(olc::vf2d sourcePos, bool returnRoot = true);
|
|
|
|
void OnUserUpdate(float fElapsedTime);
|
|
};
|
|
|
|
#ifdef AUDIO_LISTENER_IMPLEMENTATION
|
|
#undef AUDIO_LISTENER_IMPLEMENTATION
|
|
|
|
void olcPGEX_AudioListener::AudioSystemInit()
|
|
{
|
|
// Initialise the SoLoud backend
|
|
soloud.init();
|
|
}
|
|
|
|
void olcPGEX_AudioListener::AudioSystemDestroy()
|
|
{
|
|
// Clean up the SoLoud engine
|
|
soloud.deinit();
|
|
}
|
|
|
|
void olcPGEX_AudioListener::LoadAudioSample(int ID, const char* fileName)
|
|
{
|
|
// Search for any matching IDs
|
|
for (auto& a : audioSamples)
|
|
if (a.nSampleID == ID) return; // Audio Sample is already loaded, no need to load the same file twice!
|
|
|
|
// Add a new wav to the list of wavs and get a pointer to it
|
|
wavs.push_back(SoLoud::Wav());
|
|
SoLoud::Wav* wavPtr = &wavs.back();
|
|
|
|
// Use the pointer to load the file into the back of the wav list
|
|
wavPtr->load(fileName);
|
|
|
|
// Create a new Audio sample object in the vector of samples that links both the ID and wav file itself, for convenience
|
|
audioSamples.emplace_back(sAudioSample(ID, wavPtr));
|
|
}
|
|
|
|
olcPGEX_AudioListener::sAudioSample* olcPGEX_AudioListener::GetAudioSampleByID(int ID)
|
|
{
|
|
// Look for matching IDs
|
|
for (auto& a : audioSamples)
|
|
if (ID == a.nSampleID) return &a; // Match found! Return it...
|
|
|
|
// No match found, need to return a reference so we return the first sample in the list
|
|
return &audioSamples[0];
|
|
}
|
|
|
|
void olcPGEX_AudioListener::UpdatePosition(olc::vf2d pos)
|
|
{
|
|
// Position
|
|
vecPos = pos;
|
|
}
|
|
|
|
float olcPGEX_AudioListener::GetDistance(olc::vf2d sourcePos, bool returnRoot)
|
|
{
|
|
// Return the distance via square root if needed, or the squared version when optimisation is possible
|
|
if (returnRoot)
|
|
return sqrtf(abs(sourcePos.x * sourcePos.x - vecPos.x * vecPos.x) + abs(sourcePos.y * sourcePos.y - vecPos.y * vecPos.y));
|
|
else
|
|
return abs(sourcePos.x * sourcePos.x - vecPos.x * vecPos.x) + abs(sourcePos.y * sourcePos.y - vecPos.y * vecPos.y);
|
|
|
|
}
|
|
|
|
void olcPGEX_AudioListener::OnUserUpdate(float fElapsedTime){
|
|
std::erase_if(handles,[&](PlayingInstance&handle){return !soloud.isValidVoiceHandle(handle.handle);});
|
|
for(PlayingInstance&handle:handles){
|
|
soloud.setPan(handle.handle,std::clamp((handle.pos.x-vecPos.x)/1024,-1.f,1.f));
|
|
soloud.setVolume(handle.handle,handle.vol*std::max(0.f,abs(1-std::min(1.0f,(GetDistance(handle.pos)/1024.f))))*(handle.pos!=vf2d{0,0}?fSoundFXVolume:1));
|
|
}
|
|
}
|
|
|
|
#endif // AUDIO_LISTENER_IMPLEMENTATION
|
|
|
|
#endif |