Compare commits
20 Commits
b8cdd54888
...
main
Author | SHA1 | Date | |
---|---|---|---|
64a5d19464 | |||
090317b394 | |||
4c580f4956 | |||
8b8c5310ae | |||
7c5d80cd31 | |||
ff1241fe36 | |||
57b8bfa719 | |||
640f6af291 | |||
0d2603abf0 | |||
13a21387df | |||
146bd49b36 | |||
71e4ff9317 | |||
64b5a8af29 | |||
76b53b5f71 | |||
67e423a77d | |||
a40c0665ac | |||
ee83818de2 | |||
29bcf9fbae | |||
ead1e742d1 | |||
81cef5cc8a |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,7 +5,7 @@ project(ChargeAudio VERSION 1.0)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
|
||||
set(CMAKE_MODULE_PATH "modules/" ${CMAKE_MODULE_PATH})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-subobject-linkage")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address")
|
||||
endif()
|
||||
@@ -19,8 +19,9 @@ pkg_check_modules(AVCODEC REQUIRED libavcodec)
|
||||
pkg_check_modules(AVUTIL REQUIRED libavutil)
|
||||
pkg_check_modules(SWRESAMPLE REQUIRED libswresample)
|
||||
|
||||
add_library(ChargeAudio SHARED "src/ChargeAudio.hpp" "src/Engine.cpp"
|
||||
"src/Sound.cpp" "lib/miniaudio/miniaudio.c")
|
||||
add_library(
|
||||
ChargeAudio SHARED "src/ChargeAudio.hpp" "src/Engine.cpp" "src/Sound.cpp"
|
||||
"src/Listener.cpp" "lib/miniaudio/miniaudio.c")
|
||||
|
||||
target_link_libraries(
|
||||
ChargeAudio
|
||||
|
@@ -1,41 +1,117 @@
|
||||
#ifndef CHARGE_AUDIO_BASE_H
|
||||
#define CHARGE_AUDIO_BASE_H
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Vector.h>
|
||||
#include "miniaudio/miniaudio.h"
|
||||
|
||||
#include <Corrade/Containers/Containers.h>
|
||||
#include <Corrade/Containers/Pointer.h>
|
||||
#include <Magnum/Math/Vector3.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace ChargeAudio {
|
||||
namespace {
|
||||
#include "miniaudio/miniaudio.h"
|
||||
}
|
||||
using namespace Corrade;
|
||||
|
||||
typedef Containers::Pointer<class Sound> SoundContainer;
|
||||
typedef Containers::Pointer<class Listener> ListenerContainer;
|
||||
class Sound {
|
||||
public:
|
||||
enum class SoundState { Idle, Playing, Paused, Finished };
|
||||
enum class SoundType { FromFile, StreamedRawPCM, RawPCM };
|
||||
// No copying
|
||||
Sound(const Sound &) = delete;
|
||||
Sound &operator=(const Sound &) = delete;
|
||||
|
||||
// No moving
|
||||
Sound(Sound &&) = delete;
|
||||
Sound &operator=(Sound &&) = delete;
|
||||
|
||||
~Sound();
|
||||
void Play();
|
||||
void Pause();
|
||||
void Reset();
|
||||
|
||||
const SoundState GetState();
|
||||
const SoundType GetSoundType();
|
||||
|
||||
const float GetPlaybackTime();
|
||||
bool SetPlaybackTime(float time);
|
||||
const float GetDuration();
|
||||
void SetPosition(Magnum::Vector3 position);
|
||||
Magnum::Vector3 GetPosition();
|
||||
const Magnum::Vector3 GetPosition();
|
||||
void SetVolume(float value);
|
||||
float GetVolume();
|
||||
const float GetVolume();
|
||||
|
||||
private:
|
||||
Sound();
|
||||
Sound(class Engine *engine, std::function<void(Sound *)> setupFunction,
|
||||
SoundType type, std::string additionalErrorMessage = "");
|
||||
static void onSoundFinish(void *customData, ma_sound *);
|
||||
|
||||
class Engine *baseEngine;
|
||||
ma_sound maSound;
|
||||
ma_sound_config maConfig;
|
||||
ma_pcm_rb maRingBuffer;
|
||||
ma_audio_buffer maAudioBuffer;
|
||||
SoundState state = SoundState::Idle;
|
||||
SoundType type;
|
||||
|
||||
friend class Engine;
|
||||
};
|
||||
|
||||
class Listener {
|
||||
public:
|
||||
// No copying, can move
|
||||
Listener(const Listener &) = delete;
|
||||
Listener &operator=(const Listener &) = delete;
|
||||
|
||||
void SetEnabled(bool isEnabled);
|
||||
const bool GetEnabled();
|
||||
void SetDirection(Magnum::Vector3 position);
|
||||
const Magnum::Vector3 GetDirection();
|
||||
void SetPosition(Magnum::Vector3 position);
|
||||
const Magnum::Vector3 GetPosition();
|
||||
|
||||
private:
|
||||
Listener();
|
||||
class Engine *baseEngine;
|
||||
ma_uint32 listenerID;
|
||||
friend class Engine;
|
||||
};
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
Engine();
|
||||
Sound CreateSound(std::string filepath);
|
||||
Engine(uint32_t sampleRate = 44100, uint32_t channels = 2);
|
||||
|
||||
// No copying
|
||||
Engine(const Engine &) = delete;
|
||||
Engine &operator=(const Engine &) = delete;
|
||||
|
||||
// No movement
|
||||
Engine(Engine &&) = delete;
|
||||
Engine &operator=(Engine &&) = delete;
|
||||
|
||||
~Engine();
|
||||
|
||||
// Creating tools
|
||||
SoundContainer CreateSound(int bufferLengthInSeconds);
|
||||
SoundContainer CreateSound(uint8_t *data, int length);
|
||||
SoundContainer CreateSound(std::string filepath, bool streamFile = false);
|
||||
ListenerContainer CreateListener();
|
||||
|
||||
void SetVolume(float value);
|
||||
float GetVolume();
|
||||
const float GetVolume();
|
||||
|
||||
uint32_t GetSampleRate();
|
||||
uint32_t GetChannelCount();
|
||||
|
||||
private:
|
||||
ma_engine maEngine;
|
||||
ma_engine_config maConfig;
|
||||
ma_result maResponse;
|
||||
ma_decoder maStero;
|
||||
ma_uint64 listenerCounter = 0;
|
||||
friend class Listener;
|
||||
friend class Sound;
|
||||
};
|
||||
|
||||
} // namespace ChargeAudio
|
||||
|
@@ -1,37 +1,94 @@
|
||||
#include "ChargeAudio.hpp"
|
||||
#include <Corrade/Utility/Debug.h>
|
||||
#include "miniaudio/miniaudio.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <Corrade/Utility/Debug.h>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace ChargeAudio;
|
||||
using namespace Corrade;
|
||||
|
||||
Engine::Engine() {
|
||||
if ((maResponse = ma_engine_init(NULL, &maEngine)) != MA_SUCCESS) {
|
||||
Engine::Engine(uint32_t sampleRate, uint32_t channels) {
|
||||
maConfig = ma_engine_config_init();
|
||||
maConfig.sampleRate = sampleRate;
|
||||
maConfig.channels = channels;
|
||||
|
||||
if ((maResponse = ma_engine_init(&maConfig, &maEngine)) != MA_SUCCESS) {
|
||||
Utility::Error{} << "Could not init miniaudio (" << maResponse << ")";
|
||||
throw new std::runtime_error(
|
||||
"Failed to init miniaudio engine. Check STDERR for more info.");
|
||||
}
|
||||
}
|
||||
|
||||
Sound Engine::CreateSound(std::string filepath) {
|
||||
Sound sound;
|
||||
sound.baseEngine = this;
|
||||
Engine::~Engine() { ma_engine_uninit(&maEngine); }
|
||||
|
||||
maResponse = ma_sound_init_from_file(&maEngine, filepath.c_str(), 0, NULL,
|
||||
NULL, &sound.maSound);
|
||||
if (maResponse != MA_SUCCESS) {
|
||||
Utility::Error{} << "Failed to create the sound from the file: "
|
||||
<< filepath.c_str() << " (" << maResponse << ")";
|
||||
throw new std::runtime_error(
|
||||
"Failed to create the sound from file. Check STDERR for more info.");
|
||||
}
|
||||
uint32_t Engine::GetSampleRate() { return maEngine.sampleRate; }
|
||||
uint32_t Engine::GetChannelCount() { return ma_engine_get_channels(&maEngine); }
|
||||
|
||||
return sound;
|
||||
// Use case: Stream of PCM data
|
||||
SoundContainer Engine::CreateSound(int bufferLengthInSeconds) {
|
||||
return SoundContainer(new Sound(
|
||||
this,
|
||||
[length = bufferLengthInSeconds, channels = GetChannelCount(),
|
||||
sampleRate = GetSampleRate()](Sound *sound) {
|
||||
ma_result result = ma_pcm_rb_init(
|
||||
ma_format_s32, channels, sampleRate * channels * length, nullptr,
|
||||
nullptr, &sound->maRingBuffer);
|
||||
if (result != MA_SUCCESS) {
|
||||
Utility::Error{} << "Failed to create a new ring buffer!" << " ("
|
||||
<< result << ")";
|
||||
throw new std::runtime_error("Failed to create a new ring buffer! "
|
||||
"Check STDERR for more info.");
|
||||
}
|
||||
},
|
||||
Sound::SoundType::StreamedRawPCM,
|
||||
"Failed to create the sound from ring buffer: "));
|
||||
}
|
||||
|
||||
// Use case: 1 time set up and use audio
|
||||
SoundContainer Engine::CreateSound(uint8_t *data, int length) {
|
||||
return SoundContainer(new Sound(
|
||||
this,
|
||||
[data, length, channels = GetChannelCount(),
|
||||
sampleRate = GetSampleRate()](Sound *sound) {
|
||||
ma_audio_buffer_config config = ma_audio_buffer_config_init(
|
||||
ma_format_s32, channels, length / (4 * channels), data, nullptr);
|
||||
ma_result result =
|
||||
ma_audio_buffer_init_copy(&config, &sound->maAudioBuffer);
|
||||
|
||||
if (result != MA_SUCCESS) {
|
||||
Utility::Error{} << "Failed to create a new audio buffer!" << " ("
|
||||
<< result << ")";
|
||||
throw new std::runtime_error("Failed to create a new audio buffer! "
|
||||
"Check STDERR for more info.");
|
||||
}
|
||||
|
||||
sound->maConfig.pDataSource = &sound->maAudioBuffer;
|
||||
},
|
||||
Sound::SoundType::RawPCM,
|
||||
"Failed to create the sound from the PCM data: "));
|
||||
}
|
||||
|
||||
SoundContainer Engine::CreateSound(const std::string filepath,
|
||||
bool streamFile) {
|
||||
return SoundContainer(new Sound(
|
||||
this,
|
||||
[filepath, streamFile](Sound *sound) {
|
||||
sound->maConfig.pFilePath = filepath.c_str();
|
||||
sound->maConfig.flags = (streamFile ? MA_SOUND_FLAG_STREAM : 0);
|
||||
},
|
||||
Sound::SoundType::FromFile,
|
||||
"Failed to create the sound from the file: " + filepath));
|
||||
}
|
||||
|
||||
ListenerContainer Engine::CreateListener() {
|
||||
auto listener = ListenerContainer(new Listener());
|
||||
listener->baseEngine = this;
|
||||
listener->listenerID = listenerCounter++;
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
// Controls
|
||||
void Engine::SetVolume(float value) { ma_engine_set_volume(&maEngine, value); }
|
||||
float Engine::GetVolume() { return ma_engine_get_volume(&maEngine); }
|
||||
const float Engine::GetVolume() { return ma_engine_get_volume(&maEngine); }
|
||||
|
36
src/Listener.cpp
Normal file
36
src/Listener.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "ChargeAudio.hpp"
|
||||
|
||||
using namespace ChargeAudio;
|
||||
|
||||
Listener::Listener() {}
|
||||
|
||||
// Controls
|
||||
void Listener::SetEnabled(bool isEnabled) {
|
||||
ma_engine_listener_set_enabled(&baseEngine->maEngine, listenerID, isEnabled);
|
||||
}
|
||||
|
||||
const bool Listener::GetEnabled() {
|
||||
return ma_engine_listener_is_enabled(&baseEngine->maEngine, listenerID);
|
||||
}
|
||||
|
||||
void Listener::SetDirection(Magnum::Vector3 position) {
|
||||
ma_engine_listener_set_direction(&baseEngine->maEngine, listenerID,
|
||||
position.x(), position.y(), position.z());
|
||||
}
|
||||
|
||||
const Magnum::Vector3 Listener::GetDirection() {
|
||||
ma_vec3f dir =
|
||||
ma_engine_listener_get_direction(&baseEngine->maEngine, listenerID);
|
||||
return Magnum::Vector3{dir.x, dir.y, dir.z};
|
||||
}
|
||||
|
||||
void Listener::SetPosition(Magnum::Vector3 position) {
|
||||
ma_engine_listener_set_position(&baseEngine->maEngine, listenerID,
|
||||
position.x(), position.y(), position.z());
|
||||
}
|
||||
|
||||
const Magnum::Vector3 Listener::GetPosition() {
|
||||
ma_vec3f pos =
|
||||
ma_engine_listener_get_position(&baseEngine->maEngine, listenerID);
|
||||
return Magnum::Vector3(pos.x, pos.y, pos.z);
|
||||
}
|
@@ -1,22 +1,97 @@
|
||||
#include "ChargeAudio.hpp"
|
||||
#include <Magnum/Magnum.h>
|
||||
#include <Magnum/Math/Vector3.h>
|
||||
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace ChargeAudio;
|
||||
Sound::~Sound() { ma_sound_uninit(&maSound); }
|
||||
Sound::Sound(Engine *engine, std::function<void(Sound *)> setupFunction,
|
||||
SoundType soundType, std::string additionalErrorMessage)
|
||||
: baseEngine(engine) {
|
||||
maConfig = ma_sound_config_init_2(&baseEngine->maEngine);
|
||||
maConfig.endCallback = Sound::onSoundFinish;
|
||||
maConfig.pEndCallbackUserData = this;
|
||||
setupFunction(this);
|
||||
ma_result maResponse =
|
||||
ma_sound_init_ex(&baseEngine->maEngine, &maConfig, &maSound);
|
||||
if (maResponse != MA_SUCCESS) {
|
||||
Utility::Error{} << "Failed to create a new sound" << " (" << maResponse
|
||||
<< ")";
|
||||
throw new std::runtime_error(
|
||||
"Failed to create a new sound. Check STDERR for more info.\n" +
|
||||
additionalErrorMessage);
|
||||
}
|
||||
type = soundType;
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
switch (type) {
|
||||
case Sound::SoundType::RawPCM:
|
||||
ma_audio_buffer_uninit_and_free(&maAudioBuffer);
|
||||
break;
|
||||
case Sound::SoundType::StreamedRawPCM:
|
||||
ma_pcm_rb_uninit(&maRingBuffer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ma_sound_uninit(&maSound);
|
||||
}
|
||||
|
||||
const Sound::SoundState Sound::GetState() { return state; }
|
||||
const Sound::SoundType Sound::GetSoundType() { return type; }
|
||||
|
||||
const float Sound::GetDuration() {
|
||||
float time;
|
||||
ma_sound_get_length_in_seconds(&this->maSound, &time);
|
||||
return time;
|
||||
}
|
||||
|
||||
const float Sound::GetPlaybackTime() {
|
||||
float time;
|
||||
ma_sound_get_cursor_in_seconds(&this->maSound, &time);
|
||||
return time;
|
||||
}
|
||||
|
||||
// true or false depending on if the playback was set
|
||||
bool Sound::SetPlaybackTime(float time) {
|
||||
// Better to just catch it from the start
|
||||
if (time < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = ma_sound_seek_to_second(&maSound, time) != MA_SUCCESS;
|
||||
if (result) {
|
||||
Utility::Error{} << "Failed to set playback time to " << time
|
||||
<< " on a sound instance";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Controls
|
||||
void Sound::Play() { ma_sound_start(&maSound); }
|
||||
void Sound::Pause() { ma_sound_stop(&maSound); }
|
||||
void Sound::Play() {
|
||||
ma_sound_start(&maSound);
|
||||
state = Sound::SoundState::Playing;
|
||||
}
|
||||
|
||||
void Sound::Pause() {
|
||||
ma_sound_stop(&maSound);
|
||||
state = Sound::SoundState::Paused;
|
||||
}
|
||||
|
||||
void Sound::Reset() { ma_sound_seek_to_pcm_frame(&maSound, 0); }
|
||||
|
||||
void Sound::SetPosition(Magnum::Vector3 position) {
|
||||
ma_sound_set_position(&maSound, position.x(), position.y(), position.z());
|
||||
}
|
||||
Magnum::Vector3 Sound::GetPosition() {
|
||||
const Magnum::Vector3 Sound::GetPosition() {
|
||||
ma_vec3f pos = ma_sound_get_position(&maSound);
|
||||
Magnum::Vector3 position(pos.x, pos.y, pos.z);
|
||||
return position;
|
||||
return Magnum::Vector3(pos.x, pos.y, pos.z);
|
||||
}
|
||||
void Sound::SetVolume(float value) { ma_sound_set_volume(&maSound, value); }
|
||||
float Sound::GetVolume() { return ma_sound_get_volume(&maSound); }
|
||||
const float Sound::GetVolume() { return ma_sound_get_volume(&maSound); }
|
||||
|
||||
// STATICs
|
||||
void Sound::onSoundFinish(void *customData, ma_sound *) {
|
||||
auto sound = static_cast<Sound *>(customData);
|
||||
sound->state = SoundState::Finished;
|
||||
}
|
||||
|
Reference in New Issue
Block a user