Compare commits
5 Commits
5291b08330
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e9d4cc9fc5 | |||
| 1f0f193b54 | |||
| 020f9c5eef | |||
| 69fd698da1 | |||
| d996910515 |
@@ -16,8 +16,8 @@ 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 };
|
||||
enum class State { Idle, Playing, Paused, Finished };
|
||||
enum class Type { FromFile, StreamedRawPCM, RawPCM };
|
||||
// No copying
|
||||
Sound(const Sound &) = delete;
|
||||
Sound &operator=(const Sound &) = delete;
|
||||
@@ -31,8 +31,9 @@ public:
|
||||
void Pause();
|
||||
void Reset();
|
||||
|
||||
const SoundState GetState();
|
||||
const SoundType GetSoundType();
|
||||
const State GetState();
|
||||
const Type GetSoundType();
|
||||
const size_t GetPlayedSampleCount();
|
||||
|
||||
const float GetPlaybackTime();
|
||||
bool SetPlaybackTime(float time);
|
||||
@@ -42,9 +43,12 @@ public:
|
||||
void SetVolume(float value);
|
||||
const float GetVolume();
|
||||
|
||||
// For StreamedRawPCM
|
||||
void WriteToRingBuffer(uint8_t *data, uint32_t length);
|
||||
|
||||
private:
|
||||
Sound(class Engine *engine, std::function<void(Sound *)> setupFunction,
|
||||
SoundType type, std::string additionalErrorMessage = "");
|
||||
Type type, std::string additionalErrorMessage = "");
|
||||
static void onSoundFinish(void *customData, ma_sound *);
|
||||
|
||||
class Engine *baseEngine;
|
||||
@@ -52,8 +56,8 @@ private:
|
||||
ma_sound_config maConfig;
|
||||
ma_pcm_rb maRingBuffer;
|
||||
ma_audio_buffer maAudioBuffer;
|
||||
SoundState state = SoundState::Idle;
|
||||
SoundType type;
|
||||
State state = State::Idle;
|
||||
Type type;
|
||||
|
||||
friend class Engine;
|
||||
};
|
||||
@@ -110,12 +114,19 @@ private:
|
||||
ma_result maResponse;
|
||||
ma_decoder maStero;
|
||||
ma_uint64 listenerCounter = 0;
|
||||
ma_format PCMFormat = ma_format_f32;
|
||||
ma_uint8 frameSize;
|
||||
friend class Listener;
|
||||
friend class Sound;
|
||||
};
|
||||
|
||||
// For MA
|
||||
void ThrowOnRuntimeError(std::string message,
|
||||
ma_result errorType = ma_result::MA_ERROR);
|
||||
|
||||
// Generalised
|
||||
void ThrowOnRuntimeError(std::string message, bool comparison);
|
||||
void WarnIfTrue(std::string message, bool comparison = true);
|
||||
|
||||
} // namespace ChargeAudio
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@ Engine::Engine(uint32_t sampleRate, uint32_t channels) {
|
||||
maConfig = ma_engine_config_init();
|
||||
maConfig.sampleRate = sampleRate;
|
||||
maConfig.channels = channels;
|
||||
frameSize = ma_get_bytes_per_sample(PCMFormat) * channels;
|
||||
|
||||
ThrowOnRuntimeError("Failed to init miniaudio engine",
|
||||
ma_engine_init(&maConfig, &maEngine));
|
||||
@@ -27,11 +28,12 @@ SoundContainer Engine::CreateSound(int bufferLengthInSeconds) {
|
||||
[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);
|
||||
sound->baseEngine->PCMFormat, channels, sampleRate * length,
|
||||
nullptr, nullptr, &sound->maRingBuffer);
|
||||
ThrowOnRuntimeError("Failed to create a new ring buffer!", result);
|
||||
sound->maConfig.pDataSource = &sound->maRingBuffer;
|
||||
},
|
||||
Sound::SoundType::StreamedRawPCM,
|
||||
Sound::Type::StreamedRawPCM,
|
||||
"Failed to create the sound from ring buffer: "));
|
||||
}
|
||||
|
||||
@@ -42,15 +44,15 @@ SoundContainer Engine::CreateSound(uint8_t *data, int length) {
|
||||
[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);
|
||||
sound->baseEngine->PCMFormat, channels,
|
||||
length / sound->baseEngine->frameSize, data, nullptr);
|
||||
ma_result result =
|
||||
ma_audio_buffer_init_copy(&config, &sound->maAudioBuffer);
|
||||
|
||||
ThrowOnRuntimeError("Failed to create a new audio buffer!", result);
|
||||
sound->maConfig.pDataSource = &sound->maAudioBuffer;
|
||||
},
|
||||
Sound::SoundType::RawPCM,
|
||||
"Failed to create the sound from the PCM data: "));
|
||||
Sound::Type::RawPCM, "Failed to create the sound from the PCM data: "));
|
||||
}
|
||||
|
||||
SoundContainer Engine::CreateSound(const std::string filepath,
|
||||
@@ -61,7 +63,7 @@ SoundContainer Engine::CreateSound(const std::string filepath,
|
||||
sound->maConfig.pFilePath = filepath.c_str();
|
||||
sound->maConfig.flags = (streamFile ? MA_SOUND_FLAG_STREAM : 0);
|
||||
},
|
||||
Sound::SoundType::FromFile,
|
||||
Sound::Type::FromFile,
|
||||
"Failed to create the sound from the file: " + filepath));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,24 @@ void ChargeAudio::ThrowOnRuntimeError(std::string message,
|
||||
return;
|
||||
}
|
||||
|
||||
Utility::Error{} << message.c_str()
|
||||
<< magic_enum::enum_name(errorType).data();
|
||||
Utility::Error{} << Utility::Debug::color(Utility::Debug::Color::Red)
|
||||
<< "ERROR:" << Utility::Debug::resetColor << message.c_str()
|
||||
<< magic_enum::enum_name(errorType).data()
|
||||
<< "Execution will be stopped!\n";
|
||||
throw new std::runtime_error(message + " Check STDERR for more info.\n");
|
||||
}
|
||||
|
||||
void ChargeAudio::ThrowOnRuntimeError(std::string message, bool comparison) {
|
||||
if (!comparison) {
|
||||
return;
|
||||
}
|
||||
ThrowOnRuntimeError(message);
|
||||
}
|
||||
|
||||
void ChargeAudio::WarnIfTrue(std::string message, bool comparison) {
|
||||
if (comparison) {
|
||||
Utility::Debug{} << Utility::Debug::color(Utility::Debug::Color::Yellow)
|
||||
<< "WARNING:" << Utility::Debug::resetColor
|
||||
<< message.c_str() << "Execution will continue.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "ChargeAudio.hpp"
|
||||
|
||||
#include <Corrade/Utility/Debug.h>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
using namespace ChargeAudio;
|
||||
Sound::Sound(Engine *engine, std::function<void(Sound *)> setupFunction,
|
||||
SoundType soundType, std::string additionalErrorMessage)
|
||||
Type soundType, std::string additionalErrorMessage)
|
||||
: baseEngine(engine) {
|
||||
maConfig = ma_sound_config_init_2(&baseEngine->maEngine);
|
||||
maConfig.endCallback = Sound::onSoundFinish;
|
||||
@@ -21,10 +24,10 @@ Sound::Sound(Engine *engine, std::function<void(Sound *)> setupFunction,
|
||||
|
||||
Sound::~Sound() {
|
||||
switch (type) {
|
||||
case Sound::SoundType::RawPCM:
|
||||
case Sound::Type::RawPCM:
|
||||
ma_audio_buffer_uninit_and_free(&maAudioBuffer);
|
||||
break;
|
||||
case Sound::SoundType::StreamedRawPCM:
|
||||
case Sound::Type::StreamedRawPCM:
|
||||
ma_pcm_rb_uninit(&maRingBuffer);
|
||||
break;
|
||||
default:
|
||||
@@ -33,16 +36,24 @@ Sound::~Sound() {
|
||||
ma_sound_uninit(&maSound);
|
||||
}
|
||||
|
||||
const Sound::SoundState Sound::GetState() { return state; }
|
||||
const Sound::SoundType Sound::GetSoundType() { return type; }
|
||||
const Sound::State Sound::GetState() { return state; }
|
||||
const Sound::Type Sound::GetSoundType() { return type; }
|
||||
|
||||
const float Sound::GetDuration() {
|
||||
WarnIfTrue("You are using StreamedRawPCM sound! GetDuration() will not work "
|
||||
"since the PCM data is in a fixed ring buffer.",
|
||||
type == Sound::Type::StreamedRawPCM);
|
||||
float time;
|
||||
ma_sound_get_length_in_seconds(&this->maSound, &time);
|
||||
return time;
|
||||
}
|
||||
|
||||
const float Sound::GetPlaybackTime() {
|
||||
WarnIfTrue(
|
||||
"You are using StreamedRawPCM sound! GetPlaybackTime() will not work "
|
||||
"since the PCM data is in a fixed ring buffer. However you can do "
|
||||
"GetPlayedSampleCount()/SAMPLE_RATE to get time elapsed.",
|
||||
type == Sound::Type::StreamedRawPCM);
|
||||
float time;
|
||||
ma_sound_get_cursor_in_seconds(&this->maSound, &time);
|
||||
return time;
|
||||
@@ -50,6 +61,12 @@ const float Sound::GetPlaybackTime() {
|
||||
|
||||
// true or false depending on if the playback was set
|
||||
bool Sound::SetPlaybackTime(float time) {
|
||||
ThrowOnRuntimeError(
|
||||
"You cannot set playback time on a StreamedRawPCM as it uses a ring "
|
||||
"buffer. If you wanted to skip forward or backward. Empty the buffer and "
|
||||
"then write what you want.",
|
||||
type == Sound::Type::StreamedRawPCM);
|
||||
|
||||
// Better to just catch it from the start
|
||||
if (time < 0) {
|
||||
return false;
|
||||
@@ -66,24 +83,27 @@ bool Sound::SetPlaybackTime(float time) {
|
||||
// Controls
|
||||
void Sound::Play() {
|
||||
ThrowOnRuntimeError("Failed to start the sound.", ma_sound_start(&maSound));
|
||||
state = Sound::SoundState::Playing;
|
||||
state = Sound::State::Playing;
|
||||
}
|
||||
|
||||
void Sound::Pause() {
|
||||
ThrowOnRuntimeError("Failed to pause the sound.", ma_sound_stop(&maSound));
|
||||
state = Sound::SoundState::Paused;
|
||||
state = Sound::State::Paused;
|
||||
}
|
||||
|
||||
void Sound::Reset() {
|
||||
if (type == SoundType::StreamedRawPCM) {
|
||||
ThrowOnRuntimeError(
|
||||
"You cannot reset on Streamed RawPCM sounds! Since the buffer is a "
|
||||
"ring buffer there isn't a \"start\" to return to.");
|
||||
}
|
||||
|
||||
ThrowOnRuntimeError(
|
||||
"You cannot reset on Streamed RawPCM sounds! Since the buffer is a "
|
||||
"ring buffer there isn't a \"start\" to return to.",
|
||||
type == Type::StreamedRawPCM);
|
||||
ma_sound_seek_to_pcm_frame(&maSound, 0);
|
||||
}
|
||||
|
||||
// Used with Streamed PCM but works with anything
|
||||
const size_t Sound::GetPlayedSampleCount() {
|
||||
return ma_sound_get_time_in_pcm_frames(&maSound);
|
||||
}
|
||||
|
||||
void Sound::SetPosition(Magnum::Vector3 position) {
|
||||
ma_sound_set_position(&maSound, position.x(), position.y(), position.z());
|
||||
}
|
||||
@@ -94,8 +114,32 @@ const Magnum::Vector3 Sound::GetPosition() {
|
||||
void Sound::SetVolume(float value) { ma_sound_set_volume(&maSound, value); }
|
||||
const float Sound::GetVolume() { return ma_sound_get_volume(&maSound); }
|
||||
|
||||
// StreamedRawPCM
|
||||
void Sound::WriteToRingBuffer(uint8_t *data, uint32_t length) {
|
||||
uint8_t *dataPosition = data;
|
||||
void *writePosition;
|
||||
uint32_t framesToWrite = length / baseEngine->frameSize, framesWritten = 0,
|
||||
framesAvailable = 0;
|
||||
|
||||
while (framesWritten < framesToWrite) {
|
||||
framesAvailable = framesToWrite - framesWritten;
|
||||
|
||||
// Sometimes this can fail. Actually terrifying.
|
||||
ThrowOnRuntimeError("PCM ring buffer could not allocate... Oh no",
|
||||
ma_pcm_rb_acquire_write(&maRingBuffer, &framesAvailable,
|
||||
&writePosition));
|
||||
|
||||
memcpy(writePosition, dataPosition,
|
||||
framesAvailable * baseEngine->frameSize);
|
||||
ma_pcm_rb_commit_write(&maRingBuffer, framesAvailable);
|
||||
|
||||
framesWritten += framesAvailable;
|
||||
dataPosition += framesAvailable * baseEngine->frameSize;
|
||||
}
|
||||
}
|
||||
|
||||
// STATICs
|
||||
void Sound::onSoundFinish(void *customData, ma_sound *) {
|
||||
auto sound = static_cast<Sound *>(customData);
|
||||
sound->state = SoundState::Finished;
|
||||
sound->state = State::Finished;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user