Commit be65f148 authored by David Reid's avatar David Reid

Update dr_wav.

parent b0000958
// WAV audio loader. Public domain. See "unlicense" statement at the end of this file. // WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file.
// dr_wav - v0.6 - 2017-08-16 // dr_wav - v0.7 - 2017-11-04
// //
// David Reid - mackron@gmail.com // David Reid - mackron@gmail.com
...@@ -64,6 +64,23 @@ ...@@ -64,6 +64,23 @@
// without any manual intervention. // without any manual intervention.
// //
// //
// dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at
// drwav_open_write(), drwav_open_file_write(), etc. Use drwav_write() to write samples, or drwav_write_raw() to write
// raw data in the "data" chunk.
//
// drwav_data_format format;
// format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
// format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
// format.channels = 2;
// format.sampleRate = 44100;
// format.bitsPerSample = 16;
// drwav* pWav = drwav_open_file_write("data/recording.wav", &format);
//
// ...
//
// drwav_uint64 samplesWritten = drwav_write(pWav, sampleCount, pSamples);
//
//
// //
// OPTIONS // OPTIONS
// #define these options before including this file. // #define these options before including this file.
...@@ -72,7 +89,7 @@ ...@@ -72,7 +89,7 @@
// Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32(). // Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32().
// //
// #define DR_WAV_NO_STDIO // #define DR_WAV_NO_STDIO
// Disables drwav_open_file(). // Disables drwav_open_file(), drwav_open_file_write(), etc.
// //
// //
// //
...@@ -161,6 +178,17 @@ typedef enum ...@@ -161,6 +178,17 @@ typedef enum
// either the entire bytesToRead is filled or you have reached the end of the stream. // either the entire bytesToRead is filled or you have reached the end of the stream.
typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
// Callback for when data is written. Returns value is the number of bytes actually written.
//
// pUserData [in] The user data that was passed to drwav_init_write(), drwav_open_write() and family.
// pData [out] A pointer to the data to write.
// bytesToWrite [in] The number of bytes to write.
//
// Returns the number of bytes actually written.
//
// If the return value differs from bytesToWrite, it indicates an error.
typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
// Callback for when data needs to be seeked. // Callback for when data needs to be seeked.
// //
// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family. // pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
...@@ -173,7 +201,7 @@ typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t byt ...@@ -173,7 +201,7 @@ typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t byt
// will be either drwav_seek_origin_start or drwav_seek_origin_current. // will be either drwav_seek_origin_start or drwav_seek_origin_current.
typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
// Structure for internal use. Only used for loaders opened with drwav_open_memory. // Structure for internal use. Only used for loaders opened with drwav_open_memory().
typedef struct typedef struct
{ {
const drwav_uint8* data; const drwav_uint8* data;
...@@ -181,6 +209,25 @@ typedef struct ...@@ -181,6 +209,25 @@ typedef struct
size_t currentReadPos; size_t currentReadPos;
} drwav__memory_stream; } drwav__memory_stream;
// Structure for internal use. Only used for writers opened with drwav_open_memory_write().
typedef struct
{
void** ppData;
size_t* pDataSize;
size_t dataSize;
size_t dataCapacity;
size_t currentWritePos;
} drwav__memory_stream_write;
typedef struct
{
drwav_container container; // RIFF, W64.
drwav_uint32 format; // DR_WAVE_FORMAT_*
drwav_uint32 channels;
drwav_uint32 sampleRate;
drwav_uint32 bitsPerSample;
} drwav_data_format;
typedef struct typedef struct
{ {
// The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications // The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
...@@ -222,6 +269,9 @@ typedef struct ...@@ -222,6 +269,9 @@ typedef struct
// A pointer to the function to call when more data is needed. // A pointer to the function to call when more data is needed.
drwav_read_proc onRead; drwav_read_proc onRead;
// A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode.
drwav_write_proc onWrite;
// A pointer to the function to call when the wav file needs to be seeked. // A pointer to the function to call when the wav file needs to be seeked.
drwav_seek_proc onSeek; drwav_seek_proc onSeek;
...@@ -268,7 +318,7 @@ typedef struct ...@@ -268,7 +318,7 @@ typedef struct
// A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory(). // A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory().
drwav__memory_stream memoryStream; drwav__memory_stream memoryStream;
drwav__memory_stream_write memoryStreamWrite;
// Generic data for compressed formats. This data is shared across all block-compressed formats. // Generic data for compressed formats. This data is shared across all block-compressed formats.
struct struct
...@@ -318,6 +368,25 @@ typedef struct ...@@ -318,6 +368,25 @@ typedef struct
// See also: drwav_init_file(), drwav_init_memory(), drwav_uninit() // See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData); drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
// Initializes a pre-allocated drwav object for writing.
//
// onWrite [in] The function to call when data needs to be written.
// onSeek [in] The function to call when the write position needs to move.
// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
//
// Returns true if successful; false otherwise.
//
// Close the writer with drwav_uninit().
//
// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
// to open the stream from a file or from a block of memory respectively.
//
// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
// a drwav object on the heap and return a pointer to it.
//
// See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
// Uninitializes the given drwav object. // Uninitializes the given drwav object.
// //
// Use this only for objects initialized with drwav_init(). // Use this only for objects initialized with drwav_init().
...@@ -343,6 +412,25 @@ void drwav_uninit(drwav* pWav); ...@@ -343,6 +412,25 @@ void drwav_uninit(drwav* pWav);
// See also: drwav_open_file(), drwav_open_memory(), drwav_close() // See also: drwav_open_file(), drwav_open_memory(), drwav_close()
drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData); drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
// Opens a wav file for writing using the given callbacks.
//
// onWrite [in] The function to call when data needs to be written.
// onSeek [in] The function to call when the write position needs to move.
// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
//
// Returns null on error.
//
// Close the loader with drwav_close().
//
// This is the lowest level function for opening a WAV file. You can also use drwav_open_file_write() and drwav_open_memory_write()
// to open the stream from a file or from a block of memory respectively.
//
// This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
// initializing it.
//
// See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close()
drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
// Uninitializes and deletes the the given drwav object. // Uninitializes and deletes the the given drwav object.
// //
// Use this only for objects created with drwav_open(). // Use this only for objects created with drwav_open().
...@@ -378,6 +466,17 @@ drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOu ...@@ -378,6 +466,17 @@ drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOu
drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample); drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample);
// Writes raw audio data.
//
// Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
// Writes audio data based on sample counts.
//
// Returns the number of samples written.
drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData);
//// Convertion Utilities //// //// Convertion Utilities ////
#ifndef DR_WAV_NO_CONVERSION_API #ifndef DR_WAV_NO_CONVERSION_API
...@@ -482,6 +581,13 @@ void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sample ...@@ -482,6 +581,13 @@ void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sample
// any given time. // any given time.
drwav_bool32 drwav_init_file(drwav* pWav, const char* filename); drwav_bool32 drwav_init_file(drwav* pWav, const char* filename);
// Helper for initializing a wave file for writing using stdio.
//
// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
// objects because the operating system may restrict the number of file handles an application can have open at
// any given time.
drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat);
// Helper for opening a wave file using stdio. // Helper for opening a wave file using stdio.
// //
// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav // This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
...@@ -489,9 +595,16 @@ drwav_bool32 drwav_init_file(drwav* pWav, const char* filename); ...@@ -489,9 +595,16 @@ drwav_bool32 drwav_init_file(drwav* pWav, const char* filename);
// any given time. // any given time.
drwav* drwav_open_file(const char* filename); drwav* drwav_open_file(const char* filename);
// Helper for opening a wave file for writing using stdio.
//
// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
// objects because the operating system may restrict the number of file handles an application can have open at
// any given time.
drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat);
#endif //DR_WAV_NO_STDIO #endif //DR_WAV_NO_STDIO
// Helper for initializing a file from a pre-allocated memory buffer. // Helper for initializing a loader from a pre-allocated memory buffer.
// //
// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for // This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
// the lifetime of the drwav object. // the lifetime of the drwav object.
...@@ -499,7 +612,15 @@ drwav* drwav_open_file(const char* filename); ...@@ -499,7 +612,15 @@ drwav* drwav_open_file(const char* filename);
// The buffer should contain the contents of the entire wave file, not just the sample data. // The buffer should contain the contents of the entire wave file, not just the sample data.
drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize); drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize);
// Helper for opening a file from a pre-allocated memory buffer. // Helper for initializing a writer which outputs data to a memory buffer.
//
// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
//
// The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be
// considered valid until after drwav_uninit() has been called anyway.
drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
// Helper for opening a loader from a pre-allocated memory buffer.
// //
// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for // This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
// the lifetime of the drwav object. // the lifetime of the drwav object.
...@@ -507,6 +628,13 @@ drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize); ...@@ -507,6 +628,13 @@ drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize);
// The buffer should contain the contents of the entire wave file, not just the sample data. // The buffer should contain the contents of the entire wave file, not just the sample data.
drwav* drwav_open_memory(const void* data, size_t dataSize); drwav* drwav_open_memory(const void* data, size_t dataSize);
// Helper for opening a writer which outputs data to a memory buffer.
//
// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
//
// The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be
// considered valid until after drwav_close() has been called anyway.
drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
#ifndef DR_WAV_NO_CONVERSION_API #ifndef DR_WAV_NO_CONVERSION_API
...@@ -873,6 +1001,11 @@ static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t byt ...@@ -873,6 +1001,11 @@ static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t byt
return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
} }
static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
{
return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
}
static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
{ {
return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
...@@ -895,6 +1028,23 @@ drwav_bool32 drwav_init_file(drwav* pWav, const char* filename) ...@@ -895,6 +1028,23 @@ drwav_bool32 drwav_init_file(drwav* pWav, const char* filename)
return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile); return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile);
} }
drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat)
{
FILE* pFile;
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (fopen_s(&pFile, filename, "wb") != 0) {
return DRWAV_FALSE;
}
#else
pFile = fopen(filename, "wb");
if (pFile == NULL) {
return DRWAV_FALSE;
}
#endif
return drwav_init_write(pWav, pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
}
drwav* drwav_open_file(const char* filename) drwav* drwav_open_file(const char* filename)
{ {
FILE* pFile; FILE* pFile;
...@@ -917,6 +1067,29 @@ drwav* drwav_open_file(const char* filename) ...@@ -917,6 +1067,29 @@ drwav* drwav_open_file(const char* filename)
return pWav; return pWav;
} }
drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat)
{
FILE* pFile;
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (fopen_s(&pFile, filename, "wb") != 0) {
return NULL;
}
#else
pFile = fopen(filename, "wb");
if (pFile == NULL) {
return NULL;
}
#endif
drwav* pWav = drwav_open_write(pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
if (pWav == NULL) {
fclose(pFile);
return NULL;
}
return pWav;
}
#endif //DR_WAV_NO_STDIO #endif //DR_WAV_NO_STDIO
...@@ -964,14 +1137,85 @@ static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_see ...@@ -964,14 +1137,85 @@ static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_see
memory->currentReadPos = memory->dataSize; // Trying to seek too far forward. memory->currentReadPos = memory->dataSize; // Trying to seek too far forward.
} }
} }
return DRWAV_TRUE;
}
static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
{
drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
drwav_assert(memory != NULL);
drwav_assert(memory->dataCapacity >= memory->currentWritePos);
size_t bytesRemaining = memory->dataCapacity - memory->currentWritePos;
if (bytesRemaining < bytesToWrite) {
// Need to reallocate.
size_t newDataCapacity = (memory->dataCapacity == 0) ? 256 : memory->dataCapacity * 2;
// If doubling wasn't enough, just make it the minimum required size to write the data.
if ((newDataCapacity - memory->currentWritePos) < bytesToWrite) {
newDataCapacity = memory->currentWritePos + bytesToWrite;
}
void* pNewData = DRWAV_REALLOC(*memory->ppData, newDataCapacity);
if (pNewData == NULL) {
return 0;
}
*memory->ppData = pNewData;
memory->dataCapacity = newDataCapacity;
}
drwav_uint8* pDataOut = (drwav_uint8*)(*memory->ppData);
DRWAV_COPY_MEMORY(pDataOut + memory->currentWritePos, pDataIn, bytesToWrite);
memory->currentWritePos += bytesToWrite;
if (memory->dataSize < memory->currentWritePos) {
memory->dataSize = memory->currentWritePos;
}
*memory->pDataSize = memory->dataSize;
return bytesToWrite;
}
static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
{
drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
drwav_assert(memory != NULL);
if (origin == drwav_seek_origin_current) {
if (offset > 0) {
if (memory->currentWritePos + offset > memory->dataSize) {
offset = (int)(memory->dataSize - memory->currentWritePos); // Trying to seek too far forward.
}
} else {
if (memory->currentWritePos < (size_t)-offset) {
offset = -(int)memory->currentWritePos; // Trying to seek too far backwards.
}
}
// This will never underflow thanks to the clamps above.
memory->currentWritePos += offset;
} else {
if ((drwav_uint32)offset <= memory->dataSize) {
memory->currentWritePos = offset;
} else {
memory->currentWritePos = memory->dataSize; // Trying to seek too far forward.
}
}
return DRWAV_TRUE; return DRWAV_TRUE;
} }
drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize) drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize)
{ {
if (data == NULL || dataSize == 0) {
return DRWAV_FALSE;
}
drwav__memory_stream memoryStream; drwav__memory_stream memoryStream;
drwav_zero_memory(&memoryStream, sizeof(memoryStream));
memoryStream.data = (const unsigned char*)data; memoryStream.data = (const unsigned char*)data;
memoryStream.dataSize = dataSize; memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0; memoryStream.currentReadPos = 0;
...@@ -985,9 +1229,40 @@ drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize) ...@@ -985,9 +1229,40 @@ drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize)
return DRWAV_TRUE; return DRWAV_TRUE;
} }
drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
{
if (ppData == NULL) {
return DRWAV_FALSE;
}
*ppData = NULL; // Important because we're using realloc()!
*pDataSize = 0;
drwav__memory_stream_write memoryStreamWrite;
drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
memoryStreamWrite.ppData = ppData;
memoryStreamWrite.pDataSize = pDataSize;
memoryStreamWrite.dataSize = 0;
memoryStreamWrite.dataCapacity = 0;
memoryStreamWrite.currentWritePos = 0;
if (!drwav_init_write(pWav, pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite)) {
return DRWAV_FALSE;
}
pWav->memoryStreamWrite = memoryStreamWrite;
pWav->pUserData = &pWav->memoryStreamWrite;
return DRWAV_TRUE;
}
drwav* drwav_open_memory(const void* data, size_t dataSize) drwav* drwav_open_memory(const void* data, size_t dataSize)
{ {
if (data == NULL || dataSize == 0) {
return NULL;
}
drwav__memory_stream memoryStream; drwav__memory_stream memoryStream;
drwav_zero_memory(&memoryStream, sizeof(memoryStream));
memoryStream.data = (const unsigned char*)data; memoryStream.data = (const unsigned char*)data;
memoryStream.dataSize = dataSize; memoryStream.dataSize = dataSize;
memoryStream.currentReadPos = 0; memoryStream.currentReadPos = 0;
...@@ -1002,6 +1277,33 @@ drwav* drwav_open_memory(const void* data, size_t dataSize) ...@@ -1002,6 +1277,33 @@ drwav* drwav_open_memory(const void* data, size_t dataSize)
return pWav; return pWav;
} }
drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
{
if (ppData == NULL) {
return NULL;
}
*ppData = NULL; // Important because we're using realloc()!
*pDataSize = 0;
drwav__memory_stream_write memoryStreamWrite;
drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
memoryStreamWrite.ppData = ppData;
memoryStreamWrite.pDataSize = pDataSize;
memoryStreamWrite.dataSize = 0;
memoryStreamWrite.dataCapacity = 0;
memoryStreamWrite.currentWritePos = 0;
drwav* pWav = drwav_open_write(pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite);
if (pWav == NULL) {
return NULL;
}
pWav->memoryStreamWrite = memoryStreamWrite;
pWav->pUserData = &pWav->memoryStreamWrite;
return pWav;
}
drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData) drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
{ {
...@@ -1070,7 +1372,7 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS ...@@ -1070,7 +1372,7 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS
return DRWAV_FALSE; return DRWAV_FALSE;
} }
if (drwav__bytes_to_u64(chunkSize) < 84) { if (drwav__bytes_to_u64(chunkSize) < 80) {
return DRWAV_FALSE; return DRWAV_FALSE;
} }
...@@ -1212,16 +1514,174 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS ...@@ -1212,16 +1514,174 @@ drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onS
return DRWAV_TRUE; return DRWAV_TRUE;
} }
drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
{
if (onWrite == NULL || onSeek == NULL) {
return DRWAV_FALSE;
}
// Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this.
if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
return DRWAV_FALSE;
}
if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
return DRWAV_FALSE;
}
drwav_zero_memory(pWav, sizeof(*pWav));
pWav->onWrite = onWrite;
pWav->onSeek = onSeek;
pWav->pUserData = pUserData;
pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
pWav->fmt.channels = (drwav_uint16)pFormat->channels;
pWav->fmt.sampleRate = pFormat->sampleRate;
pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) >> 3);
pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) >> 3);
pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
pWav->fmt.extendedSize = 0;
size_t runningPos = 0;
// "RIFF" chunk.
drwav_uint64 chunkSizeRIFF = 0;
if (pFormat->container == drwav_container_riff) {
runningPos += pWav->onWrite(pUserData, "RIFF", 4);
runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 4);
runningPos += pWav->onWrite(pUserData, "WAVE", 4);
} else {
runningPos += pWav->onWrite(pUserData, drwavGUID_W64_RIFF, 16);
runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 8);
runningPos += pWav->onWrite(pUserData, drwavGUID_W64_WAVE, 16);
}
// "fmt " chunk.
drwav_uint64 chunkSizeFMT;
if (pFormat->container == drwav_container_riff) {
chunkSizeFMT = 16;
runningPos += pWav->onWrite(pUserData, "fmt ", 4);
runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 4);
} else {
chunkSizeFMT = 40;
runningPos += pWav->onWrite(pUserData, drwavGUID_W64_FMT, 16);
runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 8);
}
runningPos += pWav->onWrite(pUserData, &pWav->fmt.formatTag, 2);
runningPos += pWav->onWrite(pUserData, &pWav->fmt.channels, 2);
runningPos += pWav->onWrite(pUserData, &pWav->fmt.sampleRate, 4);
runningPos += pWav->onWrite(pUserData, &pWav->fmt.avgBytesPerSec, 4);
runningPos += pWav->onWrite(pUserData, &pWav->fmt.blockAlign, 2);
runningPos += pWav->onWrite(pUserData, &pWav->fmt.bitsPerSample, 2);
pWav->dataChunkDataPos = runningPos;
pWav->dataChunkDataSize = 0;
// "data" chunk.
drwav_uint64 chunkSizeDATA = 0;
if (pFormat->container == drwav_container_riff) {
runningPos += pWav->onWrite(pUserData, "data", 4);
runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 4);
} else {
runningPos += pWav->onWrite(pUserData, drwavGUID_W64_DATA, 16);
runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 8);
}
// Simple validation.
if (pFormat->container == drwav_container_riff) {
if (runningPos != 20 + chunkSizeFMT + 8) {
return DRWAV_FALSE;
}
} else {
if (runningPos != 40 + chunkSizeFMT + 24) {
return DRWAV_FALSE;
}
}
// Set some properties for the client's convenience.
pWav->container = pFormat->container;
pWav->channels = (drwav_uint16)pFormat->channels;
pWav->sampleRate = pFormat->sampleRate;
pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
pWav->bytesPerSample = (drwav_uint16)(pFormat->bitsPerSample >> 3);
pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
return DRWAV_TRUE;
}
void drwav_uninit(drwav* pWav) void drwav_uninit(drwav* pWav)
{ {
if (pWav == NULL) { if (pWav == NULL) {
return; return;
} }
// If the drwav object was opened in write mode we'll need to finialize a few things:
// - Make sure the "data" chunk is aligned to 16-bits
// - Set the size of the "data" chunk.
if (pWav->onWrite != NULL) {
// Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding.
drwav_uint32 paddingSize = 0;
if (pWav->container == drwav_container_riff) {
paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 2);
} else {
paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 8);
}
if (paddingSize > 0) {
drwav_uint64 paddingData = 0;
pWav->onWrite(pWav->pUserData, &paddingData, paddingSize);
}
// Chunk sizes.
if (pWav->onSeek) {
if (pWav->container == drwav_container_riff) {
// The "RIFF" chunk size.
if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
drwav_uint32 riffChunkSize = 36;
if (pWav->dataChunkDataSize <= (0xFFFFFFFF - 36)) {
riffChunkSize = 36 + (drwav_uint32)pWav->dataChunkDataSize;
} else {
riffChunkSize = 0xFFFFFFFF;
}
pWav->onWrite(pWav->pUserData, &riffChunkSize, 4);
}
// the "data" chunk size.
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {
drwav_uint32 dataChunkSize = 0;
if (pWav->dataChunkDataSize <= 0xFFFFFFFF) {
dataChunkSize = (drwav_uint32)pWav->dataChunkDataSize;
} else {
dataChunkSize = 0xFFFFFFFF;
}
pWav->onWrite(pWav->pUserData, &dataChunkSize, 4);
}
} else {
// The "RIFF" chunk size.
if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
drwav_uint64 riffChunkSize = 80 + 24 + pWav->dataChunkDataSize;
pWav->onWrite(pWav->pUserData, &riffChunkSize, 8);
}
// The "data" chunk size.
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {
drwav_uint64 dataChunkSize = 24 + pWav->dataChunkDataSize; // +24 because W64 includes the size of the GUID and size fields.
pWav->onWrite(pWav->pUserData, &dataChunkSize, 8);
}
}
}
}
#ifndef DR_WAV_NO_STDIO #ifndef DR_WAV_NO_STDIO
// If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file() // If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
// was used by looking at the onRead and onSeek callbacks. // was used by looking at the onRead and onSeek callbacks.
if (pWav->onRead == drwav__on_read_stdio) { if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
fclose((FILE*)pWav->pUserData); fclose((FILE*)pWav->pUserData);
} }
#endif #endif
...@@ -1243,6 +1703,21 @@ drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserDat ...@@ -1243,6 +1703,21 @@ drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserDat
return pWav; return pWav;
} }
drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
{
drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
if (pWav == NULL) {
return NULL;
}
if (!drwav_init_write(pWav, pFormat, onWrite, onSeek, pUserData)) {
DRWAV_FREE(pWav);
return NULL;
}
return pWav;
}
void drwav_close(drwav* pWav) void drwav_close(drwav* pWav)
{ {
drwav_uninit(pWav); drwav_uninit(pWav);
...@@ -1403,6 +1878,34 @@ fallback: ...@@ -1403,6 +1878,34 @@ fallback:
} }
size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
{
if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
return 0;
}
size_t bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
pWav->dataChunkDataSize += bytesWritten;
return bytesWritten;
}
drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData)
{
if (pWav == NULL || samplesToWrite == 0 || pData == NULL) {
return 0;
}
drwav_uint64 bytesToWrite = ((samplesToWrite * pWav->bitsPerSample) / 8);
if (bytesToWrite > SIZE_MAX) {
return 0;
}
size_t bytesWritten = drwav_write_raw(pWav, (size_t)bytesToWrite, pData);
return ((drwav_uint64)bytesWritten * 8) / pWav->bitsPerSample;
}
#ifndef DR_WAV_NO_CONVERSION_API #ifndef DR_WAV_NO_CONVERSION_API
static unsigned short g_drwavAlawTable[256] = { static unsigned short g_drwavAlawTable[256] = {
0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
...@@ -2853,6 +3356,9 @@ void drwav_free(void* pDataReturnedByOpenAndRead) ...@@ -2853,6 +3356,9 @@ void drwav_free(void* pDataReturnedByOpenAndRead)
// REVISION HISTORY // REVISION HISTORY
// //
// v0.7 - 2017-11-04
// - Add writing APIs.
//
// v0.6 - 2017-08-16 // v0.6 - 2017-08-16
// - API CHANGE: Rename dr_* types to drwav_*. // - API CHANGE: Rename dr_* types to drwav_*.
// - Add support for custom implementations of malloc(), realloc(), etc. // - Add support for custom implementations of malloc(), realloc(), etc.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment