Skip to content
Snippets Groups Projects
Commit 39b928b5 authored by Joseph Walton-Rivers's avatar Joseph Walton-Rivers
Browse files

first stab at audio api

parent 824009e2
No related branches found
No related tags found
No related merge requests found
*,ogg filter=lfs diff=lfs merge=lfs -text
......@@ -28,6 +28,7 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
spdlog/1.10.0
freetype/2.11.1
bullet3/3.22a
openal/1.21.1
GENERATORS
cmake_find_package
cmake_find_package_multi
......
File added
......@@ -23,6 +23,7 @@
#include <memory>
#include "fggl/app.hpp"
#include "fggl/audio/openal/audio.hpp"
#include "fggl/gfx/atlas.hpp"
#include "fggl/gfx/window.hpp"
......@@ -77,6 +78,7 @@ static void setupServiceLocators(fggl::util::ServiceLocator& locator) {
locator.supply<fggl::gui::FontLibrary>(std::make_shared<fggl::gui::FontLibrary>());
locator.supply<fggl::ecs3::TypeRegistry>(std::make_shared<fggl::ecs3::TypeRegistry>());
locator.supply<fggl::audio::AudioService>(std::make_shared<fggl::audio::openAL>());
}
int main(int argc, const char* argv[]) {
......@@ -107,9 +109,19 @@ int main(int argc, const char* argv[]) {
auto *menu = app.add_state<fggl::scenes::BasicMenu>("menu");
// add some menu items for the game states
menu->add("terrain", [&app]() { app.change_state("game"); });
menu->add("rollball", [&app]() { app.change_state("rollball"); });
menu->add("quit", [&app]() { app.running(false); });
auto audio = locator.get<fggl::audio::AudioService>();
menu->add("terrain", [&app, &audio]() {
audio->play("click.ogg", false);
app.change_state("game");
});
menu->add("rollball", [&app, &audio]() {
audio->play("click.ogg", false);
app.change_state("rollball");
});
menu->add("quit", [&app, &audio]() {
audio->play("click.ogg", false);
app.running(false);
});
// the game states themselves
app.add_state<GameScene>("game");
......
......@@ -69,6 +69,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC freetype)
# Graphics backend
add_subdirectory(gfx)
add_subdirectory(audio)
# physics integration
add_subdirectory(phys/bullet)
......
target_sources(fggl
PRIVATE
types.cpp
)
add_subdirectory(openal)
\ No newline at end of file
find_package( OpenAL REQUIRED )
target_link_libraries(fggl PUBLIC OpenAL::OpenAL )
target_sources(fggl
PRIVATE
audio.cpp
)
/*
* ${license.title}
* Copyright (C) 2022 ${license.owner}
* ${license.mailto}
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "fggl/audio/openal/audio.hpp"
#include "fggl/util/service.h"
#include "fggl/data/storage.hpp"
namespace fggl::audio::openal {
void AudioServiceOAL::play(const std::string &filename, bool looping) {
// load audio clip into temp storage
AudioClip clip;
auto& locator = util::ServiceLocator::instance();
auto storage = locator.get<data::Storage>();
bool result = storage->load(data::StorageType::Data, filename, &clip);
if ( !result ) {
std::cerr << "error: can't load audio data" << std::endl;
}
play(clip, looping);
}
void AudioServiceOAL::play(AudioClip &clip, bool looping) {
// play the audio on the default (non-positioned) source
if ( m_defaultSource != nullptr ) {
m_defaultSource->play(clip, looping);
}
}
}
\ No newline at end of file
/*
* ${license.title}
* Copyright (C) 2022 ${license.owner}
* ${license.mailto}
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "fggl/data/storage.hpp"
#include "fggl/audio/audio.hpp"
#include "../stb/stb_vorbis.h"
namespace fggl::audio {
} // namespace fggl::audio
namespace fggl::data {
template<>
bool fggl_deserialize<audio::AudioClip>(std::filesystem::path &data, audio::AudioClip* out) {
out->sampleCount = stb_vorbis_decode_filename(data.c_str(),
&out->channels,
&out->sampleRate,
&out->data);
return out->sampleCount != -1;
}
} // namespace fggl::data
\ No newline at end of file
/*
* ${license.title}
* Copyright (C) 2022 ${license.owner}
* ${license.mailto}
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef FGGL_AUDIO_AUDIO_HPP
#define FGGL_AUDIO_AUDIO_HPP
#include <string>
namespace fggl::audio {
/**
* AudioClip is bit of audio loaded into memory.
*
* If the sampleCount is -1, the clip is invalid.
*/
struct AudioClip {
int channels;
int sampleRate;
int sampleCount;
short* data;
};
class AudioService {
public:
AudioService() = default;
virtual ~AudioService() = default;
virtual void play(const std::string& filename, bool looping = false) = 0;
virtual void play(AudioClip& clip, bool looping = false) = 0;
};
} // namespace fggl::audio
#endif //FGGL_AUDIO_AUDIO_HPP
/*
* ${license.title}
* Copyright (C) 2022 ${license.owner}
* ${license.mailto}
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//
// Created by webpigeon on 01/05/22.
//
#ifndef FGGL_AUDIO_OPENAL_AUDIO_HPP
#define FGGL_AUDIO_OPENAL_AUDIO_HPP
#include <AL/al.h>
#include <AL/alc.h>
#include "fggl/audio/audio.hpp"
#include "fggl/math/types.hpp"
#include <string>
#include <iostream>
#include <memory>
namespace fggl::audio::openal {
enum class AudioType {
MONO_8 = AL_FORMAT_MONO8,
MONO_16 = AL_FORMAT_MONO16,
STEREO_8 = AL_FORMAT_STEREO8,
STEREO_16 = AL_FORMAT_STEREO16
};
static void checkError(std::string context) {
auto code = alGetError();
if ( code == AL_NO_ERROR) {
return;
}
// now we check the error message
std::string error = "unknown";
switch ( code ) {
case ALC_INVALID_DEVICE:
error = "Invalid Device";
break;
case ALC_INVALID_CONTEXT:
error = "Invalid Context";
break;
case ALC_INVALID_ENUM:
error = "Invalid enum";
break;
case ALC_INVALID_VALUE:
error = "Invalid value";
break;
case ALC_OUT_OF_MEMORY:
error = "Out of memory";
break;
default:
error = "unknown error";
}
std::cerr << "OpenAL error: " << context << ": " << error << std::endl;
}
class AudioBuffer {
public:
AudioBuffer() : m_buffer(0) {
alGenBuffers(1, &m_buffer);
}
~AudioBuffer() {
alDeleteBuffers(1, &m_buffer);
}
inline void setData(AudioType type, void* data, ALsizei size, ALsizei frequency) {
alBufferData(m_buffer, (ALenum)type, data, size, frequency);
}
inline ALuint value() {
return m_buffer;
}
private:
ALuint m_buffer;
};
class AudioSource {
public:
AudioSource() : m_source(0), m_splat() {
alGenSources(1, &m_source);
}
~AudioSource(){
alDeleteSources(1, &m_source);
}
inline void play() {
alSourcePlay( m_source );
}
inline void stop() {
alSourceStop(m_source);
}
inline void pause() {
alSourcePause( m_source );
}
inline void rewind() {
alSourceRewind( m_source );
}
inline void play(AudioBuffer& buffer, bool looping) {
alSourcei(m_source, AL_BUFFER, buffer.value());
alSourcei( m_source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
}
inline void play(AudioClip& clip, bool looping) {
checkError("pre play");
AudioType format = clip.channels == 1 ? AudioType::MONO_8 : AudioType::STEREO_8;
m_splat.setData(format, clip.data, clip.sampleCount, clip.sampleRate);
checkError("saving to buffer");
alSourcei(m_source, AL_BUFFER, m_splat.value());
alSourcei( m_source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
checkError("setting parameters");
play();
}
inline void velocity(math::vec3& value) {
alSource3f(m_source, AL_VELOCITY, value.x, value.y, value.z);
}
inline void position(math::vec3& value) {
alSource3f(m_source, AL_POSITION, value.x, value.y, value.z);
}
void direction(math::vec3& value) {
alSource3f(m_source, AL_DIRECTION, value.x, value.y, value.z);
}
private:
ALuint m_source;
AudioBuffer m_splat;
};
class AudioServiceOAL : public AudioService {
public:
AudioServiceOAL() : m_device(alcOpenDevice(nullptr)) {
if ( m_device != nullptr ) {
m_context = alcCreateContext(m_device, nullptr);
alcMakeContextCurrent(m_context);
checkError("context setup");
m_defaultSource = std::make_unique<AudioSource>();
checkError("default source setup");
}
}
~AudioServiceOAL() override {
if ( m_device != nullptr ) {
alcDestroyContext(m_context);
alcCloseDevice(m_device);
}
}
void play(const std::string& filename, bool looping = false) override;
void play(AudioClip& clip, bool looping = false) override;
private:
ALCdevice* m_device;
ALCcontext* m_context{nullptr};
std::unique_ptr<AudioSource> m_defaultSource{nullptr};
};
} // namespace fggl::audio::openal
namespace fggl::audio {
using openAL = openal::AudioServiceOAL;
} // namepace fggl::audio
#endif //FGGL_AUDIO_OPENAL_AUDIO_HPP
......@@ -22,7 +22,7 @@ namespace fggl::data {
bool load(StorageType pool, const std::string &name, T *out) {
auto path = resolvePath(pool, name);
if (!std::filesystem::exists(path)) {
//spdlog::warn("Path {} does not exist!", path.c_str());
std::cerr << "Path " << path << " does not exist!" << std::endl;
return false;
}
return fggl_deserialize<T>(path, out);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment