Compare commits

..

22 Commits

Author SHA1 Message Date
cat
64a5d19464 Added RawPCM data that doesn't use ring buffer(for non-streaming), and proper deallocation 2025-09-21 03:41:40 +03:00
cat
090317b394 (Large) Added sample rate, channel count, RingBuffered Audio(Raw PCM), Sound types, and cleared headers 2025-09-21 03:06:17 +03:00
cat
4c580f4956 Made appropriate limitations to classes 2025-09-09 19:21:23 +03:00
cat
8b8c5310ae weird residue file 2025-09-09 14:57:35 +03:00
cat
7c5d80cd31 Added mosra's suggestions 2025-09-09 14:48:59 +03:00
cat
ff1241fe36 Added typedefs for containers 2025-09-09 12:31:24 +03:00
cat
57b8bfa719 Added set playback time 2025-09-09 02:45:42 +03:00
cat
640f6af291 Renamed runtime functions to make more sense 2025-09-09 02:31:40 +03:00
cat
0d2603abf0 Added runtime functions 2025-09-08 19:20:10 +03:00
cat
13a21387df Making sound constructor more generalised(probably will make it public soon) 2025-09-08 00:37:10 +03:00
cat
146bd49b36 Changing initing to be more standardised(needs to be refactored) 2025-09-07 22:39:17 +03:00
cat
71e4ff9317 Listener direction controls 2025-09-05 17:36:18 +03:00
cat
64b5a8af29 Detection for SoundState::Finished 2025-09-05 17:30:12 +03:00
cat
76b53b5f71 Sound has states on it now 2025-09-05 12:21:40 +03:00
cat
67e423a77d Added enabling to listeners 2025-09-05 12:16:47 +03:00
cat
a40c0665ac Added Listeners instead of single engine listener 2025-09-05 12:09:33 +03:00
cat
ee83818de2 Moved miniaudio to ma namespace. Added Engine destructor. Added Engine Listener Position. 2025-09-04 23:11:31 +03:00
cat
29bcf9fbae Replaced unique_ptr with Corrade::Containers::Pointer 2025-09-04 22:48:42 +03:00
cat
ead1e742d1 For now using unique_ptr and implementation of Sound() 2025-09-04 22:30:24 +03:00
cat
81cef5cc8a cleaning .cache 2025-09-04 21:56:30 +03:00
cat
b8cdd54888 updated .gitignore 2025-09-04 21:55:28 +03:00
cat
e24e3e6637 Made miniaudio anonymous so it doesn't go outside of translation unit 2025-09-04 21:54:52 +03:00
6 changed files with 294 additions and 42 deletions

1
.gitignore vendored
View File

@@ -47,3 +47,4 @@ CMakeUserPresets.json
*.app
build/
.cache/

View File

@@ -3,12 +3,12 @@ cmake_minimum_required(VERSION 3.10)
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()
add_subdirectory(lib/miniaudio)
find_package(Corrade REQUIRED Main)
@@ -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")
add_library(
ChargeAudio SHARED "src/ChargeAudio.hpp" "src/Engine.cpp" "src/Sound.cpp"
"src/Listener.cpp" "lib/miniaudio/miniaudio.c")
target_link_libraries(
ChargeAudio
@@ -28,7 +29,9 @@ target_link_libraries(
${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES} ${SWRESAMPLE_LIBRARIES})
target_include_directories(
ChargeAudio PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
ChargeAudio
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib>
$<INSTALL_INTERFACE:include>)
# Library
@@ -41,6 +44,7 @@ install(
# include
install(FILES src/ChargeAudio.hpp DESTINATION include/Charge)
install(FILES lib/miniaudio/miniaudio.h DESTINATION include/Charge/miniaudio/)
install(
EXPORT ChargeAudioTargets

View File

@@ -1,39 +1,118 @@
#ifndef CHARGE_AUDIO_BASE_H
#define CHARGE_AUDIO_BASE_H
#include "../lib/miniaudio/miniaudio.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 {
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
#endif

View File

@@ -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); }
// 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.");
}
return sound;
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
View 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);
}

View File

@@ -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;
}