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(tinyxml2 REQUIRED)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
|
||||
|
||||
|
|
@ -32,6 +33,9 @@ find_package(glm CONFIG REQUIRED)
|
|||
add_executable (YuppleMayham
|
||||
"src/main.cpp"
|
||||
"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/ftfont.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_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.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ class Scene;
|
|||
class Text;
|
||||
class ResourceManager;
|
||||
class Renderer;
|
||||
class AudioEngine;
|
||||
class GLWindow;
|
||||
|
||||
enum {
|
||||
|
|
@ -38,7 +39,7 @@ public:
|
|||
const unsigned getWindowWidth() const;
|
||||
const unsigned getWindowHeight() const;
|
||||
|
||||
void quit() { game_state = GAME_QUITTING; }
|
||||
void quit();
|
||||
~Game();
|
||||
|
||||
private:
|
||||
|
|
@ -51,6 +52,7 @@ private:
|
|||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<Renderer> renderer;
|
||||
std::shared_ptr<Text> textHandler;
|
||||
std::shared_ptr<AudioEngine> audioEngine;
|
||||
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 "sound/engine.h"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <memory>
|
||||
#include <utility/events.h>
|
||||
|
|
@ -54,6 +56,14 @@ bool Game::init()
|
|||
globalEventManager = std::make_shared<EventManager>();
|
||||
resourceManager = std::make_shared<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);
|
||||
textHandler = std::make_shared<Text>();
|
||||
if (!textHandler->loadFonts("fonts"))
|
||||
|
|
@ -108,6 +118,14 @@ void Game::render()
|
|||
window->swap();
|
||||
}
|
||||
|
||||
void Game::quit()
|
||||
{
|
||||
game_state = GAME_QUITTING;
|
||||
if (audioEngine) {
|
||||
audioEngine->killMusic();
|
||||
}
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
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