mirror of
https://github.com/korenkonder/ReDIVA.git
synced 2026-10-04 20:58:03 +03:00
2156 lines
67 KiB
C++
2156 lines
67 KiB
C++
/*
|
|
by korenkonder
|
|
GitHub/GitLab: korenkonder
|
|
*/
|
|
|
|
#include "sound.hpp"
|
|
#include "../KKdLib/io/file_stream.hpp"
|
|
#include "../KKdLib/key_val.hpp"
|
|
#include "../KKdLib/str_utils.hpp"
|
|
#include "../KKdLib/time.hpp"
|
|
#include "../KKdLib/vec.hpp"
|
|
#include "data.hpp"
|
|
#include "ogg_vorbis.hpp"
|
|
#include <functiondiscoverykeys_devpkey.h>
|
|
#include <timeapi.h>
|
|
|
|
struct ima_storage {
|
|
int32_t step_index;
|
|
int32_t current;
|
|
|
|
inline ima_storage() : step_index(), current() {
|
|
|
|
}
|
|
};
|
|
|
|
struct SoundCueQueueVolume {
|
|
float_t min;
|
|
float_t max;
|
|
};
|
|
|
|
struct sound_stream {
|
|
std::string path;
|
|
OggPlayback* ogg_playback;
|
|
float_t current_volume;
|
|
float_t duration;
|
|
float_t time;
|
|
bool pause;
|
|
uint32_t file_loading_frames;
|
|
uint32_t file_ready_frames;
|
|
float_t load_time_seek;
|
|
int32_t volume_trans;
|
|
float_t target_volume;
|
|
int32_t state;
|
|
int32_t play_state;
|
|
|
|
sound_stream();
|
|
~sound_stream();
|
|
|
|
bool check_state();
|
|
void ctrl();
|
|
void ctrl_playback();
|
|
void reset();
|
|
void set_current_volume(float_t value);
|
|
bool set_path(const char* path, bool pause = false);
|
|
bool set_path(const char* path, float_t time, bool pause = false);
|
|
bool set_path_playback();
|
|
bool set_pause(bool value);
|
|
void set_target_volume(float_t value, int32_t frames);
|
|
bool stop();
|
|
bool stop_playback();
|
|
};
|
|
|
|
static void sound_stream_array_init();
|
|
static void sound_stream_array_ctrl();
|
|
static sound_stream* sound_stream_array_get(int32_t index);
|
|
static void sound_stream_array_free();
|
|
|
|
SoundWork* sound_work;
|
|
sound::wasapi::System* sound_wasapi_system_data;
|
|
std::map<std::string, WaveAudio> wave_audio_storage_data;
|
|
|
|
static int32_t sound_cue_counter = 0;
|
|
static int32_t sound_wasapi_system_counter = 0;
|
|
|
|
static sound_stream* sound_stream_array;
|
|
static float_t sound_stream_volume = 1.0f;
|
|
|
|
static const int8_t ima_index_table[] = {
|
|
-1, -1, -1, -1, 2, 4, 6, 8,
|
|
-1, -1, -1, -1, 2, 4, 6, 8,
|
|
};
|
|
|
|
static const int16_t ima_step_table[] = {
|
|
7, 8, 9, 10, 11, 12, 13, 14,
|
|
16, 17, 19, 21, 23, 25, 28, 31,
|
|
34, 37, 41, 45, 50, 55, 60, 66,
|
|
73, 80, 88, 97, 107, 118, 130, 143,
|
|
157, 173, 190, 209, 230, 253, 279, 307,
|
|
337, 371, 408, 449, 494, 544, 598, 658,
|
|
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
|
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
|
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
|
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
|
32767
|
|
};
|
|
|
|
static const SoundCueQueueVolume sound_cue_queue_volume_array[SOUND_WORK_SE_QUEUE_COUNT] = {
|
|
{ 0.0f, 1.0f, },
|
|
{ 0.0f, 1.0f, },
|
|
{ 0.0f, 1.0f, },
|
|
{ 0.3f, 1.0f, },
|
|
{ 0.3f, 1.0f, },
|
|
};
|
|
|
|
static size_t ima_decode(int16_t* dst, size_t dst_size, uint8_t* data, size_t samples_count,
|
|
size_t channels, ima_storage* storage, size_t storage_size);
|
|
|
|
namespace sound {
|
|
namespace wasapi {
|
|
System::System() : wave_format(), pEnumerator(), pDevice(), pAudioClient(),
|
|
samples_count(), pRenderClient(), pClockAdjustment(), hEvent(),
|
|
channels(), sample_rate(), bit_depth(), mixer(), thread(), format() {
|
|
|
|
}
|
|
|
|
System::~System() {
|
|
Reset();
|
|
}
|
|
|
|
void System::Init(size_t se_channels_count, size_t streaming_channels_count,
|
|
bool separate_speakers_headphones) {
|
|
if (FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator),
|
|
0, 1, __uuidof(IMMDeviceEnumerator), (LPVOID*)&pEnumerator))
|
|
|| FAILED(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice)))
|
|
return;
|
|
|
|
if (FAILED(pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient)))
|
|
return;
|
|
|
|
WAVEFORMATEXTENSIBLE* mix_format;
|
|
if (FAILED(pAudioClient->GetMixFormat((WAVEFORMATEX**)&mix_format)))
|
|
return;
|
|
|
|
int32_t channels = separate_speakers_headphones ? 4 : 2;
|
|
int32_t sample_rate;
|
|
int32_t bit_depth;
|
|
if (mix_format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
wave_format = *mix_format;
|
|
sample_rate = mix_format->Format.nSamplesPerSec;
|
|
bit_depth = mix_format->Samples.wValidBitsPerSample;
|
|
}
|
|
else {
|
|
wave_format.Format = mix_format->Format;
|
|
sample_rate = mix_format->Format.nSamplesPerSec;
|
|
bit_depth = 16;
|
|
}
|
|
|
|
if (mix_format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE
|
|
&& wave_format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) {
|
|
bit_depth = 32;
|
|
format = AUDIO_FORMAT_F32;
|
|
wave_format.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
|
}
|
|
else {
|
|
switch (bit_depth) {
|
|
default:
|
|
bit_depth = 16;
|
|
case 16:
|
|
format = AUDIO_FORMAT_I16;
|
|
break;
|
|
case 24:
|
|
format = AUDIO_FORMAT_I24;
|
|
break;
|
|
case 32:
|
|
format = AUDIO_FORMAT_I32;
|
|
break;
|
|
}
|
|
|
|
wave_format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
}
|
|
|
|
wave_format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
wave_format.Format.nChannels = (WORD)channels;
|
|
wave_format.Format.nSamplesPerSec = (DWORD)sample_rate;
|
|
wave_format.Format.nAvgBytesPerSec = (DWORD)(sample_rate * (bit_depth / 8) * channels);
|
|
wave_format.Format.nBlockAlign = (WORD)((bit_depth / 8) * channels);
|
|
wave_format.Format.wBitsPerSample = (WORD)bit_depth;
|
|
wave_format.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
|
wave_format.Samples.wValidBitsPerSample = (WORD)bit_depth;
|
|
wave_format.dwChannelMask = channels == 4 ? KSAUDIO_SPEAKER_QUAD : KSAUDIO_SPEAKER_STEREO;
|
|
|
|
WAVEFORMATEXTENSIBLE* closest_format;
|
|
if (FAILED(pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
|
|
&wave_format.Format, (WAVEFORMATEX**)&closest_format))) {
|
|
wave_format = {};
|
|
return;
|
|
}
|
|
|
|
this->channels = channels;
|
|
this->sample_rate = sample_rate;
|
|
this->bit_depth = bit_depth;
|
|
|
|
mixer = new Mixer(this);
|
|
if (!mixer)
|
|
return;
|
|
|
|
REFERENCE_TIME hnsPeriod;
|
|
if (FAILED(pAudioClient->GetDevicePeriod(&hnsPeriod, 0)))
|
|
return;
|
|
|
|
if (pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
|
|
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
|
| AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
|
hnsPeriod, 0, &wave_format.Format, 0) == AUDCLNT_ERR(0x019)) {
|
|
if (FAILED(pAudioClient->GetBufferSize(&samples_count)))
|
|
return;
|
|
|
|
pAudioClient->Release();
|
|
|
|
if (FAILED(pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient)))
|
|
return;
|
|
|
|
hnsPeriod = (REFERENCE_TIME)((double_t)(int32_t)samples_count
|
|
* 10000000.0 / (double_t)sample_rate + 0.5);
|
|
|
|
if (FAILED(pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
|
|
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
|
| AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
|
hnsPeriod, 0, &wave_format.Format, 0)))
|
|
return;
|
|
}
|
|
|
|
if (FAILED(pAudioClient->GetBufferSize(&samples_count)))
|
|
return;
|
|
|
|
// Rounding to prev base 2
|
|
uint32_t _samples_count = samples_count;
|
|
_samples_count--;
|
|
_samples_count |= _samples_count >> 1;
|
|
_samples_count |= _samples_count >> 2;
|
|
_samples_count |= _samples_count >> 4;
|
|
_samples_count |= _samples_count >> 8;
|
|
_samples_count |= _samples_count >> 16;
|
|
_samples_count++;
|
|
while (_samples_count + (_samples_count >> 1) >= samples_count)
|
|
_samples_count >>= 1;
|
|
samples_count = _samples_count;
|
|
|
|
if (!mixer->Init(se_channels_count, streaming_channels_count, samples_count))
|
|
return;
|
|
|
|
hEvent = CreateEventW(0, 0, 0, 0);
|
|
if (!hEvent)
|
|
return;
|
|
|
|
thread = new std::thread(System::ThreadMain, this);
|
|
if (thread) {
|
|
wchar_t buf[0x80];
|
|
swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"sound::wasapi::System #%d", sound_wasapi_system_counter++);
|
|
SetThreadDescription((HANDLE)thread->native_handle(), buf);
|
|
}
|
|
|
|
if (!thread || !SetThreadPriority((HANDLE)thread->native_handle(), THREAD_PRIORITY_HIGHEST))
|
|
return;
|
|
|
|
if (FAILED(pAudioClient->SetEventHandle(hEvent)))
|
|
return;
|
|
|
|
if (SUCCEEDED(pAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)&pRenderClient))) {
|
|
void* data = 0;
|
|
if (SUCCEEDED(pRenderClient->GetBuffer(samples_count, (BYTE**)&data))
|
|
&& SUCCEEDED(pRenderClient->ReleaseBuffer(samples_count, AUDCLNT_BUFFERFLAGS_SILENT)))
|
|
pAudioClient->Start();
|
|
}
|
|
|
|
if (SUCCEEDED(pAudioClient->GetService(__uuidof(IAudioClockAdjustment), (void**)&pClockAdjustment)))
|
|
pClockAdjustment->SetSampleRate((float_t)44100);
|
|
}
|
|
|
|
void System::Reset() {
|
|
if (thread) {
|
|
thread_state.set(1);
|
|
thread->join();
|
|
delete thread;
|
|
thread = 0;
|
|
}
|
|
|
|
if (pAudioClient)
|
|
pAudioClient->Stop();
|
|
|
|
if (pClockAdjustment) {
|
|
pClockAdjustment->Release();
|
|
pClockAdjustment = 0;
|
|
}
|
|
|
|
if (pRenderClient) {
|
|
pRenderClient->Release();
|
|
pRenderClient = 0;
|
|
}
|
|
|
|
if (pAudioClient) {
|
|
pAudioClient->Release();
|
|
pAudioClient = 0;
|
|
}
|
|
|
|
if (pDevice) {
|
|
pDevice->Release();
|
|
pDevice = 0;
|
|
}
|
|
|
|
if (pEnumerator) {
|
|
pEnumerator->Release();
|
|
pEnumerator = 0;
|
|
}
|
|
|
|
if (hEvent) {
|
|
CloseHandle(hEvent);
|
|
hEvent = 0;
|
|
}
|
|
|
|
if (mixer) {
|
|
delete mixer;
|
|
mixer = 0;
|
|
}
|
|
}
|
|
|
|
void System::ThreadMain(System* system) {
|
|
if (!system)
|
|
return;
|
|
|
|
while (!system->thread_state.get()) {
|
|
WaitForSingleObject(system->hEvent, -1);
|
|
void* data = 0;
|
|
if (SUCCEEDED(system->pRenderClient->GetBuffer(system->samples_count, (BYTE**)&data))) {
|
|
if (system->mixer && data)
|
|
system->mixer->FillBuffer(data,
|
|
system->samples_count, false, false, system->format);
|
|
|
|
if (FAILED(system->pRenderClient->ReleaseBuffer(system->samples_count, 0)))
|
|
break;
|
|
}
|
|
}
|
|
system->thread_state.set(0);
|
|
}
|
|
|
|
Mixer::Mixer(System* system) : se_channels(), se_channels_count(),
|
|
streaming_channels(), streaming_channels_count(), mix_buffer(), mix_buffer_size() {
|
|
this->system = system;
|
|
master_volume = 1.0f;
|
|
channels_volume[0] = 1.0f;
|
|
channels_volume[1] = 1.0f;
|
|
channels_volume[2] = 1.0f;
|
|
channels_volume[3] = 1.0f;
|
|
}
|
|
|
|
Mixer::~Mixer() {
|
|
Reset();
|
|
}
|
|
|
|
void Mixer::FillBuffer(void* buffer, size_t samples_count,
|
|
bool disable_headphones_volume, bool invert_phase, AudioFormat format) {
|
|
if (!system)
|
|
return;
|
|
|
|
vec4 spk_hph_volume = 0.0f;
|
|
|
|
{
|
|
std::unique_lock<std::mutex> u_lock(volume_mtx);
|
|
spk_hph_volume = master_volume * *(vec4*)channels_volume;
|
|
}
|
|
|
|
if (disable_headphones_volume)
|
|
*(vec2*)&spk_hph_volume.z = *(vec2*)&spk_hph_volume.x;
|
|
else if (invert_phase)
|
|
spk_hph_volume = -spk_hph_volume;
|
|
|
|
memset(mix_buffer, 0, mix_buffer_size);
|
|
if (streaming_channels)
|
|
for (size_t i = 0; i < streaming_channels_count; i++)
|
|
streaming_channels[i].FillBuffer(mix_buffer, samples_count, 1.0f);
|
|
|
|
if (se_channels)
|
|
for (size_t i = 0; i < se_channels_count; i++)
|
|
se_channels[i].FillBuffer(mix_buffer, samples_count, 1.0f);
|
|
|
|
sound_buffer_data* _mix_buffer = mix_buffer;
|
|
switch (format) {
|
|
case AUDIO_FORMAT_I16: {
|
|
int16_t* _buffer = (int16_t*)buffer;
|
|
switch (system->channels) {
|
|
case 2:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++) {
|
|
vec2 hph = *(vec2*)&_mix_buffer->hph_l * *(vec2*)&spk_hph_volume.z * (float_t)0x7FFF;
|
|
vec2i hph_i32;
|
|
vec2_to_vec2i(hph, hph_i32);
|
|
*(vec2i*)_buffer = vec2i::clamp(hph_i32, -0x8000, 0x7FFF);
|
|
_buffer += 2;
|
|
}
|
|
break;
|
|
case 4:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++) {
|
|
vec4 spk_hph = *(vec4*)_mix_buffer * spk_hph_volume * (float_t)0x7FFF;
|
|
vec4i spk_hph_i32;
|
|
vec4_to_vec4i(spk_hph, spk_hph_i32);
|
|
*(vec4i*)_buffer = vec4i::clamp(spk_hph_i32, -0x8000, 0x7FFF);
|
|
_buffer += 4;
|
|
}
|
|
break;
|
|
}
|
|
} break;
|
|
case AUDIO_FORMAT_I24: {
|
|
int8_t* _buffer = (int8_t*)buffer;
|
|
switch (system->channels) {
|
|
case 2:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++) {
|
|
vec2 hph = *(vec2*)&_mix_buffer->hph_l * *(vec2*)&spk_hph_volume.z * (float_t)0x7FFFFF;
|
|
vec2i hph_i32;
|
|
vec2_to_vec2i(hph, hph_i32);
|
|
hph_i32 = vec2i::clamp(hph_i32, -0x800000, 0x7FFFFF);
|
|
*(uint16_t*)&_buffer[0] = (uint16_t)hph_i32.x;
|
|
*(uint8_t*)&_buffer[2] = (uint8_t)(hph_i32.x >> 16);
|
|
*(uint16_t*)&_buffer[3] = (uint16_t)hph_i32.y;
|
|
*(uint8_t*)&_buffer[5] = (uint8_t)(hph_i32.y >> 16);
|
|
_buffer += 6;
|
|
}
|
|
break;
|
|
case 4:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++) {
|
|
vec4 spk_hph = *(vec4*)_mix_buffer * spk_hph_volume * (float_t)0x7FFFFF;
|
|
vec4i spk_hph_i32;
|
|
vec4_to_vec4i(spk_hph, spk_hph_i32);
|
|
spk_hph_i32 = vec4i::clamp(spk_hph_i32, -0x800000, 0x7FFFFF);
|
|
*(uint16_t*)&_buffer[0] = (uint16_t)spk_hph_i32.x;
|
|
*(uint8_t*)&_buffer[2] = (uint8_t)(spk_hph_i32.x >> 16);
|
|
*(uint16_t*)&_buffer[3] = (uint16_t)spk_hph_i32.y;
|
|
*(uint8_t*)&_buffer[5] = (uint8_t)(spk_hph_i32.y >> 16);
|
|
*(uint16_t*)&_buffer[6] = (uint16_t)spk_hph_i32.z;
|
|
*(uint8_t*)&_buffer[8] = (uint8_t)(spk_hph_i32.z >> 16);
|
|
*(uint16_t*)&_buffer[9] = (uint16_t)spk_hph_i32.w;
|
|
*(uint8_t*)&_buffer[11] = (uint8_t)(spk_hph_i32.w >> 16);
|
|
_buffer += 12;
|
|
}
|
|
break;
|
|
}
|
|
} break;
|
|
case AUDIO_FORMAT_I32: {
|
|
int32_t* _buffer = (int32_t*)buffer;
|
|
switch (system->channels) {
|
|
case 2:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++) {
|
|
vec2 hph = *(vec2*)&_mix_buffer->hph_l * *(vec2*)&spk_hph_volume.z;
|
|
int64_t hph_l = (int64_t)((double_t)hph.x * (double_t)0x7FFFFFFF);
|
|
int64_t hph_r = (int64_t)((double_t)hph.y * (double_t)0x7FFFFFFF);
|
|
*_buffer++ = (int32_t)clamp_def(hph_l, -0x80000000LL, 0x7FFFFFFFLL);
|
|
*_buffer++ = (int32_t)clamp_def(hph_r, -0x80000000LL, 0x7FFFFFFFLL);
|
|
}
|
|
break;
|
|
case 4:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++) {
|
|
vec4 spk_hph = *(vec4*)_mix_buffer * spk_hph_volume;
|
|
int64_t spk_l = (int64_t)((double_t)spk_hph.x * (double_t)0x7FFFFFFF);
|
|
int64_t spk_r = (int64_t)((double_t)spk_hph.y * (double_t)0x7FFFFFFF);
|
|
int64_t hph_l = (int64_t)((double_t)spk_hph.z * (double_t)0x7FFFFFFF);
|
|
int64_t hph_r = (int64_t)((double_t)spk_hph.w * (double_t)0x7FFFFFFF);
|
|
*_buffer++ = (int32_t)clamp_def(spk_l, -0x80000000LL, 0x7FFFFFFFLL);
|
|
*_buffer++ = (int32_t)clamp_def(spk_r, -0x80000000LL, 0x7FFFFFFFLL);
|
|
*_buffer++ = (int32_t)clamp_def(hph_l, -0x80000000LL, 0x7FFFFFFFLL);
|
|
*_buffer++ = (int32_t)clamp_def(hph_r, -0x80000000LL, 0x7FFFFFFFLL);
|
|
}
|
|
break;
|
|
}
|
|
} break;
|
|
case AUDIO_FORMAT_F32: {
|
|
float_t* _buffer = (float_t*)buffer;
|
|
switch (system->channels) {
|
|
case 2:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++, _buffer += 2)
|
|
*(vec2*)_buffer = *(vec2*)&_mix_buffer->hph_l * *(vec2*)&spk_hph_volume.z;
|
|
break;
|
|
case 4:
|
|
for (size_t i = samples_count; i; i--, _mix_buffer++, _buffer += 4)
|
|
*(vec4*)_buffer = *(vec4*)&_mix_buffer * spk_hph_volume;
|
|
break;
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
SEChannel* Mixer::GetSEChannel(size_t channel) {
|
|
if (channel < se_channels_count && se_channels)
|
|
return &se_channels[channel];
|
|
return 0;
|
|
}
|
|
|
|
StreamingChannel* Mixer::GetStreamingChannel(size_t channel) {
|
|
if (channel < streaming_channels_count && streaming_channels)
|
|
return &streaming_channels[channel];
|
|
return 0;
|
|
}
|
|
|
|
bool Mixer::Init(size_t se_channels_count, size_t streaming_channels_count, size_t samples_count) {
|
|
if (this->se_channels_count || this->streaming_channels_count) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
mix_buffer_size = sizeof(sound_buffer_data) * samples_count;
|
|
mix_buffer = (sound_buffer_data*)malloc(mix_buffer_size);
|
|
if (!mix_buffer) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
memset(mix_buffer, 0, mix_buffer_size);
|
|
|
|
if (se_channels_count) {
|
|
se_channels = new SEChannel[se_channels_count];
|
|
if (!se_channels) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < se_channels_count; i++)
|
|
se_channels[i].Init(this);
|
|
}
|
|
this->se_channels_count = se_channels_count;
|
|
|
|
if (streaming_channels_count) {
|
|
streaming_channels = new StreamingChannel[streaming_channels_count];
|
|
if (!streaming_channels) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < streaming_channels_count; i++)
|
|
streaming_channels[i].Init(this, samples_count);
|
|
}
|
|
this->streaming_channels_count = streaming_channels_count;
|
|
return true;
|
|
}
|
|
|
|
void Mixer::Reset() {
|
|
if (streaming_channels) {
|
|
for (size_t i = 0; i < streaming_channels_count; i++)
|
|
streaming_channels[i].Reset();
|
|
|
|
delete[] streaming_channels;
|
|
streaming_channels = 0;
|
|
}
|
|
streaming_channels_count = 0;
|
|
|
|
if (se_channels) {
|
|
for (size_t i = 0; i < se_channels_count; i++)
|
|
se_channels[i].Reset();
|
|
|
|
delete[] se_channels;
|
|
se_channels = 0;
|
|
}
|
|
se_channels_count = 0;
|
|
|
|
if (mix_buffer) {
|
|
free(mix_buffer);
|
|
mix_buffer = 0;
|
|
}
|
|
mix_buffer_size = 0;
|
|
}
|
|
|
|
void Mixer::SetChannelsVolume(int32_t mask, float_t value) {
|
|
std::unique_lock<std::mutex> u_lock(volume_mtx);
|
|
for (int32_t i = 0; i < 4; i++)
|
|
if (mask & (1 << i))
|
|
channels_volume[i] = value;
|
|
}
|
|
|
|
void Mixer::SetMasterVolume(float_t value) {
|
|
std::unique_lock<std::mutex> u_lock(volume_mtx);
|
|
master_volume = value;
|
|
}
|
|
|
|
SEChannel::SEChannel() : mixer(), buffer(), buffer_size(), channels(),
|
|
samples_count(), sample_rate(), loop_start(), loop_end(), current_sample() {
|
|
master_volume = 1.0f;
|
|
channels_volume[0] = 1.0f;
|
|
channels_volume[1] = 1.0f;
|
|
channels_volume[2] = 1.0f;
|
|
channels_volume[3] = 1.0f;
|
|
}
|
|
|
|
SEChannel::~SEChannel() {
|
|
Reset();
|
|
}
|
|
|
|
void SEChannel::FillBuffer(sound_buffer_data* buffer, size_t samples_count, float_t volume) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
if (!play_state.get() || !buffer)
|
|
return;
|
|
|
|
float_t master_volume = this->master_volume;
|
|
float_t spk_l_volume = master_volume * channels_volume[0];
|
|
float_t spk_r_volume = master_volume * channels_volume[1];
|
|
float_t hph_l_volume = master_volume * channels_volume[2];
|
|
float_t hph_r_volume = master_volume * channels_volume[3];
|
|
|
|
vec4 channels_volume(spk_l_volume, spk_r_volume, hph_l_volume, hph_r_volume);
|
|
|
|
float_t* se_buffer = this->buffer;
|
|
size_t _samples_count = this->samples_count;
|
|
size_t loop_start = this->loop_start;
|
|
size_t loop_end = this->loop_end;
|
|
size_t current_sample = this->current_sample;
|
|
bool loop = !!(loop_start || loop_end);
|
|
if (channels == 1)
|
|
for (size_t i = samples_count; i && current_sample < _samples_count; i--, buffer++) {
|
|
float_t* _buffer = se_buffer + current_sample;
|
|
*(vec4*)buffer = channels_volume * _buffer[0] + *(vec4*)buffer;
|
|
|
|
current_sample++;
|
|
if (loop && current_sample > loop_end)
|
|
current_sample = loop_start;
|
|
}
|
|
else if (channels == 2)
|
|
for (size_t i = samples_count; i && current_sample < _samples_count; i--, buffer++) {
|
|
float_t* _buffer = se_buffer + current_sample * 2;
|
|
*(vec4*)buffer = channels_volume * vec4(_buffer[0], _buffer[1],
|
|
_buffer[0], _buffer[1]) + *(vec4*)buffer;
|
|
|
|
current_sample++;
|
|
if (loop && current_sample > loop_end)
|
|
current_sample = loop_start;
|
|
}
|
|
else if (channels == 4)
|
|
for (size_t i = samples_count; i && current_sample < _samples_count; i--, buffer++) {
|
|
float_t* _buffer = se_buffer + current_sample * 4;
|
|
*(vec4*)buffer = channels_volume * *(vec4*)_buffer + *(vec4*)buffer;
|
|
|
|
current_sample++;
|
|
if (loop && current_sample > loop_end)
|
|
current_sample = loop_start;
|
|
}
|
|
|
|
this->current_sample = current_sample;
|
|
|
|
if (current_sample >= _samples_count)
|
|
ResetData();
|
|
}
|
|
|
|
bool SEChannel::Init(Mixer* mixer) {
|
|
bool ret = false;
|
|
{
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
if (!this->mixer) {
|
|
this->mixer = mixer;
|
|
buffer = 0;
|
|
buffer_size = 0;
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
if (!ret)
|
|
Reset();
|
|
return ret;
|
|
}
|
|
|
|
float_t* SEChannel::InitBuffer(size_t channels, size_t samples_count, size_t sample_rate) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
ResetData();
|
|
buffer = (float_t*)malloc(sizeof(float_t) * samples_count * channels);
|
|
if (!buffer)
|
|
return 0;
|
|
|
|
buffer_size = sizeof(float_t) * samples_count * channels;
|
|
memset(buffer, 0, buffer_size);
|
|
this->channels = channels;
|
|
this->sample_rate = sample_rate;
|
|
this->samples_count = samples_count;
|
|
loop_start = 0;
|
|
loop_end = 0;
|
|
return buffer;
|
|
}
|
|
|
|
bool SEChannel::Play(size_t loop_start, size_t loop_end) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
if (!buffer)
|
|
return false;
|
|
|
|
this->loop_start = loop_start;
|
|
this->loop_end = loop_end;
|
|
current_sample = 0;
|
|
play_state.set(1);
|
|
return true;
|
|
}
|
|
|
|
void SEChannel::Reset() {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
ResetData();
|
|
}
|
|
|
|
void SEChannel::ResetData() {
|
|
play_state.set(0);
|
|
if (buffer) {
|
|
free(buffer);
|
|
buffer = 0;
|
|
}
|
|
buffer_size = 0;
|
|
channels = 0;
|
|
samples_count = 0;
|
|
sample_rate = 0;
|
|
loop_start = 0;
|
|
loop_end = 0;
|
|
current_sample = 0;
|
|
}
|
|
|
|
void SEChannel::ResetDataProt() {
|
|
if (!play_state.get())
|
|
return;
|
|
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
ResetData();
|
|
}
|
|
|
|
void SEChannel::SetChannelsVolume(int32_t mask, float_t value) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
for (int32_t i = 0; i < 4; i++)
|
|
if (mask & (1 << i))
|
|
channels_volume[i] = value;
|
|
}
|
|
|
|
void SEChannel::SetMasterVolume(float_t value) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
master_volume = value;
|
|
}
|
|
|
|
StreamingChannel::StreamingChannel() : mixer(), buffer(),
|
|
buffer_size(), callback_func(), callback_data() {
|
|
master_volume = 1.0f;
|
|
channels_volume[0] = 1.0f;
|
|
channels_volume[1] = 1.0f;
|
|
channels_volume[2] = 1.0f;
|
|
channels_volume[3] = 1.0f;
|
|
}
|
|
|
|
StreamingChannel::~StreamingChannel() {
|
|
Reset();
|
|
}
|
|
|
|
void StreamingChannel::FillBuffer(sound_buffer_data* buffer, size_t samples_count, float_t volume) {
|
|
if (playing_state.get() && reset_state.get()) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
ResetData();
|
|
}
|
|
|
|
if (!playing_state.get() || !this->buffer || !buffer)
|
|
return;
|
|
|
|
float_t spk_l_volume = 0.0f;
|
|
float_t spk_r_volume = 0.0f;
|
|
float_t hph_l_volume = 0.0f;
|
|
float_t hph_r_volume = 0.0f;
|
|
{
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
if (!callback_func)
|
|
return;
|
|
|
|
memset(this->buffer, 0, buffer_size);
|
|
|
|
callback_func(this->buffer, samples_count, callback_data);
|
|
|
|
float_t master_volume = this->master_volume;
|
|
spk_l_volume = master_volume * channels_volume[0];
|
|
spk_r_volume = master_volume * channels_volume[1];
|
|
hph_l_volume = master_volume * channels_volume[2];
|
|
hph_r_volume = master_volume * channels_volume[3];
|
|
}
|
|
|
|
vec4 channels_volume(spk_l_volume, spk_r_volume, hph_l_volume, hph_r_volume);
|
|
|
|
sound_buffer_data* _buffer = this->buffer;
|
|
for (size_t i = samples_count / 4; i; i--, buffer += 4, _buffer += 4) {
|
|
((vec4*)buffer)[0] = channels_volume * ((vec4*)_buffer)[0] + ((vec4*)buffer)[0];
|
|
((vec4*)buffer)[1] = channels_volume * ((vec4*)_buffer)[1] + ((vec4*)buffer)[1];
|
|
((vec4*)buffer)[2] = channels_volume * ((vec4*)_buffer)[2] + ((vec4*)buffer)[2];
|
|
((vec4*)buffer)[3] = channels_volume * ((vec4*)_buffer)[3] + ((vec4*)buffer)[3];
|
|
}
|
|
|
|
for (size_t i = samples_count % 4; i; i--, buffer++, _buffer++)
|
|
*(vec4*)buffer = channels_volume * *(vec4*)_buffer + *(vec4*)buffer;
|
|
}
|
|
|
|
bool StreamingChannel::Init(Mixer* mixer, size_t samples_count) {
|
|
if (this->mixer) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
this->mixer = mixer;
|
|
buffer_size = sizeof(sound_buffer_data) * samples_count;
|
|
buffer = (sound_buffer_data*)malloc(buffer_size);
|
|
if (!buffer) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
memset(buffer, 0, buffer_size);
|
|
return true;
|
|
}
|
|
|
|
void StreamingChannel::Reset() {
|
|
reset_state.set(1);
|
|
FillBuffer(0, 0, 0.0f);
|
|
if (buffer) {
|
|
free(buffer);
|
|
buffer = 0;
|
|
}
|
|
mixer = 0;
|
|
buffer_size = 0;
|
|
}
|
|
|
|
void StreamingChannel::ResetData() {
|
|
playing_state.set(0);
|
|
reset_state.set(0);
|
|
callback_func = 0;
|
|
callback_data = 0;
|
|
}
|
|
|
|
bool StreamingChannel::SetCallback(void(*func)(sound_buffer_data* buffer,
|
|
size_t samples_count, void* data), void* data) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
ResetData();
|
|
callback_func = func;
|
|
callback_data = data;
|
|
playing_state.set(1);
|
|
return true;
|
|
}
|
|
|
|
void StreamingChannel::SetChannelsVolume(int32_t mask, float_t value) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
for (int32_t i = 0; i < 4; i++)
|
|
if (mask & (1 << i))
|
|
channels_volume[i] = value;
|
|
}
|
|
|
|
void StreamingChannel::SetMasterVolume(float_t value) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
master_volume = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
sound_db_farc::sound_db_farc() : ready() {
|
|
|
|
}
|
|
|
|
sound_db_farc::~sound_db_farc() {
|
|
|
|
}
|
|
|
|
bool sound_db_farc::load() {
|
|
if (ready)
|
|
return false;
|
|
else if (file_handler.check_not_ready())
|
|
return true;
|
|
|
|
farc.read(file_handler.get_data(), file_handler.get_size());
|
|
if (farc.files.size() && sound_work->ParseProperty(this))
|
|
ready = true;
|
|
return false;
|
|
}
|
|
|
|
bool sound_db_farc::read(const char* file_path) {
|
|
if (ready)
|
|
unload();
|
|
|
|
this->file_path.assign(file_path);
|
|
return file_handler.read_file(&data_list[DATA_AFT], file_path);
|
|
}
|
|
|
|
bool sound_db_farc::unload() {
|
|
sound_work_release_farc_se(file_path.c_str());
|
|
if (!sound_work->UnloadProperty(file_path.c_str()))
|
|
return false;
|
|
|
|
file_path.clear();
|
|
ready = false;
|
|
file_handler.reset();
|
|
return true;
|
|
}
|
|
|
|
sound_db_property::sound_db_property() : farc(),
|
|
volume(), loop_start(), loop_end(), release_time()/*, field_48()*/ {
|
|
|
|
}
|
|
|
|
sound_db_property::~sound_db_property() {
|
|
|
|
}
|
|
|
|
SoundCue::SoundCue() : thread(), property(), counter(), release_time(), se_channel() {
|
|
queue_index = SOUND_WORK_SE_QUEUE_COUNT;
|
|
volume = new SoundCueVolume(this);
|
|
}
|
|
|
|
SoundCue::~SoundCue() {
|
|
if (thread) {
|
|
thread_state.set(1);
|
|
cnd.notify_one();
|
|
thread->join();
|
|
delete thread;
|
|
thread = 0;
|
|
}
|
|
|
|
if (volume) {
|
|
delete volume;
|
|
volume = 0;
|
|
}
|
|
}
|
|
|
|
bool SoundCue::CanPlay() {
|
|
if (se_channel && se_channel->play_state.get())
|
|
return true;
|
|
return !!load_state.get();
|
|
}
|
|
|
|
void SoundCue::Ctrl() {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
while (!thread_state.get()) {
|
|
if (!data_state.get())
|
|
cnd.wait(u_lock);
|
|
|
|
int32_t _data_state = data_state.get();
|
|
data_state.set(SOUND_CUE_DATA_STATE_NONE);
|
|
switch (_data_state) {
|
|
case SOUND_CUE_DATA_STATE_LOAD:
|
|
LoadData();
|
|
break;
|
|
case SOUND_CUE_DATA_STATE_RESET:
|
|
ResetData();
|
|
break;
|
|
}
|
|
}
|
|
thread_state.set(0);
|
|
}
|
|
|
|
int32_t SoundCue::Load(int32_t queue_index, const char* name, float_t volume) {
|
|
ReleaseProt(true);
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
if (!se_channel)
|
|
return 0;
|
|
|
|
sound_db_property* property = sound_work->FindProperty(name);
|
|
if (!property)
|
|
return 0;
|
|
|
|
this->name.assign(name);
|
|
this->property = property;
|
|
this->queue_index = queue_index;
|
|
this->volume->SetValue(volume);
|
|
|
|
Play();
|
|
load_state.set(1);
|
|
data_state.set(SOUND_CUE_DATA_STATE_LOAD);
|
|
cnd.notify_one();
|
|
counter = sound_work->counter;
|
|
if (++sound_work->counter <= -1)
|
|
sound_work->counter = 1;
|
|
return counter;
|
|
}
|
|
|
|
void SoundCue::LoadData() {
|
|
WaveAudio* _wave_audio = 0;
|
|
if (!property || !property->farc) {
|
|
load_state.set(0);
|
|
return;
|
|
}
|
|
|
|
WaveAudio* wave_audio = wave_audio_storage_get_wave_audio(name);
|
|
if (!wave_audio) {
|
|
_wave_audio = new WaveAudio;
|
|
if (_wave_audio && _wave_audio->Read(property->farc, property->file_name.c_str()))
|
|
wave_audio = _wave_audio;
|
|
}
|
|
|
|
if (wave_audio) {
|
|
float_t* buffer = se_channel->InitBuffer(wave_audio->data.channels,
|
|
wave_audio->data.samples_count, wave_audio->data.sample_rate);
|
|
if (buffer) {
|
|
memmove(buffer, wave_audio->buffer, wave_audio->buffer_size);
|
|
se_channel->Play(property->loop_start, property->loop_end);
|
|
}
|
|
}
|
|
|
|
if (_wave_audio) {
|
|
_wave_audio->Reset();
|
|
delete _wave_audio;
|
|
}
|
|
load_state.set(0);
|
|
}
|
|
|
|
void SoundCue::Play() {
|
|
if (se_channel && !property)
|
|
return;
|
|
|
|
bool master_volume_set = false;
|
|
float_t master_volume = property->volume * volume->value;
|
|
|
|
if (volume->set) {
|
|
volume->set = false;
|
|
master_volume_set = true;
|
|
}
|
|
|
|
if (release_time > 0.0f) {
|
|
double_t release_time = this->release_time * 1000.0;
|
|
double_t current_time = time.calc_time();
|
|
double_t remain_time;
|
|
if (current_time >= release_time) {
|
|
remain_time = 0.0;
|
|
Release(true);
|
|
}
|
|
else
|
|
remain_time = release_time - current_time;
|
|
|
|
master_volume_set = true;
|
|
master_volume = (float_t)(remain_time / release_time) * master_volume;
|
|
}
|
|
|
|
if (se_channel) {
|
|
if (master_volume_set)
|
|
se_channel->SetMasterVolume(master_volume);
|
|
|
|
if (!queue_index)
|
|
se_channel->SetChannelsVolume(0x03, get_max_speakers_volume());
|
|
}
|
|
return;
|
|
}
|
|
|
|
void SoundCue::PlayProt() {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
Play();
|
|
}
|
|
|
|
void SoundCue::Release(bool force_release) {
|
|
if (!property || fabsf(property->release_time) <= 0.000001f || force_release) {
|
|
data_state.set(SOUND_CUE_DATA_STATE_RESET);
|
|
cnd.notify_one();
|
|
queue_index = SOUND_WORK_SE_QUEUE_COUNT;
|
|
name.clear();
|
|
property = 0;
|
|
counter = 0;
|
|
release_time = 0.0f;
|
|
}
|
|
else
|
|
release_time = property->release_time;
|
|
|
|
time.get_timestamp();
|
|
}
|
|
|
|
void SoundCue::ReleaseProt(bool force_release) {
|
|
std::unique_lock<std::mutex> u_lock(mtx);
|
|
Release(force_release);
|
|
}
|
|
|
|
void SoundCue::Reset() {
|
|
ReleaseProt(true);
|
|
|
|
if (thread) {
|
|
thread_state.set(1);
|
|
cnd.notify_one();
|
|
thread->join();
|
|
delete thread;
|
|
thread = 0;
|
|
}
|
|
}
|
|
|
|
void SoundCue::ResetData() {
|
|
if (se_channel)
|
|
se_channel->ResetDataProt();
|
|
}
|
|
|
|
void SoundCue::SetSEChannel(sound::wasapi::SEChannel* se_channel) {
|
|
this->se_channel = se_channel;
|
|
if (se_channel)
|
|
se_channel->SetMasterVolume(1.0f);
|
|
|
|
thread = new std::thread(SoundCue::ThreadMain, this);
|
|
if (thread) {
|
|
wchar_t buf[0x80];
|
|
swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), L"SoundCue #%d", sound_cue_counter++);
|
|
SetThreadDescription((HANDLE)thread->native_handle(), buf);
|
|
}
|
|
}
|
|
|
|
void SoundCue::ThreadMain(SoundCue* cue) {
|
|
if (cue)
|
|
cue->Ctrl();
|
|
}
|
|
|
|
WaveAudioData::WaveAudioData() : format(), channels(), sample_rate(), bit_depth(),
|
|
samples_count(), position(), data_size(), has_loop(), loop_start(), loop_end() {
|
|
|
|
}
|
|
|
|
WaveAudioData::~WaveAudioData() {
|
|
|
|
}
|
|
|
|
bool WaveAudioData::Read(WaveAudioDataFileMemoryStream* fms) {
|
|
Reset();
|
|
return ReadDiva(fms)/* || ReadRiff(fms)*/;
|
|
}
|
|
|
|
bool WaveAudioData::ReadData(const void* data, size_t size) {
|
|
if (!this || !data || !size)
|
|
return false;
|
|
|
|
WaveAudioDataFileMemoryStream fms(data, size);
|
|
return Read(&fms);
|
|
}
|
|
|
|
bool WaveAudioData::ReadDiva(WaveAudioDataFileMemoryStream* fms) {
|
|
struct DivaAudioHeader {
|
|
uint32_t signature;
|
|
uint8_t pad_4[4];
|
|
uint32_t size;
|
|
uint32_t sample_rate;
|
|
uint32_t samples_count;
|
|
uint32_t loop_start;
|
|
uint32_t loop_end;
|
|
uint8_t channels;
|
|
uint8_t pad_1D[3];
|
|
uint8_t reserved1[32];
|
|
} header;
|
|
|
|
if (fms->file) {
|
|
if (fseek(fms->file, 0, SEEK_SET))
|
|
return false;
|
|
}
|
|
else {
|
|
if (!fms->data || !fms->size)
|
|
return false;
|
|
fms->position = 0;
|
|
}
|
|
|
|
size_t read_bytes = 0;
|
|
if (fms->file)
|
|
read_bytes = fread(&header, sizeof(DivaAudioHeader), 1, fms->file);
|
|
else if (fms->data && fms->size && fms->position + sizeof(DivaAudioHeader) <= fms->size) {
|
|
memmove(&header, (uint8_t*)fms->data + fms->position, sizeof(DivaAudioHeader));
|
|
read_bytes = sizeof(DivaAudioHeader);
|
|
}
|
|
|
|
if (!read_bytes || header.signature != reverse_endianness_uint32_t('DIVA'))
|
|
return false;
|
|
|
|
format = WAVE_AUDIO_DATA_FORMAT_DIVA;
|
|
channels = header.channels;
|
|
sample_rate = header.sample_rate;
|
|
bit_depth = 0;
|
|
samples_count = header.samples_count;
|
|
position = 0;
|
|
data_size = 0;
|
|
has_loop = header.loop_start < header.loop_end;
|
|
loop_start = header.loop_start;
|
|
loop_end = header.loop_end;
|
|
return true;
|
|
}
|
|
|
|
void WaveAudioData::Reset() {
|
|
format = WAVE_AUDIO_DATA_FORMAT_NONE;
|
|
channels = 0;
|
|
sample_rate = 0;
|
|
bit_depth = 0;
|
|
samples_count = 0;
|
|
position = 0;
|
|
data_size = 0;
|
|
has_loop = false;
|
|
loop_start = 0;
|
|
loop_end = 0;
|
|
}
|
|
|
|
WaveAudio::WaveAudio() : buffer(), buffer_size() {
|
|
|
|
}
|
|
|
|
bool WaveAudio::Read(sound_db_farc* farc, const char* file_name) {
|
|
if (!this)
|
|
return false;
|
|
|
|
if (!farc) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
::farc* f = &farc->farc;
|
|
farc_file* ff = f->read_file(file_name);
|
|
if (!ff || !ff->data || !ff->size) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
data.ReadData(ff->data, ff->size);
|
|
|
|
size_t buffer_size = sizeof(float_t) * data.samples_count * data.channels;
|
|
float_t* buffer = (float_t*)malloc(buffer_size);
|
|
this->buffer_size = buffer_size;
|
|
this->buffer = buffer;
|
|
|
|
if (!buffer) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
bool ret = false;
|
|
memset(buffer, 0, buffer_size);
|
|
if (data.format == WAVE_AUDIO_DATA_FORMAT_DIVA) {
|
|
size_t buf_size = sizeof(int16_t) * data.samples_count * data.channels;
|
|
int16_t* buf = (int16_t*)malloc(buf_size);
|
|
if (!buf) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
memset(buf, 0, buf_size);
|
|
|
|
{
|
|
std::vector<ima_storage> storage(data.channels);
|
|
data.samples_count = ima_decode(buf, buf_size, (uint8_t*)ff->data + data.position,
|
|
data.samples_count, data.channels, storage.data(), storage.size());
|
|
}
|
|
|
|
int16_t* _buf = buf;
|
|
float_t* buffer = this->buffer;
|
|
size_t channels = data.channels;
|
|
size_t samples_count = data.samples_count;
|
|
if (channels == 1)
|
|
for (size_t i = samples_count; i; i--, buffer++, _buf++)
|
|
*buffer = (float_t)*_buf * (float_t)(1.0 / (double_t)0x7FFF);
|
|
else if (channels == 2)
|
|
for (size_t i = samples_count; i; i--, buffer += 2, _buf += 2) {
|
|
vec2 value;
|
|
vec2i16_to_vec2(*(vec2i16*)_buf, value);
|
|
*(vec2*)buffer = value * (float_t)(1.0 / (double_t)0x7FFF);
|
|
}
|
|
else if (channels == 4)
|
|
for (size_t i = samples_count; i; i--, buffer += 4, _buf += 4) {
|
|
vec4 value;
|
|
vec4i16_to_vec4(*(vec4i16*)_buf, value);
|
|
*(vec4*)buffer = value * (float_t)(1.0 / (double_t)0x7FFF);
|
|
}
|
|
else
|
|
for (size_t i = samples_count; i; i--)
|
|
for (size_t j = channels; j; j--, buffer++, _buf++)
|
|
*buffer++ = (float_t)*_buf++ * (float_t)(1.0 / (double_t)0x7FFF);
|
|
|
|
free(buf);
|
|
ret = true;
|
|
}
|
|
|
|
if (!ret) {
|
|
Reset();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void WaveAudio::Reset() {
|
|
if (buffer) {
|
|
free(buffer);
|
|
buffer = 0;
|
|
}
|
|
buffer_size = 0;
|
|
data.Reset();
|
|
}
|
|
|
|
SoundWork::SoundWork() : se_queue_enable(), stream_enable(), se_queue_volume() {
|
|
counter = 1;
|
|
speakers_volume = get_max_speakers_volume();
|
|
speakers_volume_changed = true;
|
|
headphones_volume = get_min_headphones_volume();
|
|
headphones_volume_changed = true;
|
|
se_volume = 1.0f;
|
|
|
|
for (bool& i : se_queue_enable)
|
|
i = true;
|
|
|
|
for (bool& i : stream_enable)
|
|
i = true;
|
|
|
|
for (int32_t i = 0; i < SOUND_WORK_SE_QUEUE_COUNT; i++)
|
|
se_queue_volume[i] = sound_cue_queue_volume_array_get_max(i);
|
|
}
|
|
|
|
SoundWork::~SoundWork() {
|
|
|
|
}
|
|
|
|
sound_db_property* SoundWork::FindProperty(const char* name) {
|
|
auto elem = properties.find(name);
|
|
if (elem != properties.end())
|
|
return &elem->second;
|
|
return 0;
|
|
}
|
|
|
|
bool SoundWork::ParseProperty(sound_db_farc* snd_db_farc) {
|
|
if (!snd_db_farc)
|
|
return false;
|
|
|
|
farc& f = snd_db_farc->farc;
|
|
farc_file* property_ff = f.read_file("property.txt");
|
|
if (!property_ff || !property_ff->data || !property_ff->size)
|
|
return false;
|
|
|
|
key_val kv;
|
|
kv.parse(property_ff->data, property_ff->size);
|
|
|
|
int32_t max;
|
|
float_t volume_bias;
|
|
if (!kv.read("max", max) || !kv.read("volume_bias", volume_bias))
|
|
return false;
|
|
|
|
for (int32_t i = 0, j = 1; i < max; i++, j++) {
|
|
if (!kv.open_scope_fmt("%zu", j))
|
|
continue;
|
|
|
|
std::string name;
|
|
std::string file_name;
|
|
if (!kv.read("name", name) || !kv.read("file_name", file_name)
|
|
|| !kv.has_key("volume") || !f.has_file(file_name.c_str())) {
|
|
kv.close_scope();
|
|
continue;
|
|
}
|
|
|
|
uint32_t loop_start = 0;
|
|
if (kv.open_scope("loop_start")) {
|
|
kv.read(loop_start);
|
|
kv.close_scope();
|
|
}
|
|
|
|
uint32_t loop_end = 0;
|
|
if (kv.open_scope("loop_end")) {
|
|
kv.read(loop_end);
|
|
kv.close_scope();
|
|
}
|
|
|
|
float_t release_time = 0.0f;
|
|
if (kv.open_scope("release_time")) {
|
|
kv.read(release_time);
|
|
release_time *= 0.001f;
|
|
kv.close_scope();
|
|
}
|
|
|
|
float_t volume = 0.0f;
|
|
kv.read("volume", volume);
|
|
|
|
sound_db_property property;
|
|
property.farc = snd_db_farc;
|
|
property.file_name.assign(file_name);
|
|
property.volume = volume * volume_bias;
|
|
property.loop_start = loop_start;
|
|
property.loop_end = loop_end;
|
|
property.release_time = release_time;
|
|
properties.insert_or_assign(name, property);
|
|
kv.close_scope();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SoundWork::UnloadProperty(const char* file_path) {
|
|
for (std::map<std::string, sound_db_property>::iterator i
|
|
= properties.begin(); i != properties.end(); i++)
|
|
if (!i->second.farc->file_path.compare(file_path)) {
|
|
properties.erase(i);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
sound_stream_info::sound_stream_info() : duration(), time() {
|
|
|
|
}
|
|
|
|
sound_stream_info::~sound_stream_info() {
|
|
|
|
}
|
|
|
|
float_t get_min_headphones_volume() {
|
|
return 1.0f;//0.02f;
|
|
}
|
|
|
|
float_t get_max_headphones_volume() {
|
|
return 0.45f;
|
|
}
|
|
|
|
float_t get_min_speakers_volume() {
|
|
return 0.0f;
|
|
}
|
|
|
|
float_t get_max_speakers_volume() {
|
|
return 0.8f;
|
|
}
|
|
|
|
float_t db_to_ratio(int32_t value) {
|
|
if (value <= -10000)
|
|
return 0.0f;
|
|
else if (value >= 0)
|
|
return 1.0f;
|
|
else
|
|
return powf(10.0, (float_t)value * 0.0005f);
|
|
}
|
|
|
|
int32_t ratio_to_db(float_t value) {
|
|
if (value <= 0.0f)
|
|
return -10000;
|
|
else if (value >= 1.0f)
|
|
return 0;
|
|
else
|
|
return (int32_t)(log10f(value) * 20.0f * 100.0f);
|
|
}
|
|
|
|
void sound_init() {
|
|
IMMDeviceEnumerator* mmEnumerator;
|
|
if (SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
|
|
CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&mmEnumerator))) {
|
|
IMMDeviceCollection* collection;
|
|
if (SUCCEEDED(mmEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection))) {
|
|
UINT count;
|
|
if (SUCCEEDED(collection->GetCount(&count))) {
|
|
for (UINT i = 0; i < count; i++) {
|
|
IMMDevice* device;
|
|
if (FAILED(collection->Item(i, &device)))
|
|
continue;
|
|
|
|
wchar_t* wstrID;
|
|
if (SUCCEEDED(device->GetId((LPWSTR*)&wstrID))) {
|
|
IPropertyStore* store;
|
|
if (SUCCEEDED(device->OpenPropertyStore(STGM_READ, &store))) {
|
|
PROPVARIANT pv;
|
|
|
|
PropVariantInit(&pv);
|
|
if (SUCCEEDED(store->GetValue(PKEY_Device_FriendlyName, &pv)))
|
|
printf_debug("%ls\n", pv.pwszVal);
|
|
PropVariantClear(&pv);
|
|
|
|
store->Release();
|
|
}
|
|
CoTaskMemFree((LPVOID)wstrID);
|
|
}
|
|
device->Release();
|
|
}
|
|
}
|
|
collection->Release();
|
|
}
|
|
mmEnumerator->Release();
|
|
}
|
|
|
|
if (!sound_work)
|
|
sound_work = new SoundWork;
|
|
|
|
if (!sound_wasapi_system_data)
|
|
sound_wasapi_system_data = new sound::wasapi::System;
|
|
|
|
sound_wasapi_system_data->Init(SOUND_WORK_SE_CHANNELS_COUNT, SOUND_WORK_STREAMING_CHANNELS_COUNT);
|
|
|
|
sound::wasapi::Mixer* mixer = sound_wasapi_system_data->mixer;
|
|
if (mixer) {
|
|
mixer->SetMasterVolume(1.0f);
|
|
mixer->SetChannelsVolume(0x03, 1.0f);
|
|
mixer->SetChannelsVolume(0x0C, get_min_headphones_volume());
|
|
|
|
size_t se_channels_count = mixer->se_channels_count;
|
|
for (size_t i = 0; i < se_channels_count; i++) {
|
|
sound::wasapi::SEChannel* se_channel = mixer->GetSEChannel(i);
|
|
se_channel->SetChannelsVolume(0x03, get_max_speakers_volume());
|
|
se_channel->SetChannelsVolume(0x0C, 1.0f);
|
|
}
|
|
|
|
size_t streaming_channels_count = mixer->streaming_channels_count;
|
|
for (size_t i = 0; i < streaming_channels_count; i++) {
|
|
sound::wasapi::StreamingChannel* streaming_channel = mixer->GetStreamingChannel(i);
|
|
streaming_channel->SetChannelsVolume(0x03, get_max_speakers_volume());
|
|
streaming_channel->SetChannelsVolume(0x0C, 1.0f);
|
|
}
|
|
|
|
SoundCue* sound_cue = sound_work->cues;
|
|
for (size_t i = 0; i < se_channels_count
|
|
&& i < sizeof(sound_work->cues) / sizeof(SoundCue); i++, sound_cue++) {
|
|
sound::wasapi::SEChannel* se_channel = mixer->GetSEChannel(i);
|
|
if (se_channel)
|
|
sound_cue->SetSEChannel(se_channel);
|
|
}
|
|
}
|
|
|
|
sound_stream_array_init();
|
|
}
|
|
|
|
void sound_ctrl() {
|
|
sound::wasapi::System* sound_wasapi_system = sound_wasapi_system_data_get();
|
|
if (sound_wasapi_system) {
|
|
sound::wasapi::Mixer* mixer = sound_wasapi_system->mixer;
|
|
if (mixer) {
|
|
if (sound_work->headphones_volume_changed)
|
|
mixer->SetChannelsVolume(0x0C, sound_work_get_headphones_volume());
|
|
|
|
if (sound_work->speakers_volume_changed) {
|
|
size_t se_channels_count = mixer->se_channels_count;
|
|
for (size_t i = 0; i < se_channels_count; i++) {
|
|
sound::wasapi::SEChannel* se_channel = mixer->GetSEChannel(i);
|
|
se_channel->SetChannelsVolume(0x03, sound_work_get_speakers_volume());
|
|
}
|
|
|
|
size_t streaming_channels_count = mixer->streaming_channels_count;
|
|
for (size_t i = 0; i < streaming_channels_count; i++) {
|
|
sound::wasapi::StreamingChannel* streaming_channel = mixer->GetStreamingChannel(i);
|
|
streaming_channel->SetChannelsVolume(0x03, sound_work_get_speakers_volume());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sound_work->speakers_volume_changed = false;
|
|
sound_work->headphones_volume_changed = false;
|
|
|
|
for (SoundCue& i : sound_work->cues) {
|
|
if (i.CanPlay())
|
|
i.PlayProt();
|
|
else
|
|
i.ReleaseProt(true);
|
|
}
|
|
|
|
sound_stream_array_ctrl();
|
|
|
|
sound_work->names_list.clear();
|
|
}
|
|
|
|
void sound_free() {
|
|
sound_stream_array_free();
|
|
|
|
if (sound_work) {
|
|
delete sound_work;
|
|
sound_work = 0;
|
|
}
|
|
|
|
if (sound_wasapi_system_data) {
|
|
delete sound_wasapi_system_data;
|
|
sound_wasapi_system_data = 0;
|
|
}
|
|
}
|
|
|
|
void sound_stream_array_reset() {
|
|
for (size_t i = 0; i < SOUND_WORK_STREAM_COUNT; i++)
|
|
sound_stream_array[i].reset();
|
|
}
|
|
|
|
bool sound_work_check_stream_state(int32_t index) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
return stream->check_state();
|
|
}
|
|
|
|
bool sound_work_cue_release(int32_t queue_index, const char* name, bool force_release) {
|
|
if (!name)
|
|
return false;
|
|
|
|
SoundCue* cue = sound_work_get_cue(queue_index, name);
|
|
if (cue) {
|
|
cue->ReleaseProt(force_release);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SoundCue* sound_work_get_cue(int32_t queue_index, const char* name) {
|
|
for (SoundCue& i : sound_work->cues)
|
|
if ((!i.se_channel || i.property) && i.queue_index == queue_index
|
|
&& !i.name.compare(name))
|
|
return &i;
|
|
return 0;
|
|
}
|
|
|
|
bool sound_work_get_cue_can_play(int32_t queue_index, const char* name) {
|
|
if (!name)
|
|
return false;
|
|
|
|
SoundCue* cue = sound_work_get_cue(queue_index, name);
|
|
if (cue)
|
|
return cue->CanPlay();
|
|
return false;
|
|
}
|
|
|
|
sound_db_farc* sound_work_get_farc(const char* file_path) {
|
|
for (sound_db_farc& i : sound_work->farcs)
|
|
if (!i.file_path.compare(file_path))
|
|
return &i;
|
|
return 0;
|
|
}
|
|
|
|
bool sound_work_get_stream_info(sound_stream_info& info, int32_t index) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
info.duration = stream->duration;
|
|
info.time = stream->time;
|
|
info.path.assign(stream->path);
|
|
return true;
|
|
}
|
|
|
|
float_t sound_work_get_stream_volume(int32_t index) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return 1.0f;
|
|
|
|
return stream->current_volume;
|
|
}
|
|
|
|
float_t sound_work_get_headphones_volume() {
|
|
return sound_work->headphones_volume;
|
|
}
|
|
|
|
float_t sound_work_get_speakers_volume() {
|
|
return sound_work->speakers_volume;
|
|
}
|
|
|
|
bool sound_work_has_property(const char* name) {
|
|
return !!sound_work->FindProperty(name);
|
|
}
|
|
|
|
bool sound_work_load_farc(const char* file_path) {
|
|
if (!file_path)
|
|
return false;
|
|
|
|
sound_db_farc* snd_db_farc = sound_work_get_farc(file_path);
|
|
if (snd_db_farc)
|
|
return snd_db_farc->load();
|
|
return false;
|
|
}
|
|
|
|
int32_t sound_work_play_se(int32_t queue_index, const char* name, float_t volume) {
|
|
if (!str_utils_compare(name, "sys_kettei_aif"))
|
|
name = "sys_kettei.aif";
|
|
|
|
bool found = false;
|
|
for (std::string& i : sound_work->names_list)
|
|
if (!i.compare(name)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
if (found || queue_index < 0 || queue_index >= SOUND_WORK_SE_QUEUE_COUNT || !sound_work->se_queue_enable[queue_index])
|
|
return 0;
|
|
|
|
SoundCue* cue = 0;
|
|
for (SoundCue& i : sound_work->cues)
|
|
if (i.se_channel && !i.property) {
|
|
cue = &i;
|
|
break;
|
|
}
|
|
|
|
if (!cue) {
|
|
int32_t min_counter = sound_work->counter;
|
|
for (SoundCue& i : sound_work->cues)
|
|
if (!i.name.compare(name) && min_counter > i.counter) {
|
|
cue = &i;
|
|
min_counter = i.counter;
|
|
}
|
|
|
|
if (!cue) {
|
|
int32_t min_counter = sound_work->counter;
|
|
for (SoundCue& i : sound_work->cues)
|
|
if (min_counter > i.counter) {
|
|
cue = &i;
|
|
min_counter = i.counter;
|
|
}
|
|
|
|
if (!cue)
|
|
return 0;
|
|
}
|
|
|
|
cue->ReleaseProt(true);
|
|
}
|
|
|
|
float_t cue_volume = volume * sound_work->se_queue_volume[queue_index];
|
|
if (queue_index < 3 || queue_index > 4)
|
|
cue_volume *= sound_work->se_volume;
|
|
|
|
int32_t counter = cue->Load(queue_index, name, cue_volume);
|
|
if (counter)
|
|
sound_work->names_list.push_back(name);
|
|
return counter;
|
|
}
|
|
|
|
bool sound_work_play_stream(int32_t index, const char* path, bool pause) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
return stream->set_path(path, pause);
|
|
}
|
|
|
|
bool sound_work_play_stream(int32_t index, const char* path, float_t time, bool pause) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
return stream->set_path(path, time, pause);
|
|
}
|
|
|
|
bool sound_work_read_farc(const char* file_path) {
|
|
if (!file_path || sound_work_get_farc(file_path))
|
|
return false;
|
|
|
|
sound_db_farc* snd_db_farc = 0;
|
|
for (sound_db_farc& i : sound_work->farcs)
|
|
if (!i.file_path.size()) {
|
|
snd_db_farc = &i;
|
|
break;
|
|
}
|
|
|
|
if (snd_db_farc)
|
|
return snd_db_farc->read(file_path);
|
|
return false;
|
|
}
|
|
|
|
void sound_work_release_farc_se(const char* file_path) {
|
|
if (!file_path)
|
|
return;
|
|
|
|
for (SoundCue& i : sound_work->cues) {
|
|
if (i.se_channel && !i.property || i.property->farc)
|
|
continue;
|
|
|
|
sound_db_farc* snd_db_farc = i.property->farc;
|
|
if (!snd_db_farc)
|
|
continue;
|
|
|
|
if (!snd_db_farc->farc.file_path.compare(file_path))
|
|
i.ReleaseProt(true);
|
|
}
|
|
}
|
|
|
|
bool sound_work_release_se(const char* name, bool release) {
|
|
if (!name)
|
|
return false;
|
|
|
|
bool found = false;
|
|
for (SoundCue& i : sound_work->cues)
|
|
if ((!i.se_channel || i.property) && !i.name.compare(name)) {
|
|
i.ReleaseProt(release);
|
|
found = true;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
bool sound_work_release_stream(int32_t index) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
return stream->stop();
|
|
}
|
|
|
|
void sound_work_reset_all_se() {
|
|
for (SoundCue& i : sound_work->cues)
|
|
if ((!i.se_channel || i.property) && i.queue_index)
|
|
i.ReleaseProt(true);
|
|
}
|
|
|
|
bool sound_work_stream_set_pause(int32_t index, bool value) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
return stream->set_pause(value);
|
|
}
|
|
|
|
float_t sound_cue_queue_volume_array_get_max(int32_t queue_index) {
|
|
if (queue_index >= 0 && queue_index < SOUND_WORK_SE_QUEUE_COUNT)
|
|
return sound_cue_queue_volume_array[queue_index].max;
|
|
return 1.0f;
|
|
}
|
|
|
|
float_t sound_cue_queue_volume_array_get_min(int32_t queue_index) {
|
|
if (queue_index >= 0 && queue_index < SOUND_WORK_SE_QUEUE_COUNT)
|
|
return sound_cue_queue_volume_array[queue_index].min;
|
|
return 0.0f;
|
|
}
|
|
|
|
sound::wasapi::System* sound_wasapi_system_data_get() {
|
|
return sound_wasapi_system_data;
|
|
}
|
|
|
|
void sound_work_set_headphones_volume(float_t value) {
|
|
float_t min = get_min_headphones_volume();
|
|
float_t max = get_max_headphones_volume();
|
|
value = clamp_def(value, min, max);
|
|
if (fabsf(sound_work->headphones_volume - value) > 0.000001f) {
|
|
sound_work->headphones_volume = value;
|
|
sound_work->headphones_volume_changed = true;
|
|
}
|
|
}
|
|
|
|
void sound_work_set_se_queue_volume(int32_t queue_index, float_t value) {
|
|
if (queue_index < 0 || queue_index >= SOUND_WORK_SE_QUEUE_COUNT)
|
|
return;
|
|
|
|
const SoundCueQueueVolume& vol = sound_cue_queue_volume_array[queue_index];
|
|
sound_work->se_queue_volume[queue_index] = clamp_def(value, vol.min, vol.max);
|
|
}
|
|
|
|
void sound_work_set_speakers_volume(float_t value) {
|
|
float_t min = get_min_speakers_volume();
|
|
float_t max = get_max_speakers_volume();
|
|
value = clamp_def(value, min, max);
|
|
if (fabsf(sound_work->speakers_volume - value) > 0.000001f) {
|
|
sound_work->speakers_volume = value;
|
|
sound_work->speakers_volume_changed = true;
|
|
}
|
|
}
|
|
|
|
bool sound_work_set_stream_current_volume(int32_t index, float_t value) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
stream->set_current_volume(value);
|
|
return true;
|
|
}
|
|
|
|
bool sound_work_set_stream_target_volume(int32_t index, float_t value, int32_t frames) {
|
|
if (index < 0 || index >= SOUND_WORK_STREAM_COUNT || !sound_work->stream_enable[index])
|
|
return false;
|
|
|
|
sound_stream* stream = sound_stream_array_get(index);
|
|
if (!stream)
|
|
return false;
|
|
|
|
stream->set_target_volume(value, frames);
|
|
return true;
|
|
}
|
|
|
|
bool sound_work_unload_farc(const char* file_path) {
|
|
if (!file_path)
|
|
return false;
|
|
|
|
sound_db_farc* snd_db_farc = sound_work_get_farc(file_path);
|
|
if (snd_db_farc)
|
|
return snd_db_farc->unload();
|
|
return false;
|
|
}
|
|
|
|
void wave_audio_storage_init() {
|
|
wave_audio_storage_data = {};
|
|
}
|
|
|
|
void wave_audio_storage_clear() {
|
|
wave_audio_storage_data.clear();
|
|
}
|
|
|
|
WaveAudio* wave_audio_storage_get_wave_audio(const std::string& name) {
|
|
auto elem = wave_audio_storage_data.find(name);
|
|
if (elem != wave_audio_storage_data.end())
|
|
return &elem->second;
|
|
return 0;
|
|
}
|
|
|
|
bool wave_audio_storage_load_wave_audio(const std::string& name) {
|
|
wave_audio_storage_unload_wave_audio(name);
|
|
|
|
sound_db_property* property = sound_work->FindProperty(name.c_str());
|
|
if (!property)
|
|
return false;
|
|
|
|
WaveAudio wave_audio;
|
|
if (wave_audio.Read(property->farc, property->file_name.c_str())) {
|
|
wave_audio_storage_data.insert({ name, wave_audio });
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void wave_audio_storage_unload_wave_audio(const std::string& name) {
|
|
auto elem = wave_audio_storage_data.find(name);
|
|
if (elem != wave_audio_storage_data.end()) {
|
|
elem->second.Reset();
|
|
wave_audio_storage_data.erase(elem);
|
|
}
|
|
}
|
|
|
|
void wave_audio_storage_free() {
|
|
wave_audio_storage_data.clear();
|
|
}
|
|
|
|
static size_t ima_decode(int16_t* dst, size_t dst_size, uint8_t* data, size_t samples_count,
|
|
size_t channels, ima_storage* storage, size_t storage_size) {
|
|
if (!dst || !dst_size || !data || !samples_count || !storage || channels > storage_size)
|
|
return 0;
|
|
|
|
bool odd_sample = false;
|
|
size_t act_samp_count = 0;
|
|
for (size_t i = min_def(samples_count, dst_size / sizeof(int16_t)); i; i--, act_samp_count++) {
|
|
for (size_t j = channels, k = 0; j; j--, k++, odd_sample ^= true) {
|
|
ima_storage& _storage = storage[k];
|
|
int16_t step = ima_step_table[_storage.step_index];
|
|
|
|
uint8_t nibble = (odd_sample ? *data : (*data >> 4)) & 0x0F;
|
|
|
|
int32_t diff = step >> 3;
|
|
if (nibble & 0x01)
|
|
diff += step >> 2;
|
|
if (nibble & 0x02)
|
|
diff += step >> 1;
|
|
if (nibble & 0x04)
|
|
diff += step;
|
|
if (nibble & 0x08)
|
|
diff = -diff;
|
|
|
|
int32_t current = _storage.current + diff;
|
|
current = clamp_def(current, -0x8000, 0x7FFF);
|
|
_storage.current = current;
|
|
|
|
int32_t step_index = _storage.step_index + ima_index_table[nibble];
|
|
_storage.step_index = clamp_def(step_index, 0, 88);
|
|
|
|
*dst++ = current;
|
|
|
|
if (odd_sample)
|
|
data++;
|
|
}
|
|
}
|
|
return act_samp_count;
|
|
}
|
|
|
|
sound_stream::sound_stream() : ogg_playback(), duration(), pause(), file_loading_frames(),
|
|
file_ready_frames(), load_time_seek(), volume_trans(), state(), play_state(), time() {
|
|
current_volume = 1.0f;
|
|
target_volume = 1.0f;
|
|
}
|
|
|
|
sound_stream::~sound_stream() {
|
|
|
|
}
|
|
|
|
bool sound_stream::check_state() {
|
|
if (play_state == 1)
|
|
return true;
|
|
return state == 1;
|
|
}
|
|
|
|
void sound_stream::ctrl() {
|
|
if (!check_state())
|
|
return;
|
|
|
|
if (play_state == 1 && volume_trans > 0) {
|
|
current_volume += (target_volume - current_volume) / (float_t)volume_trans;
|
|
volume_trans--;
|
|
}
|
|
|
|
if (path.size() < 4)
|
|
return;
|
|
|
|
std::string ext;
|
|
ext.assign(path.c_str() + (path.size() - 4), 4);
|
|
for (char& c : ext)
|
|
if (c >= 'A' && c <= 'Z')
|
|
c += 0x20;
|
|
|
|
if (ext.compare(".ogg"))
|
|
return;
|
|
|
|
if (!ogg_playback) {
|
|
size_t index = 0;
|
|
for (size_t i = 0; i < SOUND_WORK_STREAM_COUNT; i++)
|
|
if (&sound_stream_array[i] == this) {
|
|
index = i;
|
|
break;
|
|
}
|
|
|
|
ogg_playback = ogg_playback_data_get(index);
|
|
}
|
|
|
|
if (ogg_playback)
|
|
ctrl_playback();
|
|
}
|
|
|
|
void sound_stream::ctrl_playback() {
|
|
OggFileHandlerFileState file_state = OGG_FILE_HANDLER_FILE_STATE_NONE;
|
|
if (ogg_playback) {
|
|
file_state = ogg_playback->GetFileState();
|
|
ogg_playback->SetMasterVolume(ratio_to_db(current_volume));
|
|
}
|
|
|
|
duration = 0.0f;
|
|
time = 0.0f;
|
|
play_state = 0;
|
|
|
|
if (file_state == OGG_FILE_HANDLER_FILE_STATE_NONE
|
|
|| (file_state >= OGG_FILE_HANDLER_FILE_STATE_STOPPED
|
|
&& file_state <= OGG_FILE_HANDLER_FILE_STATE_MAX)) {
|
|
play_state = 0;
|
|
file_loading_frames = 0;
|
|
file_ready_frames = 0;
|
|
duration = 0.0f;
|
|
time = 0.0f;
|
|
if (state != 1) {
|
|
state = 0;
|
|
stop_playback();
|
|
}
|
|
else if (set_path_playback()) {
|
|
play_state = 1;
|
|
state = 0;
|
|
}
|
|
else
|
|
stop_playback();
|
|
}
|
|
else if (file_state >= OGG_FILE_HANDLER_FILE_STATE_LOADING
|
|
&& file_state <= OGG_FILE_HANDLER_FILE_STATE_STOPPING) {
|
|
play_state = 1;
|
|
if (ogg_playback) {
|
|
if (ogg_playback->GetPauseState() == OGG_FILE_HANDLER_PAUSE_STATE_PLAY) {
|
|
file_loading_frames += file_state == OGG_FILE_HANDLER_FILE_STATE_LOADING;
|
|
file_ready_frames += file_state == OGG_FILE_HANDLER_FILE_STATE_READY;
|
|
}
|
|
float_t _duration = ogg_playback->GetDuration();
|
|
float_t _time = ogg_playback->GetTime();
|
|
duration = _duration;
|
|
if (_duration > 0.0f && _time > _duration)
|
|
do
|
|
_time -= _duration;
|
|
while (_time > _duration);
|
|
time = _time;
|
|
}
|
|
|
|
if (state)
|
|
stop_playback();
|
|
}
|
|
|
|
OggPlayback::SetChannelPairVolumePan(ogg_playback);
|
|
}
|
|
|
|
void sound_stream::reset() {
|
|
state = 0;
|
|
play_state = 0;
|
|
}
|
|
|
|
void sound_stream::set_current_volume(float_t value) {
|
|
current_volume = clamp_def(value, 0.0f, 1.0f);
|
|
volume_trans = 0;
|
|
}
|
|
|
|
bool sound_stream::set_path(const char* path, bool pause) {
|
|
if (!pause) {
|
|
bool paused = false;
|
|
if (ogg_playback)
|
|
paused = ogg_playback->GetPauseState() == OGG_FILE_HANDLER_PAUSE_STATE_PAUSE;
|
|
|
|
if (!(this->path.compare(path) || !check_state() || !paused)) {
|
|
if (ogg_playback)
|
|
ogg_playback->SetPauseState(OGG_FILE_HANDLER_PAUSE_STATE_PLAY);
|
|
pause = false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
this->path.assign(path);
|
|
state = 1;
|
|
this->pause = pause;
|
|
current_volume = sound_stream_volume;
|
|
load_time_seek = 0.0f;
|
|
volume_trans = 0;
|
|
target_volume = sound_stream_volume;
|
|
return true;
|
|
}
|
|
|
|
bool sound_stream::set_path(const char* path, float_t time, bool pause) {
|
|
bool res = set_path(path, pause);
|
|
if (res)
|
|
load_time_seek = time;
|
|
return res;
|
|
}
|
|
|
|
bool sound_stream::set_path_playback() {
|
|
if (!ogg_playback)
|
|
return false;
|
|
|
|
ogg_playback->SetPauseState(pause
|
|
? OGG_FILE_HANDLER_PAUSE_STATE_PAUSE : OGG_FILE_HANDLER_PAUSE_STATE_PLAY);
|
|
ogg_playback->SetLoadTimeSeek(load_time_seek);
|
|
ogg_playback->SetPath(path);
|
|
return true;
|
|
}
|
|
|
|
bool sound_stream::set_pause(bool value) {
|
|
if (!ogg_playback)
|
|
return false;
|
|
|
|
pause = value;
|
|
ogg_playback->SetPauseState(value
|
|
? OGG_FILE_HANDLER_PAUSE_STATE_PAUSE : OGG_FILE_HANDLER_PAUSE_STATE_PLAY);
|
|
return true;
|
|
}
|
|
|
|
void sound_stream::set_target_volume(float_t value, int32_t frames) {
|
|
target_volume = value;
|
|
volume_trans = max_def(frames, 1);
|
|
}
|
|
|
|
bool sound_stream::stop() {
|
|
state = 2;
|
|
return true;
|
|
}
|
|
|
|
bool sound_stream::stop_playback() {
|
|
if (!ogg_playback)
|
|
return false;
|
|
|
|
ogg_playback->Stop();
|
|
ogg_playback->SetPauseState(OGG_FILE_HANDLER_PAUSE_STATE_PLAY);
|
|
return true;
|
|
}
|
|
|
|
static void sound_stream_array_init() {
|
|
if (!sound_stream_array)
|
|
sound_stream_array = new sound_stream[SOUND_WORK_STREAM_COUNT];
|
|
}
|
|
|
|
static void sound_stream_array_ctrl() {
|
|
for (size_t i = 0; i < SOUND_WORK_STREAM_COUNT; i++)
|
|
sound_stream_array[i].ctrl();
|
|
}
|
|
|
|
static sound_stream* sound_stream_array_get(int32_t index) {
|
|
if (index >= 0 && index < SOUND_WORK_STREAM_COUNT)
|
|
return &sound_stream_array[index];
|
|
return 0;
|
|
}
|
|
|
|
static void sound_stream_array_free() {
|
|
if (sound_stream_array) {
|
|
delete[] sound_stream_array;
|
|
sound_stream_array = 0;
|
|
}
|
|
}
|