Archived
1
Fork 0

Add audio system

This commit is contained in:
Joshua Goins 2019-01-08 22:27:42 -05:00
parent 0827103515
commit 94cccac9b2
14 changed files with 238 additions and 3 deletions

View file

@ -14,6 +14,8 @@ find_package(SDL2 REQUIRED)
find_package(Vulkan REQUIRED)
find_package(assimp REQUIRED)
find_package(glm REQUIRED)
find_package(opus REQUIRED)
find_package(OpenAL REQUIRED)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DDEBUG)
@ -58,7 +60,8 @@ set(ENGINE_SRC
src/assetmanager.cpp
src/entityparser.cpp
src/smaapass.cpp
src/debugpass.cpp)
src/debugpass.cpp
src/audiosystem.cpp)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(ENGINE_SRC
@ -78,7 +81,9 @@ target_link_libraries(Engine
assimp::assimp
nlohmann::json
stb::stb
SMAA::SMAA)
SMAA::SMAA
opus::opus
OpenAL::OpenAL)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(Engine
@ -131,6 +136,7 @@ add_data(ITER
data/sphere.obj
data/matpreview.world
data/basic.material
data/maticon.png)
data/maticon.png
data/music.opus)
add_subdirectory(tools)

47
cmake/Findopus.cmake Normal file
View file

@ -0,0 +1,47 @@
# - FindOpus.cmake
# Find the native opus includes and libraries
#
# OPUS_INCLUDE_DIRS - where to find opus/opus.h, etc.
# OPUS_LIBRARIES - List of libraries when using libopus(file).
# OPUS_FOUND - True if libopus found.
if(OPUS_INCLUDE_DIR AND OPUS_LIBRARY AND OPUSFILE_LIBRARY)
# Already in cache, be silent
set(OPUS_FIND_QUIETLY TRUE)
endif(OPUS_INCLUDE_DIR AND OPUS_LIBRARY AND OPUSFILE_LIBRARY)
find_path(OPUS_INCLUDE_DIR
NAMES opusfile.h
PATH_SUFFIXES opus include/opus include
)
# MSVC built opus may be named opus_static
# The provided project files name the library with the lib prefix.
find_library(OPUS_LIBRARY
NAMES opus opus_static libopus libopus_static
PATH_SUFFIXES lib64 lib libs64 libs
)
find_library(OPUSFILE_LIBRARY
NAMES opusfile opusfile_static libopusfile libopusfile_static
PATH_SUFFIXES lib64 lib libs64 libs
)
# Handle the QUIETLY and REQUIRED arguments and set OPUS_FOUND
# to TRUE if all listed variables are TRUE.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OPUS DEFAULT_MSG
OPUSFILE_LIBRARY OPUS_LIBRARY OPUS_INCLUDE_DIR
)
if(OPUS_FOUND)
set(OPUS_LIBRARIES ${OPUSFILE_LIBRARY} ${OPUS_LIBRARY})
set(OPUS_INCLUDE_DIRS ${OPUS_INCLUDE_DIR})
add_library(opus::opus UNKNOWN IMPORTED)
set_target_properties(opus::opus PROPERTIES
IMPORTED_LOCATION "${OPUSFILE_LIBRARY}")
set_target_properties(opus::opus PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${OPUS_INCLUDE_DIRS}
)
endif(OPUS_FOUND)

BIN
data/music.opus Normal file

Binary file not shown.

View file

@ -48,6 +48,9 @@
{
"type": "Camera",
"fov": 75
},
{
"type": "Listener"
}
]
}
@ -137,6 +140,9 @@
{
"type": "Camera",
"fov": 75
},
{
"type": "Listener"
}
]
}

View file

@ -11,6 +11,10 @@
"type": "Mesh",
"path": "scene.obj",
"material": "test.material"
},
{
"type": "Speaker",
"file": "music.opus"
}
]
},

View file

@ -5,18 +5,26 @@
struct MeshAsset;
struct MaterialAsset;
class Renderer;
struct AudioAsset;
class AudioSystem;
class AssetManager {
public:
void setRenderer(Renderer* r) {
renderer = r;
}
void setAudioSystem(AudioSystem* a) {
audioSystem = a;
}
MeshAsset* loadMesh(const std::string& path);
MaterialAsset* loadMaterial(const std::string& path);
AudioAsset* loadAudio(const std::string& path);
private:
Renderer* renderer = nullptr;
AudioSystem* audioSystem = nullptr;
};
inline AssetManager assetManager;

12
include/audio.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
struct AudioAsset {
int16_t* data = nullptr;
size_t size = 0;
int numChannels = 1;
unsigned int buffer = 0;
};

18
include/audiosystem.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include <AL/alc.h>
struct AudioAsset;
class AudioSystem {
public:
AudioSystem();
void update();
void fillAudioBuffers(AudioAsset* asset);
private:
ALCdevice* device = nullptr;
ALCcontext* context = nullptr;
};

View file

@ -2,6 +2,7 @@
#include <cstdint>
#include <map>
#include <vector>
#include <array>
#include <glm/glm.hpp>
#include <type_traits>
@ -41,6 +42,18 @@ struct CameraComponent {
glm::vec3 target = glm::vec3(0);
};
struct ListenerComponent {
};
struct AudioAsset;
struct SpeakerComponent {
AudioAsset* audio = nullptr;
unsigned int source = 0;
bool playing = false;
};
using EntityID = uint64_t;
struct World;
@ -52,6 +65,8 @@ namespace ECS {
inline std::map<EntityID, MeshComponent*> meshes;
inline std::map<EntityID, LightComponent*> lights;
inline std::map<EntityID, CameraComponent*> cameras;
inline std::map<EntityID, ListenerComponent*> listeners;
inline std::map<EntityID, SpeakerComponent*> speakers;
inline std::map<EntityID, ::World*> worlds;
inline EntityID lastID = 1;
@ -71,6 +86,12 @@ namespace ECS {
for(const auto& [id, camera] : cameras)
delete camera;
for(const auto& [id, listener] : listeners)
delete listener;
for(const auto& [id, speaker] : speakers)
delete speaker;
}
static inline EntityID createEntity(World* world) {
@ -97,6 +118,10 @@ namespace ECS {
lights[id] = t;
} else if constexpr(std::is_same<T, CameraComponent>::value) {
cameras[id] = t;
} else if constexpr(std::is_same<T, ListenerComponent>::value) {
listeners[id] = t;
} else if constexpr(std::is_same<T, SpeakerComponent>::value) {
speakers[id] = t;
}
return t;
@ -114,6 +139,10 @@ namespace ECS {
return lights[id];
} else if constexpr(std::is_same<T, CameraComponent>::value) {
return cameras[id];
} else if constexpr(std::is_same<T, ListenerComponent>::value) {
return listeners[id];
} else if constexpr(std::is_same<T, SpeakerComponent>::value) {
return speakers[id];
}
}
@ -158,6 +187,10 @@ namespace ECS {
lights.erase(lights.find(id), lights.end());
} else if constexpr(std::is_same<T, CameraComponent>::value) {
cameras.erase(cameras.find(id), cameras.end());
} else if constexpr(std::is_same<T, ListenerComponent>::value) {
listeners.erase(listeners.find(id), listeners.end());
} else if constexpr(std::is_same<T, SpeakerComponent>::value) {
speakers.erase(speakers.find(id), speakers.end());
}
}
};

View file

@ -6,11 +6,14 @@
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <opus/opusfile.h>
#include "mesh.h"
#include "material.h"
#include "renderer.h"
#include "stringutils.h"
#include "audio.h"
#include "audiosystem.h"
MeshAsset* AssetManager::loadMesh(const std::string& path) {
std::string fixedPath = "data/" + path;
@ -78,3 +81,34 @@ MaterialAsset* AssetManager::loadMaterial(const std::string& path) {
return material;
}
AudioAsset* AssetManager::loadAudio(const std::string& path) {
std::string fixedPath = "data/" + path;
AudioAsset* audio = new AudioAsset();
OggOpusFile* file = op_open_file(fixedPath.c_str(), nullptr);
if(!file)
return nullptr;
audio->numChannels = op_channel_count(file, -1);
int pcmSize = op_pcm_total(file, -1);
audio->data = new int16_t[pcmSize * audio->numChannels];
int samplesRead = 0;
while(samplesRead < pcmSize) {
int ns = op_read(file, audio->data + samplesRead * audio->numChannels, pcmSize * audio->numChannels, 0);
samplesRead += ns;
}
op_free(file);
audio->size = samplesRead * audio->numChannels * 2;
audioSystem->fillAudioBuffers(audio);
return audio;
}

52
src/audiosystem.cpp Normal file
View file

@ -0,0 +1,52 @@
#include "audiosystem.h"
#include <AL/al.h>
#include "ecs.h"
#include "worldmanager.h"
#include "audio.h"
AudioSystem::AudioSystem() {
device = alcOpenDevice(nullptr);
context = alcCreateContext(device, 0);
alcMakeContextCurrent(context);
}
void AudioSystem::update() {
const auto listeners = ECS::getWorldComponents<ListenerComponent>(worldManager.getCurrentWorld());
if(listeners.size() > 0) {
const auto& [id, listener] = listeners[0];
const auto& transform = ECS::getComponent<TransformComponent>(id);
alListener3f(AL_POSITION, transform->position.x, transform->position.y, transform->position.z);
}
const auto& speakers = ECS::getWorldComponents<SpeakerComponent>(worldManager.getCurrentWorld());
for(const auto& [id, speaker] : speakers) {
if(speaker->source == 0) {
alGenSources(1, &speaker->source);
alSourcei(speaker->source, AL_BUFFER, speaker->audio->buffer);
alSourcePlay(speaker->source);
}
const auto& transform = ECS::getComponent<TransformComponent>(id);
alSource3f(speaker->source, AL_POSITION, transform->position.x, transform->position.y, transform->position.z);
}
}
void AudioSystem::fillAudioBuffers(AudioAsset* asset) {
alGenBuffers(1, &asset->buffer);
ALenum format;
if(asset->numChannels == 1)
format = AL_FORMAT_MONO16;
else if(asset->numChannels == 2)
format = AL_FORMAT_STEREO16;
alBufferData(asset->buffer, format, asset->data, asset->size, 48000);
}

View file

@ -37,6 +37,11 @@ EntityID parseEntity(const nlohmann::json& json, World* world) {
light->type = componentObject["kind"];
} else if(componentType == "Camera") {
ECS::addComponent<CameraComponent>(entity);
} else if(componentType == "Listener") {
ECS::addComponent<ListenerComponent>(entity);
} else if(componentType == "Speaker") {
auto listener = ECS::addComponent<SpeakerComponent>(entity);
listener->audio = assetManager.loadAudio(componentObject["file"]);
}
}

View file

@ -20,6 +20,7 @@
#include "stringutils.h"
#include "worldmanager.h"
#include "assetmanager.h"
#include "audiosystem.h"
SDL_Window* window = nullptr;
Renderer* renderer = nullptr;
@ -141,7 +142,10 @@ int main(int argc, char* argv[]) {
RenderTarget* target = renderer->createSurfaceRenderTarget(surface);
auto audio = new AudioSystem();
assetManager.setRenderer(renderer);
assetManager.setAudioSystem(audio);
AnimationSystem* animationSystem = new AnimationSystem();
@ -169,6 +173,8 @@ int main(int argc, char* argv[]) {
MeshComponent* mesh = ECS::addComponent<MeshComponent>(playerEntity);
mesh->mesh = assetManager.loadMesh("player.obj");
mesh->material = assetManager.loadMaterial("test.material");
ListenerComponent* listener = ECS::addComponent<ListenerComponent>(playerEntity);
}
float currentTime = 0.0f, lastTime = 0.0f;
@ -238,6 +244,8 @@ int main(int argc, char* argv[]) {
cameraComponent->target = playerTransform->position;
}
audio->update();
#ifdef DEBUG
ImGui::NewFrame();

View file

@ -12,6 +12,8 @@
#include "entityparser.h"
#include "ecs.h"
#include "renderer.h"
#include "mesh.h"
#include "material.h"
void WorldManager::loadWorld(const std::string& path) {
std::ifstream file("data/" + path);