mirror of https://github.com/mackron/dr_libs
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
12 KiB
C
327 lines
12 KiB
C
#define DR_WAV_IMPLEMENTATION
|
|
#include "../../dr_wav.h"
|
|
#include <sndfile.h>
|
|
#include "../common/dr_common.c"
|
|
|
|
dr_handle g_libsndfile = NULL;
|
|
|
|
typedef SNDFILE* (* pfn_sf_open_virtual)(SF_VIRTUAL_IO* sfvirtual, int mode, SF_INFO* sfinfo, void* user_data);
|
|
typedef int (* pfn_sf_close) (SNDFILE* sndfile);
|
|
typedef sf_count_t (* pfn_sf_readf_short) (SNDFILE *sndfile, short *ptr, sf_count_t frames);
|
|
typedef sf_count_t (* pfn_sf_readf_int) (SNDFILE *sndfile, int *ptr, sf_count_t frames);
|
|
typedef sf_count_t (* pfn_sf_readf_float) (SNDFILE *sndfile, float *ptr, sf_count_t frames);
|
|
typedef sf_count_t (* pfn_sf_readf_double)(SNDFILE *sndfile, double *ptr, sf_count_t frames);
|
|
typedef sf_count_t (* pfn_sf_seek) (SNDFILE *sndfile, sf_count_t frames, int whence);
|
|
|
|
pfn_sf_open_virtual libsndfile__sf_open_virtual;
|
|
pfn_sf_close libsndfile__sf_close;
|
|
pfn_sf_readf_short libsndfile__sf_readf_short;
|
|
pfn_sf_readf_int libsndfile__sf_readf_int;
|
|
pfn_sf_readf_float libsndfile__sf_readf_float;
|
|
pfn_sf_readf_double libsndfile__sf_readf_double;
|
|
pfn_sf_seek libsndfile__sf_seek;
|
|
|
|
drwav_result libsndfile_init_api()
|
|
{
|
|
unsigned int i;
|
|
const char* pFileNames[] = {
|
|
#if defined(_WIN32)
|
|
#if defined(_WIN64)
|
|
"libsndfile-1-x64.dll",
|
|
#else
|
|
"libsndfile-1-x86.dll",
|
|
#endif
|
|
"libsndfile-1.dll"
|
|
#else
|
|
"libsndfile-1.so",
|
|
"libsndfile.so.1"
|
|
#endif
|
|
};
|
|
|
|
if (g_libsndfile != NULL) {
|
|
return DRWAV_INVALID_OPERATION; /* Already initialized. */
|
|
}
|
|
|
|
for (i = 0; i < sizeof(pFileNames)/sizeof(pFileNames[0]); i += 1) {
|
|
g_libsndfile = dr_dlopen(pFileNames[i]);
|
|
if (g_libsndfile != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (g_libsndfile == NULL) {
|
|
return DRWAV_ERROR; /* Unable to load libsndfile-1.so/dll. */
|
|
}
|
|
|
|
libsndfile__sf_open_virtual = (pfn_sf_open_virtual)dr_dlsym(g_libsndfile, "sf_open_virtual");
|
|
libsndfile__sf_close = (pfn_sf_close) dr_dlsym(g_libsndfile, "sf_close");
|
|
libsndfile__sf_readf_short = (pfn_sf_readf_short) dr_dlsym(g_libsndfile, "sf_readf_short");
|
|
libsndfile__sf_readf_int = (pfn_sf_readf_int) dr_dlsym(g_libsndfile, "sf_readf_int");
|
|
libsndfile__sf_readf_float = (pfn_sf_readf_float) dr_dlsym(g_libsndfile, "sf_readf_float");
|
|
libsndfile__sf_readf_double = (pfn_sf_readf_double)dr_dlsym(g_libsndfile, "sf_readf_double");
|
|
libsndfile__sf_seek = (pfn_sf_seek) dr_dlsym(g_libsndfile, "sf_seek");
|
|
|
|
return DRWAV_SUCCESS;
|
|
}
|
|
|
|
void libsndfile_uninit_api()
|
|
{
|
|
if (g_libsndfile == NULL) {
|
|
return; /* Invalid operation. Not initialized. */
|
|
}
|
|
|
|
dr_dlclose(g_libsndfile);
|
|
g_libsndfile = NULL;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
SNDFILE* pHandle;
|
|
SF_INFO info;
|
|
drwav_uint8* pFileData;
|
|
size_t fileSizeInBytes;
|
|
size_t fileReadPos;
|
|
} libsndfile;
|
|
|
|
sf_count_t libsndfile__on_filelen(void *user_data)
|
|
{
|
|
libsndfile* pSndFile = (libsndfile*)user_data;
|
|
return (sf_count_t)pSndFile->fileSizeInBytes;
|
|
}
|
|
|
|
sf_count_t libsndfile__on_seek(sf_count_t offset, int whence, void *user_data)
|
|
{
|
|
libsndfile* pSndFile = (libsndfile*)user_data;
|
|
|
|
switch (whence)
|
|
{
|
|
case SF_SEEK_SET:
|
|
{
|
|
pSndFile->fileReadPos = (size_t)offset;
|
|
} break;
|
|
case SF_SEEK_CUR:
|
|
{
|
|
pSndFile->fileReadPos += (size_t)offset;
|
|
} break;
|
|
case SF_SEEK_END:
|
|
{
|
|
pSndFile->fileReadPos = pSndFile->fileSizeInBytes - (size_t)offset;
|
|
} break;
|
|
}
|
|
|
|
return (sf_count_t)pSndFile->fileReadPos;
|
|
}
|
|
|
|
sf_count_t libsndfile__on_read(void *ptr, sf_count_t count, void *user_data)
|
|
{
|
|
libsndfile* pSndFile = (libsndfile*)user_data;
|
|
|
|
DRWAV_COPY_MEMORY(ptr, pSndFile->pFileData + pSndFile->fileReadPos, (size_t)count);
|
|
pSndFile->fileReadPos += (size_t)count;
|
|
|
|
return count;
|
|
}
|
|
|
|
sf_count_t libsndfile__on_write(const void *ptr, sf_count_t count, void *user_data)
|
|
{
|
|
/* We're not doing anything with writing. */
|
|
(void)ptr;
|
|
(void)count;
|
|
(void)user_data;
|
|
return 0;
|
|
}
|
|
|
|
sf_count_t libsndfile__on_tell(void *user_data)
|
|
{
|
|
libsndfile* pSndFile = (libsndfile*)user_data;
|
|
return (sf_count_t)pSndFile->fileReadPos;
|
|
}
|
|
|
|
drwav_result libsndfile_init_file(const char* pFilePath, libsndfile* pSndFile)
|
|
{
|
|
SF_VIRTUAL_IO callbacks;
|
|
|
|
if (pFilePath == NULL || pSndFile == NULL) {
|
|
return DRWAV_INVALID_ARGS;
|
|
}
|
|
|
|
DRWAV_ZERO_MEMORY(pSndFile, sizeof(*pSndFile));
|
|
|
|
/* We use libsndfile's virtual IO technique because we want to load from memory to make speed benchmarking fairer. */
|
|
pSndFile->pFileData = (drwav_uint8*)dr_open_and_read_file(pFilePath, &pSndFile->fileSizeInBytes);
|
|
if (pSndFile->pFileData == NULL) {
|
|
return DRWAV_ERROR; /* Failed to open the file. */
|
|
}
|
|
|
|
DRWAV_ZERO_MEMORY(&callbacks, sizeof(callbacks));
|
|
callbacks.get_filelen = libsndfile__on_filelen;
|
|
callbacks.seek = libsndfile__on_seek;
|
|
callbacks.read = libsndfile__on_read;
|
|
callbacks.write = libsndfile__on_write;
|
|
callbacks.tell = libsndfile__on_tell;
|
|
|
|
pSndFile->pHandle = libsndfile__sf_open_virtual(&callbacks, SFM_READ, &pSndFile->info, pSndFile);
|
|
if (pSndFile->pHandle == NULL) {
|
|
free(pSndFile->pFileData);
|
|
return DRWAV_ERROR;
|
|
}
|
|
|
|
return DRWAV_SUCCESS;
|
|
}
|
|
|
|
void libsndfile_uninit(libsndfile* pSndFile)
|
|
{
|
|
if (pSndFile == NULL) {
|
|
return;
|
|
}
|
|
|
|
libsndfile__sf_close(pSndFile->pHandle);
|
|
free(pSndFile->pFileData);
|
|
}
|
|
|
|
drwav_uint64 libsndfile_read_pcm_frames_s16(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
|
|
{
|
|
if (pSndFile == NULL || pBufferOut == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
|
|
if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
|
|
/* Read as float and convert. */
|
|
drwav_uint64 totalFramesRead = 0;
|
|
while (totalFramesRead < framesToRead) {
|
|
float temp[4096];
|
|
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
|
|
drwav_uint64 framesReadThisIteration;
|
|
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
|
|
if (framesToReadThisIteration > framesRemaining) {
|
|
framesToReadThisIteration = framesRemaining;
|
|
}
|
|
|
|
framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
|
|
|
|
drwav_f32_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
|
|
|
|
totalFramesRead += framesReadThisIteration;
|
|
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
|
|
|
|
/* If we read less frames than we requested we've reached the end of the file. */
|
|
if (framesReadThisIteration < framesToReadThisIteration) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return totalFramesRead;
|
|
} else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
|
|
/* Read as double and convert. */
|
|
drwav_uint64 totalFramesRead = 0;
|
|
while (totalFramesRead < framesToRead) {
|
|
double temp[4096];
|
|
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
|
|
drwav_uint64 framesReadThisIteration;
|
|
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
|
|
if (framesToReadThisIteration > framesRemaining) {
|
|
framesToReadThisIteration = framesRemaining;
|
|
}
|
|
|
|
framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
|
|
|
|
drwav_f64_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
|
|
|
|
totalFramesRead += framesReadThisIteration;
|
|
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
|
|
|
|
/* If we read less frames than we requested we've reached the end of the file. */
|
|
if (framesReadThisIteration < framesToReadThisIteration) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return totalFramesRead;
|
|
} else {
|
|
return libsndfile__sf_readf_short(pSndFile->pHandle, pBufferOut, framesToRead);
|
|
}
|
|
}
|
|
|
|
drwav_uint64 libsndfile_read_pcm_frames_f32(libsndfile* pSndFile, drwav_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
if (pSndFile == NULL || pBufferOut == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return libsndfile__sf_readf_float(pSndFile->pHandle, pBufferOut, framesToRead);
|
|
}
|
|
|
|
drwav_uint64 libsndfile_read_pcm_frames_s32(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
|
|
{
|
|
if (pSndFile == NULL || pBufferOut == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
|
|
if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
|
|
/* Read floats and convert. */
|
|
drwav_uint64 totalFramesRead = 0;
|
|
while (totalFramesRead < framesToRead) {
|
|
float temp[4096];
|
|
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
|
|
drwav_uint64 framesReadThisIteration;
|
|
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
|
|
if (framesToReadThisIteration > framesRemaining) {
|
|
framesToReadThisIteration = framesRemaining;
|
|
}
|
|
|
|
framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
|
|
|
|
drwav_f32_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
|
|
|
|
totalFramesRead += framesReadThisIteration;
|
|
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
|
|
|
|
/* If we read less frames than we requested we've reached the end of the file. */
|
|
if (framesReadThisIteration < framesToReadThisIteration) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return totalFramesRead;
|
|
} else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
|
|
/* Read doubles and convert. */
|
|
drwav_uint64 totalFramesRead = 0;
|
|
while (totalFramesRead < framesToRead) {
|
|
double temp[4096];
|
|
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
|
|
drwav_uint64 framesReadThisIteration;
|
|
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
|
|
if (framesToReadThisIteration > framesRemaining) {
|
|
framesToReadThisIteration = framesRemaining;
|
|
}
|
|
|
|
framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
|
|
|
|
drwav_f64_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
|
|
|
|
totalFramesRead += framesReadThisIteration;
|
|
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
|
|
|
|
/* If we read less frames than we requested we've reached the end of the file. */
|
|
if (framesReadThisIteration < framesToReadThisIteration) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return totalFramesRead;
|
|
} else {
|
|
return libsndfile__sf_readf_int(pSndFile->pHandle, pBufferOut, framesToRead);
|
|
}
|
|
}
|
|
|
|
drwav_bool32 libsndfile_seek_to_pcm_frame(libsndfile* pSndFile, drwav_uint64 targetPCMFrameIndex)
|
|
{
|
|
if (pSndFile == NULL) {
|
|
return DRWAV_FALSE;
|
|
}
|
|
|
|
return libsndfile__sf_seek(pSndFile->pHandle, (sf_count_t)targetPCMFrameIndex, SF_SEEK_SET) == (sf_count_t)targetPCMFrameIndex;
|
|
}
|