2020-10-07 16:41:31 +02:00
|
|
|
// SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
|
|
|
|
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
|
|
|
|
// license. You should have receive a LICENSE.md file along with the code.
|
|
|
|
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
|
|
|
|
|
|
|
|
|
#include "st_audiofile.h"
|
|
|
|
|
#if !defined(ST_AUDIO_FILE_USE_SNDFILE)
|
|
|
|
|
#include "st_audiofile_libs.h"
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
struct st_audio_file {
|
|
|
|
|
int type;
|
|
|
|
|
union {
|
|
|
|
|
drwav *wav;
|
|
|
|
|
drflac *flac;
|
2020-10-31 11:59:45 +01:00
|
|
|
AIFF_Ref aiff;
|
2020-10-09 15:41:08 +02:00
|
|
|
drmp3 *mp3;
|
2020-10-09 17:29:53 +02:00
|
|
|
stb_vorbis* ogg;
|
2022-09-28 23:09:50 +02:00
|
|
|
WavpackContext* wv;
|
2020-10-07 16:41:31 +02:00
|
|
|
};
|
2020-10-09 15:41:08 +02:00
|
|
|
|
|
|
|
|
union {
|
2020-10-31 11:59:45 +01:00
|
|
|
struct { uint32_t channels; float sample_rate; uint64_t frames; } aiff;
|
2020-10-09 15:41:08 +02:00
|
|
|
struct { uint64_t frames; } mp3;
|
2020-10-09 17:29:53 +02:00
|
|
|
struct { uint32_t channels; float sample_rate; uint64_t frames; } ogg;
|
2022-09-28 23:09:50 +02:00
|
|
|
struct { uint32_t channels; float sample_rate; uint64_t frames; int bitrate; int mode; } wv;
|
2020-10-09 15:41:08 +02:00
|
|
|
} cache;
|
2021-04-09 20:36:05 +02:00
|
|
|
|
|
|
|
|
union {
|
|
|
|
|
stb_vorbis_alloc ogg;
|
|
|
|
|
} alloc;
|
2020-10-07 16:41:31 +02:00
|
|
|
};
|
|
|
|
|
|
2020-10-09 18:30:26 +02:00
|
|
|
static st_audio_file* st_generic_open_file(const void* filename, int widepath)
|
2020-10-07 16:41:31 +02:00
|
|
|
{
|
2020-10-09 18:30:26 +02:00
|
|
|
#if !defined(_WIN32)
|
|
|
|
|
if (widepath)
|
|
|
|
|
return NULL;
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-10-07 16:41:31 +02:00
|
|
|
st_audio_file* af = (st_audio_file*)malloc(sizeof(st_audio_file));
|
|
|
|
|
if (!af)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2020-10-09 18:30:26 +02:00
|
|
|
// Try WAV
|
|
|
|
|
{
|
2020-10-07 16:41:31 +02:00
|
|
|
af->wav = (drwav*)malloc(sizeof(drwav));
|
|
|
|
|
if (!af->wav) {
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-10-09 18:30:26 +02:00
|
|
|
drwav_bool32 ok =
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
widepath ? drwav_init_file_w(af->wav, (const wchar_t*)filename, NULL) :
|
|
|
|
|
#endif
|
|
|
|
|
drwav_init_file(af->wav, (const char*)filename, NULL);
|
|
|
|
|
if (!ok)
|
2020-10-07 16:41:31 +02:00
|
|
|
free(af->wav);
|
2020-10-09 18:30:26 +02:00
|
|
|
else {
|
2020-10-07 16:41:31 +02:00
|
|
|
af->type = st_audio_file_wav;
|
2020-10-09 18:30:26 +02:00
|
|
|
return af;
|
|
|
|
|
}
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-09 18:30:26 +02:00
|
|
|
// Try FLAC
|
|
|
|
|
{
|
|
|
|
|
af->flac =
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
widepath ? drflac_open_file_w((const wchar_t*)filename, NULL) :
|
|
|
|
|
#endif
|
|
|
|
|
drflac_open_file((const char*)filename, NULL);
|
|
|
|
|
if (af->flac) {
|
2020-10-07 16:41:31 +02:00
|
|
|
af->type = st_audio_file_flac;
|
2020-10-09 18:30:26 +02:00
|
|
|
return af;
|
|
|
|
|
}
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-31 11:59:45 +01:00
|
|
|
// Try AIFF
|
|
|
|
|
{
|
|
|
|
|
af->aiff =
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
widepath ? AIFF_OpenFileW((const wchar_t*)filename, F_RDONLY) :
|
|
|
|
|
#endif
|
|
|
|
|
AIFF_OpenFile((const char*)filename, F_RDONLY);
|
|
|
|
|
if (af->aiff) {
|
|
|
|
|
int channels;
|
|
|
|
|
double sample_rate;
|
|
|
|
|
uint64_t frames;
|
|
|
|
|
if (AIFF_GetAudioFormat(af->aiff, &frames, &channels, &sample_rate, NULL, NULL) == -1) {
|
|
|
|
|
AIFF_CloseFile(af->aiff);
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
af->cache.aiff.channels = (uint32_t)channels;
|
|
|
|
|
af->cache.aiff.sample_rate = (float)sample_rate;
|
|
|
|
|
af->cache.aiff.frames = frames;
|
|
|
|
|
af->type = st_audio_file_aiff;
|
|
|
|
|
return af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-09 18:30:26 +02:00
|
|
|
// Try OGG
|
|
|
|
|
{
|
2021-04-09 20:36:05 +02:00
|
|
|
int err = 0;
|
|
|
|
|
char* alloc_buffer = NULL;
|
|
|
|
|
int alloc_size = 0;
|
|
|
|
|
const int alloc_max_size = 16 * 1024 * 1024;
|
|
|
|
|
|
|
|
|
|
af->alloc.ogg.alloc_buffer = NULL;
|
|
|
|
|
af->alloc.ogg.alloc_buffer_length_in_bytes = 0;
|
|
|
|
|
|
|
|
|
|
int try_again;
|
|
|
|
|
do {
|
|
|
|
|
af->ogg =
|
2020-10-09 18:30:26 +02:00
|
|
|
#if defined(_WIN32)
|
2021-04-09 20:36:05 +02:00
|
|
|
widepath ? stb_vorbis_open_filename_w((const wchar_t*)filename, NULL, NULL) :
|
2020-10-09 18:30:26 +02:00
|
|
|
#endif
|
2021-04-09 20:36:05 +02:00
|
|
|
stb_vorbis_open_filename((const char*)filename, &err, &af->alloc.ogg);
|
|
|
|
|
try_again = 0;
|
|
|
|
|
if (!af->ogg && err == VORBIS_outofmem) {
|
|
|
|
|
int next_size = alloc_size ? (int)(alloc_size * 3L / 2) : (128 * 1024);
|
|
|
|
|
if (next_size <= alloc_max_size) {
|
|
|
|
|
free(alloc_buffer);
|
|
|
|
|
alloc_size = next_size;
|
|
|
|
|
alloc_buffer = (char*)malloc(alloc_size);
|
|
|
|
|
af->alloc.ogg.alloc_buffer = alloc_buffer;
|
|
|
|
|
af->alloc.ogg.alloc_buffer_length_in_bytes = alloc_size;
|
|
|
|
|
try_again = alloc_buffer != NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (try_again);
|
|
|
|
|
|
|
|
|
|
if (!af->ogg)
|
|
|
|
|
free(alloc_buffer);
|
|
|
|
|
else {
|
2020-10-09 18:30:26 +02:00
|
|
|
af->cache.ogg.frames = stb_vorbis_stream_length_in_samples(af->ogg);
|
|
|
|
|
if (af->cache.ogg.frames == 0) {
|
2020-10-09 17:29:53 +02:00
|
|
|
stb_vorbis_close(af->ogg);
|
2021-04-09 20:36:05 +02:00
|
|
|
free(alloc_buffer);
|
2020-10-09 18:30:26 +02:00
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
2020-10-09 17:29:53 +02:00
|
|
|
}
|
2020-10-09 18:30:26 +02:00
|
|
|
stb_vorbis_info info = stb_vorbis_get_info(af->ogg);
|
|
|
|
|
af->cache.ogg.channels = info.channels;
|
|
|
|
|
af->cache.ogg.sample_rate = info.sample_rate;
|
|
|
|
|
af->type = st_audio_file_ogg;
|
|
|
|
|
return af;
|
2020-10-09 17:29:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-09 18:30:26 +02:00
|
|
|
// Try MP3
|
2020-10-31 12:09:40 +01:00
|
|
|
{
|
2020-10-09 15:41:08 +02:00
|
|
|
af->mp3 = (drmp3*)malloc(sizeof(drmp3));
|
|
|
|
|
if (!af->mp3) {
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-10-09 18:30:26 +02:00
|
|
|
drmp3_bool32 ok =
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
widepath ? drmp3_init_file_w(af->mp3, (const wchar_t*)filename, NULL) :
|
|
|
|
|
#endif
|
|
|
|
|
drmp3_init_file(af->mp3, (const char*)filename, NULL);
|
|
|
|
|
if (!ok)
|
2020-10-09 15:41:08 +02:00
|
|
|
free(af->mp3);
|
2020-10-09 18:30:26 +02:00
|
|
|
else {
|
|
|
|
|
af->cache.mp3.frames = drmp3_get_pcm_frame_count(af->mp3);
|
|
|
|
|
if (af->cache.mp3.frames == 0) {
|
|
|
|
|
free(af->mp3);
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-10-09 15:41:08 +02:00
|
|
|
af->type = st_audio_file_mp3;
|
2020-10-09 18:30:26 +02:00
|
|
|
return af;
|
|
|
|
|
}
|
2020-10-09 15:41:08 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-28 23:09:50 +02:00
|
|
|
// Try WV
|
|
|
|
|
{
|
|
|
|
|
af->wv =
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
WavpackOpenFileInput((const char*)filename, NULL, OPEN_FILE_UTF8, 0);
|
|
|
|
|
#else
|
|
|
|
|
WavpackOpenFileInput((const char*)filename, NULL, 0, 0);
|
|
|
|
|
#endif
|
|
|
|
|
if (af->wv) {
|
|
|
|
|
af->cache.wv.channels = (uint32_t)WavpackGetNumChannels(af->wv);
|
|
|
|
|
af->cache.wv.sample_rate = (float)WavpackGetSampleRate(af->wv);
|
|
|
|
|
af->cache.wv.frames = (uint64_t)WavpackGetNumSamples64(af->wv);
|
|
|
|
|
af->cache.wv.bitrate = WavpackGetBitsPerSample(af->wv);
|
|
|
|
|
af->cache.wv.mode = WavpackGetMode(af->wv);
|
|
|
|
|
af->type = st_audio_file_wv;
|
|
|
|
|
return af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-09 18:30:26 +02:00
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-10-07 16:41:31 +02:00
|
|
|
|
2021-11-21 23:55:28 +01:00
|
|
|
st_audio_file* st_open_memory(const void* memory, size_t length)
|
|
|
|
|
{
|
|
|
|
|
st_audio_file* af = (st_audio_file*)malloc(sizeof(st_audio_file));
|
|
|
|
|
if (!af)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
// Try WAV
|
|
|
|
|
{
|
|
|
|
|
af->wav = (drwav*)malloc(sizeof(drwav));
|
|
|
|
|
if (!af->wav) {
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
drwav_bool32 ok =
|
|
|
|
|
drwav_init_memory(af->wav, memory, length, NULL);
|
|
|
|
|
if (!ok)
|
|
|
|
|
free(af->wav);
|
|
|
|
|
else {
|
|
|
|
|
af->type = st_audio_file_wav;
|
|
|
|
|
return af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try FLAC
|
|
|
|
|
{
|
|
|
|
|
af->flac =
|
|
|
|
|
drflac_open_memory(memory, length, NULL);
|
|
|
|
|
if (af->flac) {
|
|
|
|
|
af->type = st_audio_file_flac;
|
|
|
|
|
return af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try OGG
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
char* alloc_buffer = NULL;
|
|
|
|
|
int alloc_size = 0;
|
|
|
|
|
const int alloc_max_size = 16 * 1024 * 1024;
|
|
|
|
|
|
|
|
|
|
af->alloc.ogg.alloc_buffer = NULL;
|
|
|
|
|
af->alloc.ogg.alloc_buffer_length_in_bytes = 0;
|
|
|
|
|
|
|
|
|
|
int try_again;
|
|
|
|
|
do {
|
|
|
|
|
af->ogg =
|
|
|
|
|
stb_vorbis_open_memory(memory, length, &err, &af->alloc.ogg);
|
|
|
|
|
try_again = 0;
|
|
|
|
|
if (!af->ogg && err == VORBIS_outofmem) {
|
|
|
|
|
int next_size = alloc_size ? (int)(alloc_size * 3L / 2) : (128 * 1024);
|
|
|
|
|
if (next_size <= alloc_max_size) {
|
|
|
|
|
free(alloc_buffer);
|
|
|
|
|
alloc_size = next_size;
|
|
|
|
|
alloc_buffer = (char*)malloc(alloc_size);
|
|
|
|
|
af->alloc.ogg.alloc_buffer = alloc_buffer;
|
|
|
|
|
af->alloc.ogg.alloc_buffer_length_in_bytes = alloc_size;
|
|
|
|
|
try_again = alloc_buffer != NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (try_again);
|
|
|
|
|
|
|
|
|
|
if (!af->ogg)
|
|
|
|
|
free(alloc_buffer);
|
|
|
|
|
else {
|
|
|
|
|
af->cache.ogg.frames = stb_vorbis_stream_length_in_samples(af->ogg);
|
|
|
|
|
if (af->cache.ogg.frames == 0) {
|
|
|
|
|
stb_vorbis_close(af->ogg);
|
|
|
|
|
free(alloc_buffer);
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
stb_vorbis_info info = stb_vorbis_get_info(af->ogg);
|
|
|
|
|
af->cache.ogg.channels = info.channels;
|
|
|
|
|
af->cache.ogg.sample_rate = info.sample_rate;
|
|
|
|
|
af->type = st_audio_file_ogg;
|
|
|
|
|
return af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try MP3
|
|
|
|
|
{
|
|
|
|
|
af->mp3 = (drmp3*)malloc(sizeof(drmp3));
|
|
|
|
|
if (!af->mp3) {
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
drmp3_bool32 ok = drmp3_init_memory(af->mp3, memory, length, NULL);
|
|
|
|
|
if (!ok)
|
|
|
|
|
free(af->mp3);
|
|
|
|
|
else {
|
|
|
|
|
af->cache.mp3.frames = drmp3_get_pcm_frame_count(af->mp3);
|
|
|
|
|
if (af->cache.mp3.frames == 0) {
|
|
|
|
|
free(af->mp3);
|
|
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
af->type = st_audio_file_mp3;
|
|
|
|
|
return af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-28 23:09:50 +02:00
|
|
|
// Try WV
|
|
|
|
|
{
|
|
|
|
|
af->wv =
|
|
|
|
|
WavpackOpenRawDecoder((void*)memory, (int32_t)length, NULL, 0,
|
|
|
|
|
WAVPACK_MEMORY_ASSUMED_VERSION, NULL, 0, 0);
|
|
|
|
|
if (af->wv) {
|
|
|
|
|
af->type = st_audio_file_wv;
|
|
|
|
|
return af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-21 23:55:28 +01:00
|
|
|
free(af);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-09 18:30:26 +02:00
|
|
|
st_audio_file* st_open_file(const char* filename)
|
|
|
|
|
{
|
|
|
|
|
return st_generic_open_file(filename, 0);
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
st_audio_file* st_open_file_w(const wchar_t* filename)
|
|
|
|
|
{
|
2020-10-09 18:30:26 +02:00
|
|
|
return st_generic_open_file(filename, 1);
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void st_close(st_audio_file* af)
|
|
|
|
|
{
|
|
|
|
|
switch (af->type) {
|
|
|
|
|
case st_audio_file_wav:
|
|
|
|
|
drwav_uninit(af->wav);
|
|
|
|
|
free(af->wav);
|
|
|
|
|
break;
|
|
|
|
|
case st_audio_file_flac:
|
|
|
|
|
drflac_close(af->flac);
|
|
|
|
|
break;
|
2020-10-31 11:59:45 +01:00
|
|
|
case st_audio_file_aiff:
|
|
|
|
|
AIFF_CloseFile(af->aiff);
|
|
|
|
|
break;
|
2020-10-09 17:29:53 +02:00
|
|
|
case st_audio_file_ogg:
|
|
|
|
|
stb_vorbis_close(af->ogg);
|
2021-04-09 20:36:05 +02:00
|
|
|
free(af->alloc.ogg.alloc_buffer);
|
2020-10-09 17:29:53 +02:00
|
|
|
break;
|
2020-10-09 15:41:08 +02:00
|
|
|
case st_audio_file_mp3:
|
|
|
|
|
drmp3_uninit(af->mp3);
|
|
|
|
|
free(af->mp3);
|
|
|
|
|
break;
|
2022-09-28 23:09:50 +02:00
|
|
|
case st_audio_file_wv:
|
|
|
|
|
WavpackCloseFile(af->wv);
|
|
|
|
|
break;
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-30 11:49:22 +01:00
|
|
|
free(af);
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int st_get_type(st_audio_file* af)
|
|
|
|
|
{
|
|
|
|
|
return af->type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t st_get_channels(st_audio_file* af)
|
|
|
|
|
{
|
|
|
|
|
uint32_t channels = 0;
|
|
|
|
|
|
|
|
|
|
switch (af->type) {
|
|
|
|
|
case st_audio_file_wav:
|
|
|
|
|
channels = af->wav->channels;
|
|
|
|
|
break;
|
|
|
|
|
case st_audio_file_flac:
|
|
|
|
|
channels = af->flac->channels;
|
|
|
|
|
break;
|
2020-10-31 11:59:45 +01:00
|
|
|
case st_audio_file_aiff:
|
|
|
|
|
channels = af->cache.aiff.channels;
|
|
|
|
|
break;
|
2020-10-09 17:29:53 +02:00
|
|
|
case st_audio_file_ogg:
|
|
|
|
|
channels = af->cache.ogg.channels;
|
|
|
|
|
break;
|
2020-10-09 15:41:08 +02:00
|
|
|
case st_audio_file_mp3:
|
|
|
|
|
channels = af->mp3->channels;
|
|
|
|
|
break;
|
2022-09-28 23:09:50 +02:00
|
|
|
case st_audio_file_wv:
|
|
|
|
|
channels = af->cache.wv.channels;
|
|
|
|
|
break;
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return channels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float st_get_sample_rate(st_audio_file* af)
|
|
|
|
|
{
|
|
|
|
|
float sample_rate = 0;
|
|
|
|
|
|
|
|
|
|
switch (af->type) {
|
|
|
|
|
case st_audio_file_wav:
|
|
|
|
|
sample_rate = af->wav->sampleRate;
|
|
|
|
|
break;
|
|
|
|
|
case st_audio_file_flac:
|
|
|
|
|
sample_rate = af->flac->sampleRate;
|
|
|
|
|
break;
|
2020-10-31 11:59:45 +01:00
|
|
|
case st_audio_file_aiff:
|
|
|
|
|
sample_rate = af->cache.aiff.sample_rate;
|
|
|
|
|
break;
|
2020-10-09 17:29:53 +02:00
|
|
|
case st_audio_file_ogg:
|
|
|
|
|
sample_rate = af->cache.ogg.sample_rate;
|
|
|
|
|
break;
|
2020-10-09 15:41:08 +02:00
|
|
|
case st_audio_file_mp3:
|
|
|
|
|
sample_rate = af->mp3->sampleRate;
|
|
|
|
|
break;
|
2022-09-28 23:09:50 +02:00
|
|
|
case st_audio_file_wv:
|
|
|
|
|
sample_rate = af->cache.wv.sample_rate;
|
|
|
|
|
break;
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sample_rate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t st_get_frame_count(st_audio_file* af)
|
|
|
|
|
{
|
|
|
|
|
uint64_t frames = 0;
|
|
|
|
|
|
|
|
|
|
switch (af->type) {
|
|
|
|
|
case st_audio_file_wav:
|
|
|
|
|
frames = af->wav->totalPCMFrameCount;
|
|
|
|
|
break;
|
|
|
|
|
case st_audio_file_flac:
|
|
|
|
|
frames = af->flac->totalPCMFrameCount;
|
|
|
|
|
break;
|
2020-10-31 11:59:45 +01:00
|
|
|
case st_audio_file_aiff:
|
|
|
|
|
frames = af->cache.aiff.frames;
|
|
|
|
|
break;
|
2020-10-09 17:29:53 +02:00
|
|
|
case st_audio_file_ogg:
|
|
|
|
|
frames = af->cache.ogg.frames;
|
|
|
|
|
break;
|
2020-10-09 15:41:08 +02:00
|
|
|
case st_audio_file_mp3:
|
|
|
|
|
frames = af->cache.mp3.frames;
|
|
|
|
|
break;
|
2022-09-28 23:09:50 +02:00
|
|
|
case st_audio_file_wv:
|
|
|
|
|
frames = af->cache.wv.frames;
|
|
|
|
|
break;
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return frames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool st_seek(st_audio_file* af, uint64_t frame)
|
|
|
|
|
{
|
|
|
|
|
bool success = false;
|
|
|
|
|
|
|
|
|
|
switch (af->type) {
|
|
|
|
|
case st_audio_file_wav:
|
|
|
|
|
success = drwav_seek_to_pcm_frame(af->wav, frame);
|
|
|
|
|
break;
|
|
|
|
|
case st_audio_file_flac:
|
|
|
|
|
success = drflac_seek_to_pcm_frame(af->flac, frame);
|
|
|
|
|
break;
|
2020-10-31 11:59:45 +01:00
|
|
|
case st_audio_file_aiff:
|
|
|
|
|
success = AIFF_Seek(af->aiff, frame) != -1;
|
|
|
|
|
break;
|
2020-10-09 17:29:53 +02:00
|
|
|
case st_audio_file_ogg:
|
|
|
|
|
success = stb_vorbis_seek(af->ogg, (unsigned)frame) != 0;
|
|
|
|
|
break;
|
2020-10-09 15:41:08 +02:00
|
|
|
case st_audio_file_mp3:
|
|
|
|
|
success = drmp3_seek_to_pcm_frame(af->mp3, frame);
|
|
|
|
|
break;
|
2022-09-28 23:09:50 +02:00
|
|
|
case st_audio_file_wv:
|
|
|
|
|
success = WavpackSeekSample64(af->wv, (int64_t)frame);
|
|
|
|
|
break;
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t st_read_s16(st_audio_file* af, int16_t* buffer, uint64_t count)
|
|
|
|
|
{
|
|
|
|
|
switch (af->type) {
|
|
|
|
|
case st_audio_file_wav:
|
|
|
|
|
count = drwav_read_pcm_frames_s16(af->wav, count, buffer);
|
|
|
|
|
break;
|
|
|
|
|
case st_audio_file_flac:
|
|
|
|
|
count = drflac_read_pcm_frames_s16(af->flac, count, buffer);
|
|
|
|
|
break;
|
2020-10-31 11:59:45 +01:00
|
|
|
case st_audio_file_aiff:
|
|
|
|
|
{
|
|
|
|
|
uint32_t channels = af->cache.aiff.channels;
|
|
|
|
|
unsigned samples = AIFF_ReadSamples16Bit(af->aiff, buffer, (unsigned)(channels * count));
|
|
|
|
|
count = ((int)samples != -1) ? (samples / channels) : 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-10-09 17:29:53 +02:00
|
|
|
case st_audio_file_ogg:
|
|
|
|
|
count = stb_vorbis_get_samples_short_interleaved(
|
|
|
|
|
af->ogg, af->cache.ogg.channels, buffer,
|
|
|
|
|
count * af->cache.ogg.channels);
|
|
|
|
|
break;
|
2020-10-09 15:41:08 +02:00
|
|
|
case st_audio_file_mp3:
|
|
|
|
|
count = drmp3_read_pcm_frames_s16(af->mp3, count, buffer);
|
|
|
|
|
break;
|
2022-09-28 23:09:50 +02:00
|
|
|
case st_audio_file_wv:
|
|
|
|
|
{
|
|
|
|
|
int32_t* buf_i32 = (int32_t*)malloc(4 * count);
|
|
|
|
|
if (!buf_i32) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
count = WavpackUnpackSamples(af->wv, buf_i32, (uint32_t)count);
|
|
|
|
|
if (af->cache.wv.mode & MODE_FLOAT) {
|
|
|
|
|
drwav_f32_to_s16((drwav_int16*)buffer, (float*)buf_i32, (size_t)count);
|
|
|
|
|
} else {
|
|
|
|
|
int d = af->cache.wv.bitrate - 16;
|
|
|
|
|
for (uint64_t i = 0; i < count; i++) {
|
|
|
|
|
buffer[i] = (int16_t)(buf_i32[i] >> d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(buf_i32);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t st_read_f32(st_audio_file* af, float* buffer, uint64_t count)
|
|
|
|
|
{
|
|
|
|
|
switch (af->type) {
|
|
|
|
|
case st_audio_file_wav:
|
|
|
|
|
count = drwav_read_pcm_frames_f32(af->wav, count, buffer);
|
|
|
|
|
break;
|
|
|
|
|
case st_audio_file_flac:
|
|
|
|
|
count = drflac_read_pcm_frames_f32(af->flac, count, buffer);
|
|
|
|
|
break;
|
2020-10-31 11:59:45 +01:00
|
|
|
case st_audio_file_aiff:
|
|
|
|
|
{
|
|
|
|
|
uint32_t channels = af->cache.aiff.channels;
|
|
|
|
|
unsigned samples = AIFF_ReadSamplesFloat(af->aiff, buffer, (unsigned)(channels * count));
|
|
|
|
|
count = ((int)samples != -1) ? (samples / channels) : 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-10-09 17:29:53 +02:00
|
|
|
case st_audio_file_ogg:
|
|
|
|
|
count = stb_vorbis_get_samples_float_interleaved(
|
|
|
|
|
af->ogg, af->cache.ogg.channels, buffer,
|
|
|
|
|
count * af->cache.ogg.channels);
|
|
|
|
|
break;
|
2020-10-09 15:41:08 +02:00
|
|
|
case st_audio_file_mp3:
|
|
|
|
|
count = drmp3_read_pcm_frames_f32(af->mp3, count, buffer);
|
|
|
|
|
break;
|
2022-09-28 23:09:50 +02:00
|
|
|
case st_audio_file_wv:
|
|
|
|
|
count = WavpackUnpackSamples(af->wv, (int32_t*)buffer, (uint32_t)count);
|
|
|
|
|
if (!(af->cache.wv.mode & MODE_FLOAT)) {
|
|
|
|
|
if (af->cache.wv.bitrate < 32) {
|
|
|
|
|
int d = 32 - af->cache.wv.bitrate;
|
|
|
|
|
for (uint64_t i = 0; i < count; i++) {
|
|
|
|
|
((int32_t*)buffer)[i] <<= d;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
drwav_s32_to_f32(buffer, (drwav_int32*)buffer, (size_t)count);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-10-07 16:41:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // !defined(ST_AUDIO_FILE_USE_SNDFILE)
|