Added music player, next is the sound effects and loading music depending on the scene
This commit is contained in:
parent
268e6d6ebf
commit
1b6f5cff5b
10 changed files with 368 additions and 2 deletions
BIN
Resources/music/bright.ogg
Normal file
BIN
Resources/music/bright.ogg
Normal file
Binary file not shown.
Binary file not shown.
BIN
Resources/music/short_song.ogg
Normal file
BIN
Resources/music/short_song.ogg
Normal file
Binary file not shown.
|
|
@ -9,6 +9,7 @@ pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET GLOBAL luajit)
|
||||||
find_package(sol2 REQUIRED)
|
find_package(sol2 REQUIRED)
|
||||||
find_package(tinyxml2 REQUIRED)
|
find_package(tinyxml2 REQUIRED)
|
||||||
find_package(Freetype REQUIRED)
|
find_package(Freetype REQUIRED)
|
||||||
|
find_package(OpenAL REQUIRED)
|
||||||
find_package(glm CONFIG REQUIRED)
|
find_package(glm CONFIG REQUIRED)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,6 +33,9 @@ find_package(glm CONFIG REQUIRED)
|
||||||
add_executable (YuppleMayham
|
add_executable (YuppleMayham
|
||||||
"src/main.cpp"
|
"src/main.cpp"
|
||||||
"src/thirdparty/glad.c"
|
"src/thirdparty/glad.c"
|
||||||
|
"src/sound/engine.cpp"
|
||||||
|
"src/sound/audiostream.cpp"
|
||||||
|
"include/thirdparty/stb_vorbis.c"
|
||||||
"src/utility/data/font_data.c"
|
"src/utility/data/font_data.c"
|
||||||
"src/utility/ftfont.cpp"
|
"src/utility/ftfont.cpp"
|
||||||
"src/graphics/sprite.cpp"
|
"src/graphics/sprite.cpp"
|
||||||
|
|
@ -98,6 +102,6 @@ endif()
|
||||||
|
|
||||||
target_include_directories(YuppleMayham PRIVATE "${PROJECT_SOURCE_DIR}/YuppleMayham/include" ${LuaJIT_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build})
|
target_include_directories(YuppleMayham PRIVATE "${PROJECT_SOURCE_DIR}/YuppleMayham/include" ${LuaJIT_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build})
|
||||||
|
|
||||||
target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image glm::glm-header-only sol2 tinyxml2 freetype ${LuaJIT_LINK_LIBRARIES})
|
target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image openal glm::glm-header-only sol2 tinyxml2 freetype ${LuaJIT_LINK_LIBRARIES})
|
||||||
|
|
||||||
# TODO: Add tests and install targets if needed.
|
# TODO: Add tests and install targets if needed.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ class Scene;
|
||||||
class Text;
|
class Text;
|
||||||
class ResourceManager;
|
class ResourceManager;
|
||||||
class Renderer;
|
class Renderer;
|
||||||
|
class AudioEngine;
|
||||||
class GLWindow;
|
class GLWindow;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
@ -38,7 +39,7 @@ public:
|
||||||
const unsigned getWindowWidth() const;
|
const unsigned getWindowWidth() const;
|
||||||
const unsigned getWindowHeight() const;
|
const unsigned getWindowHeight() const;
|
||||||
|
|
||||||
void quit() { game_state = GAME_QUITTING; }
|
void quit();
|
||||||
~Game();
|
~Game();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -51,6 +52,7 @@ private:
|
||||||
std::shared_ptr<ResourceManager> resourceManager;
|
std::shared_ptr<ResourceManager> resourceManager;
|
||||||
std::shared_ptr<Renderer> renderer;
|
std::shared_ptr<Renderer> renderer;
|
||||||
std::shared_ptr<Text> textHandler;
|
std::shared_ptr<Text> textHandler;
|
||||||
|
std::shared_ptr<AudioEngine> audioEngine;
|
||||||
std::shared_ptr<EventManager> globalEventManager;
|
std::shared_ptr<EventManager> globalEventManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
63
YuppleMayham/include/sound/audiostream.h
Normal file
63
YuppleMayham/include/sound/audiostream.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef _H_AUDIO_STREAM_H
|
||||||
|
#define _H_AUDIO_STREAM_H
|
||||||
|
|
||||||
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
|
#include "thirdparty/stb_vorbis.c"
|
||||||
|
|
||||||
|
#include "utility/logger.h"
|
||||||
|
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace AUDIO {
|
||||||
|
constexpr size_t CHUNK_SIZE = 4096;
|
||||||
|
constexpr int SAMPLE_RATE = 44100;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioStream();
|
||||||
|
|
||||||
|
bool PlayStream();
|
||||||
|
void AddToQueue(std::string songName);
|
||||||
|
void PauseStream() { paused = !paused; }
|
||||||
|
|
||||||
|
void kill() { stopThread = true; }
|
||||||
|
~AudioStream();
|
||||||
|
private:
|
||||||
|
std::queue<std::string> songNames;
|
||||||
|
std::mutex mutex;
|
||||||
|
std::atomic<bool> stopThread;
|
||||||
|
std::atomic<bool> paused;
|
||||||
|
std::string CurStreamName();
|
||||||
|
bool eof;
|
||||||
|
std::thread streamThread;
|
||||||
|
|
||||||
|
int loadInitalChunk();
|
||||||
|
int loadNextChunk();
|
||||||
|
|
||||||
|
int loadChunk(ALuint buffer);
|
||||||
|
|
||||||
|
void popQueue();
|
||||||
|
void cycleQueue();
|
||||||
|
|
||||||
|
bool openFile(std::string fileName);
|
||||||
|
|
||||||
|
void stream();
|
||||||
|
|
||||||
|
short *data;
|
||||||
|
stb_vorbis *file;
|
||||||
|
stb_vorbis_info info;
|
||||||
|
ALuint buffers[4];
|
||||||
|
ALuint source;
|
||||||
|
ALint state;
|
||||||
|
ALint format;
|
||||||
|
size_t last_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _H_AUDIO_STREAM_H
|
||||||
34
YuppleMayham/include/sound/engine.h
Normal file
34
YuppleMayham/include/sound/engine.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef _H_AUDIO_ENGINE_H
|
||||||
|
#define _H_AUDIO_ENGINE_H
|
||||||
|
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "utility/events.h"
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include "utility/resourcemanager.h"
|
||||||
|
#include "utility/logger.h"
|
||||||
|
|
||||||
|
class AudioEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioEngine(std::weak_ptr<ResourceManager> _resource);
|
||||||
|
|
||||||
|
void hookEventManager(std::weak_ptr<EventManager> _events);
|
||||||
|
void pushMusic(std::string _songName);
|
||||||
|
void playMusic();
|
||||||
|
void pauseMusic();
|
||||||
|
void killMusic();
|
||||||
|
|
||||||
|
~AudioEngine();
|
||||||
|
private:
|
||||||
|
std::unique_ptr<AudioStream> musicPlayer;
|
||||||
|
std::weak_ptr<EventManager> globalEventManager;
|
||||||
|
std::weak_ptr<ResourceManager> resourceManager;
|
||||||
|
ALCdevice *device;
|
||||||
|
ALCcontext *context;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _H_AUDIO_ENGINE_H
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
#include "graphics/glwindow.h"
|
#include "graphics/glwindow.h"
|
||||||
|
|
||||||
|
#include "sound/engine.h"
|
||||||
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility/events.h>
|
#include <utility/events.h>
|
||||||
|
|
@ -54,6 +56,14 @@ bool Game::init()
|
||||||
globalEventManager = std::make_shared<EventManager>();
|
globalEventManager = std::make_shared<EventManager>();
|
||||||
resourceManager = std::make_shared<ResourceManager>();
|
resourceManager = std::make_shared<ResourceManager>();
|
||||||
renderer = std::make_shared<Renderer>(resourceManager);
|
renderer = std::make_shared<Renderer>(resourceManager);
|
||||||
|
audioEngine = std::make_shared<AudioEngine>(resourceManager);
|
||||||
|
audioEngine->hookEventManager(globalEventManager);
|
||||||
|
/* Testing */
|
||||||
|
audioEngine->pushMusic("music/short_song.ogg");
|
||||||
|
audioEngine->pushMusic("music/bright.ogg");
|
||||||
|
audioEngine->pushMusic("music/main_song.ogg");
|
||||||
|
audioEngine->playMusic();
|
||||||
|
/* */
|
||||||
renderer->hookEventManager(globalEventManager);
|
renderer->hookEventManager(globalEventManager);
|
||||||
textHandler = std::make_shared<Text>();
|
textHandler = std::make_shared<Text>();
|
||||||
if (!textHandler->loadFonts("fonts"))
|
if (!textHandler->loadFonts("fonts"))
|
||||||
|
|
@ -108,6 +118,14 @@ void Game::render()
|
||||||
window->swap();
|
window->swap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::quit()
|
||||||
|
{
|
||||||
|
game_state = GAME_QUITTING;
|
||||||
|
if (audioEngine) {
|
||||||
|
audioEngine->killMusic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Game::~Game()
|
Game::~Game()
|
||||||
{
|
{
|
||||||
resourceManager->clearResources();
|
resourceManager->clearResources();
|
||||||
|
|
|
||||||
193
YuppleMayham/src/sound/audiostream.cpp
Normal file
193
YuppleMayham/src/sound/audiostream.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
AudioStream::AudioStream()
|
||||||
|
{
|
||||||
|
alGenBuffers(4, buffers);
|
||||||
|
alGenSources(1, &source);
|
||||||
|
data = (short *)std::malloc(AUDIO::CHUNK_SIZE * sizeof(short) * 2); // Make space for stereo even if we are playing mono
|
||||||
|
stopThread = false;
|
||||||
|
eof = true;
|
||||||
|
paused = true;
|
||||||
|
streamThread = std::thread(&AudioStream::stream, this);
|
||||||
|
streamThread.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AudioStream::CurStreamName()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
return songNames.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioStream::PlayStream()
|
||||||
|
{
|
||||||
|
if (CurStreamName().empty()) {
|
||||||
|
LOG(WARN, "Song queue empty!", NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG(INFO, "Current song is: {}", CurStreamName());
|
||||||
|
return (paused = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStream::AddToQueue(std::string songName)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
LOG(INFO, "Adding song {} to queue", songName);
|
||||||
|
songNames.push(songName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioStream::openFile(std::string fileName)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
file = stb_vorbis_open_filename(CurStreamName().c_str(), &error, NULL);
|
||||||
|
if (file == nullptr || error != STBVorbisError::VORBIS__no_error) {
|
||||||
|
LOG(ERROR, "Failed to open file: '{}' error code: {}", CurStreamName(), error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
info = stb_vorbis_get_info(file);
|
||||||
|
if (info.sample_rate != AUDIO::SAMPLE_RATE) {
|
||||||
|
LOG(ERROR, "Failed to open file: '{}', make sure to convert sample rate to 44100!", CurStreamName());
|
||||||
|
stb_vorbis_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
stb_vorbis_seek_start(file);
|
||||||
|
std::free(data);
|
||||||
|
data = (short *)std::malloc(AUDIO::CHUNK_SIZE * sizeof(short) * info.channels);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioStream::loadChunk(ALuint buffer)
|
||||||
|
{
|
||||||
|
int sample = 0;
|
||||||
|
if (file == NULL) {
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
if (info.channels == 0) {
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
sample = stb_vorbis_get_samples_short_interleaved(file, info.channels, data, AUDIO::CHUNK_SIZE);
|
||||||
|
if (sample == 0)
|
||||||
|
return sample;
|
||||||
|
format = info.channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||||
|
//LOG(INFO, "Buffer id {}, Format {}, data size {}, sample rate {},\nfilename {}",
|
||||||
|
// buffer, format == AL_FORMAT_MONO16 ? "MONO" : "STEREO", sample * info.channels * sizeof(short), info.sample_rate, songNames.front());
|
||||||
|
alBufferData(buffer, format, data, sample * info.channels * sizeof(short), info.sample_rate);
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioStream::loadInitalChunk()
|
||||||
|
{
|
||||||
|
int samples = 0;
|
||||||
|
if (!openFile(CurStreamName())) {
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
samples = loadChunk(buffers[i]);
|
||||||
|
if (samples == 0) {
|
||||||
|
LOG(ERROR, "Music file {} too small!", CurStreamName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioStream::loadNextChunk()
|
||||||
|
{
|
||||||
|
int samples = loadChunk(buffers[last_buffer]);
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStream::popQueue()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
if (songNames.empty())
|
||||||
|
return;
|
||||||
|
songNames.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStream::cycleQueue()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
LOG(INFO, "Cycling queue", NULL);
|
||||||
|
if (songNames.empty())
|
||||||
|
return;
|
||||||
|
std::string buffer = songNames.front();
|
||||||
|
songNames.pop();
|
||||||
|
songNames.push(buffer);
|
||||||
|
LOG(INFO, "New front: {}", songNames.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStream::stream()
|
||||||
|
{
|
||||||
|
int processed = 0;
|
||||||
|
int queued = 0;
|
||||||
|
bool inital = true;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
//LOG(INFO, "Call from in audio thread!", NULL);
|
||||||
|
//LOG(INFO, "Top of queue is: {}", CurStreamName());
|
||||||
|
paused = false;
|
||||||
|
|
||||||
|
while (!stopThread) {
|
||||||
|
if (songNames.empty() || paused) {
|
||||||
|
LOG(INFO, "Paused or queue empty", NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||||
|
if (inital) {
|
||||||
|
int samples = loadInitalChunk();
|
||||||
|
if (samples == 0) {
|
||||||
|
LOG(ERROR, "Music file '{}' either failed to load or not big enough!", CurStreamName());
|
||||||
|
popQueue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
alSourceQueueBuffers(source, 4, buffers);
|
||||||
|
alSourcePlay(source);
|
||||||
|
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||||
|
inital = false;
|
||||||
|
eof = false;
|
||||||
|
}
|
||||||
|
// If we are at the end of file, the and stream queue has a song in it
|
||||||
|
// Load in the inital buffers
|
||||||
|
if (state != AL_PLAYING) {
|
||||||
|
alSourcePlay(source);
|
||||||
|
}
|
||||||
|
while (state == AL_PLAYING) {
|
||||||
|
if (paused) {
|
||||||
|
alSourcePause(source);
|
||||||
|
}
|
||||||
|
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||||
|
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
|
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
|
||||||
|
|
||||||
|
while (processed != 0) {
|
||||||
|
alSourceUnqueueBuffers(source, 1, &buffers[last_buffer]);
|
||||||
|
int samples = loadNextChunk();
|
||||||
|
if (samples == 0) {
|
||||||
|
LOG(INFO, "Hit the end of the song!", NULL);
|
||||||
|
cycleQueue();
|
||||||
|
stb_vorbis_close(file);
|
||||||
|
while (!openFile(CurStreamName())) {
|
||||||
|
LOG(ERROR, "Failed to open next in stream! {}", CurStreamName());
|
||||||
|
popQueue();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
//LOG(INFO, "Buffer index: {}", last_buffer);
|
||||||
|
alSourceQueueBuffers(source, 1, &buffers[last_buffer]);
|
||||||
|
last_buffer = (last_buffer + 1) % 4;
|
||||||
|
processed--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioStream::~AudioStream()
|
||||||
|
{
|
||||||
|
std::free(data);
|
||||||
|
alDeleteBuffers(4, buffers);
|
||||||
|
alDeleteSources(1, &source);
|
||||||
|
}
|
||||||
52
YuppleMayham/src/sound/engine.cpp
Normal file
52
YuppleMayham/src/sound/engine.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "sound/engine.h"
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
|
||||||
|
AudioEngine::AudioEngine(std::weak_ptr<ResourceManager> _resource)
|
||||||
|
{
|
||||||
|
// Open the default device for now
|
||||||
|
if ((device = alcOpenDevice(NULL)) == NULL) {
|
||||||
|
LOG(ERROR, "Failed to open default device {}", alGetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context = alcCreateContext(device, NULL);
|
||||||
|
alcMakeContextCurrent(context);
|
||||||
|
musicPlayer = std::make_unique<AudioStream>();
|
||||||
|
resourceManager = _resource;
|
||||||
|
alGetError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioEngine::hookEventManager(std::weak_ptr<EventManager> _events)
|
||||||
|
{
|
||||||
|
globalEventManager = _events;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioEngine::pushMusic(std::string _songName)
|
||||||
|
{
|
||||||
|
LOG(INFO, "Loading song {}", _songName);
|
||||||
|
musicPlayer->AddToQueue(_songName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioEngine::playMusic()
|
||||||
|
{
|
||||||
|
LOG(INFO, "Playing stream", NULL);
|
||||||
|
musicPlayer->PlayStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioEngine::pauseMusic()
|
||||||
|
{
|
||||||
|
musicPlayer->PauseStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioEngine::killMusic()
|
||||||
|
{
|
||||||
|
musicPlayer->kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioEngine::~AudioEngine()
|
||||||
|
{
|
||||||
|
musicPlayer->kill();
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
alcDestroyContext(context);
|
||||||
|
alcCloseDevice(device);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue