The tiny wav I/O module is the most simple wav file I/O module you've ever seen.
Dependents: SimpleWaveRecorderPlayer USBMSD_SD_HelloWorld_FRDM-KL25Z Application-SimpleWaveRecorderPlayerGenerator MbedClock ... more
Revision 0:c853ba46d0b9, committed 2012-04-14
- Comitter:
- shintamainjp
- Date:
- Sat Apr 14 02:13:30 2012 +0000
- Commit message:
- Initial version of the tiny wav I/O module for mbed.
Changed in this revision
wavfile.cpp | Show annotated file Show diff for this revision Revisions of this file |
wavfile.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r c853ba46d0b9 wavfile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wavfile.cpp Sat Apr 14 02:13:30 2012 +0000 @@ -0,0 +1,1021 @@ +/** + * @file wavfile.cpp + * @author Shinichiro Nakamura + */ + +/* + * =============================================================== + * Tiny WAV I/O Module + * Version 0.0.1 + * =============================================================== + * Copyright (c) 2011-2012 Shinichiro Nakamura + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * =============================================================== + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "wavfile.h" + +#define DEBUG printf + +#define CHUNK_ID_RIFF (('R' << 24) | ('I' << 16) | ('F' << 8) | ('F' << 0)) +#define CHUNK_ID_FMT (('f' << 24) | ('m' << 16) | ('t' << 8) | (' ' << 0)) +#define CHUNK_ID_DATA (('d' << 24) | ('a' << 16) | ('t' << 8) | ('a' << 0)) + +#define RIFF_CHUNK_FORMAT_WAVE (('W' << 24) | ('A' << 16) | ('V' << 8) | ('E' << 0)) + +#define BITS_PER_SAMPLE_8 (8) +#define BITS_PER_SAMPLE_16 (16) +#define BITS_PER_SAMPLE_24 (24) + +#define CHUNK_SIZE_FMT_PCM (16) +#define CHUNK_SIZE_FMT_EXTENSIBLE (40) + +#define CHANNEL_MASK_SPEAKER_FRONT_LEFT (0x00000001) +#define CHANNEL_MASK_SPEAKER_FRONT_RIGHT (0x00000002) +#define CHANNEL_MASK_SPEAKER_FRONT_CENTER (0x00000004) +#define CHANNEL_MASK_SPEAKER_LOW_FREQUENCY (0x00000008) +#define CHANNEL_MASK_SPEAKER_BACK_LEFT (0x00000010) +#define CHANNEL_MASK_SPEAKER_BACK_RIGHT (0x00000020) +#define CHANNEL_MASK_SPEAKER_FRONT_LEFT_OF_CENTER (0x00000040) +#define CHANNEL_MASK_SPEAKER_FRONT_RIGHT_OF_CENTER (0x00000080) +#define CHANNEL_MASK_SPEAKER_BACK_CENTER (0x00000100) +#define CHANNEL_MASK_SPEAKER_SIDE_LEFT (0x00000200) +#define CHANNEL_MASK_SPEAKER_SIDE_RIGHT (0x00000400) +#define CHANNEL_MASK_SPEAKER_TOP_CENTER (0x00000800) +#define CHANNEL_MASK_SPEAKER_TOP_FRONT_LEFT (0x00001000) +#define CHANNEL_MASK_SPEAKER_TOP_FRONT_CENTER (0x00002000) +#define CHANNEL_MASK_SPEAKER_TOP_FRONT_RIGHT (0x00004000) +#define CHANNEL_MASK_SPEAKER_TOP_BACK_LEFT (0x00008000) +#define CHANNEL_MASK_SPEAKER_TOP_BACK_CENTER (0x00010000) +#define CHANNEL_MASK_SPEAKER_TOP_BACK_RIGHT (0x00020000) +#define CHANNEL_MASK_SPEAKER_RESERVED (0x80000000) + +struct WAVFILE { + FILE *fp; + char filename[BUFSIZ]; + WavFileMode mode; + bool info_checked; + bool data_checked; + uint32_t data_byte_count; + wavfile_info_t info; +}; + +static int WRITE_U32_BE(FILE *fp, const uint32_t value) { + for (int i = 0; i < 4; i++) { + if (fputc((value >> (8 * (3 - i))), fp) == EOF) { + return -1; + } + } + return 0; +} + +static int WRITE_U32_LE(FILE *fp, const uint32_t value) { + for (int i = 0; i < 4; i++) { + if (fputc((value >> (8 * i)), fp) == EOF) { + return -1; + } + } + return 0; +} + +static int WRITE_U16_LE(FILE *fp, const uint16_t value) { + for (int i = 0; i < 2; i++) { + if (fputc((value >> (8 * i)), fp) == EOF) { + return -1; + } + } + return 0; +} + +static int READ_U32_BE(FILE *fp, uint32_t *value) { + int raw[4]; + for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) { + raw[i] = fgetc(fp); + if (raw[i] == EOF) { + *value = 0x00000000; + return -1; + } + } + *value = + ((uint32_t)raw[0] << 24) | + ((uint32_t)raw[1] << 16) | + ((uint32_t)raw[2] << 8) | + ((uint32_t)raw[3] << 0); + return 0; +} + +static int READ_U32_LE(FILE *fp, uint32_t *value) { + int raw[4]; + for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) { + raw[i] = fgetc(fp); + if (raw[i] == EOF) { + *value = 0x00000000; + return -1; + } + } + *value = + ((uint32_t)raw[3] << 24) | + ((uint32_t)raw[2] << 16) | + ((uint32_t)raw[1] << 8) | + ((uint32_t)raw[0] << 0); + return 0; +} + +static int READ_U16_LE(FILE *fp, uint16_t *value) { + int raw[2]; + for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) { + raw[i] = fgetc(fp); + if (raw[i] == EOF) { + *value = 0x00000000; + return -1; + } + } + *value = + ((uint16_t)raw[1] << 8) | + ((uint16_t)raw[0] << 0); + return 0; +} + +static WavFileResult chunk_reader_unknown( + const uint32_t chunk_id, + const uint32_t chunk_size, + FILE *fp) { + for (int i = 0; i < (int)chunk_size; i++) { + int c = fgetc(fp); + if (c == EOF) { + return WavFileResultErrorBrokenChunkData; + } + } + return WavFileResultOK; +} + +static WavFileResult chunk_reader_riff( + const uint32_t chunk_id, + const uint32_t chunk_size, + FILE *fp, + uint32_t *format_id) { + if (READ_U32_BE(fp, format_id) != 0) { + return WavFileResultErrorBrokenFormatId; + } + return WavFileResultOK; +} + +static WavFileResult chunk_reader_fmt( + const uint32_t chunk_id, + const uint32_t chunk_size, + FILE *fp, + uint16_t *audio_format, + uint16_t *num_channels, + uint32_t *sample_rate, + uint32_t *byte_rate, + uint16_t *block_align, + uint16_t *bits_per_sample) { + uint32_t read_byte_count = 0; + + /* + * 2 + */ + if (read_byte_count < chunk_size) { + if (READ_U16_LE(fp, audio_format) != 0) { + return WavFileResultErrorBrokenAudioFormat; + } + } + read_byte_count+=2; + + /* + * 2 + 2 + */ + if (read_byte_count < chunk_size) { + if (READ_U16_LE(fp, num_channels) != 0) { + return WavFileResultErrorBrokenNumChannels; + } + } + read_byte_count+=2; + + /* + * 2 + 2 + 4 + */ + if (read_byte_count < chunk_size) { + if (READ_U32_LE(fp, sample_rate) != 0) { + return WavFileResultErrorBrokenSampleRate; + } + } + read_byte_count+=4; + + /* + * 2 + 2 + 4 + 4 + */ + if (read_byte_count < chunk_size) { + if (READ_U32_LE(fp, byte_rate) != 0) { + return WavFileResultErrorBrokenByteRate; + } + } + read_byte_count+=4; + + /* + * 2 + 2 + 4 + 4 + 2 + */ + if (read_byte_count < chunk_size) { + if (READ_U16_LE(fp, block_align) != 0) { + return WavFileResultErrorBrokenBlockAlign; + } + } + read_byte_count+=2; + + /* + * 2 + 2 + 4 + 4 + 2 + 2 + */ + if (read_byte_count < chunk_size) { + if (READ_U16_LE(fp, bits_per_sample) != 0) { + return WavFileResultErrorBrokenBitsPerSample; + } + } + read_byte_count+=2; + + /* + * 2 + 2 + 4 + 4 + 2 + 2 + */ + while (read_byte_count < chunk_size) { + if (fgetc(fp) == EOF) { + return WavFileResultErrorBrokenChunkData; + } + read_byte_count++; + } + + return WavFileResultOK; +} + +WAVFILE *wavfile_open(const char *filename, WavFileMode mode, WavFileResult *result) { + /* + * Verify the filename. + */ + if (filename == NULL) { + *result = WavFileResultErrorInvalidFileName; + return NULL; + } + + /* + * Open the file. + */ + FILE *fp = NULL; + switch (mode) { + case WavFileModeRead: + fp = fopen(filename, "rb"); + break; + case WavFileModeWrite: + fp = fopen(filename, "wb"); + break; + default: + fp = NULL; + break; + } + if (fp == NULL) { + *result = WavFileResultErrorFileOpen; + return NULL; + } + + /* + * Allocate the handler. + */ + WAVFILE *p = (WAVFILE *)malloc(sizeof(WAVFILE)); + if (p == NULL) { + *result = WavFileResultErrorMemoryAllocation; + return NULL; + } + + /* + * Fill the fields. + */ + p->fp = fp; + strcpy(p->filename, filename); + p->mode = mode; + p->info_checked = false; + p->data_checked = false; + p->data_byte_count = 0; + WAVFILE_INFO_AUDIO_FORMAT(&(p->info)) = 0; + WAVFILE_INFO_NUM_CHANNELS(&(p->info)) = 0; + WAVFILE_INFO_SAMPLE_RATE(&(p->info)) = 0; + WAVFILE_INFO_BYTE_RATE(&(p->info)) = 0; + WAVFILE_INFO_BLOCK_ALIGN(&(p->info)) = 0; + WAVFILE_INFO_BITS_PER_SAMPLE(&(p->info)) = 0; + + *result = WavFileResultOK; + return p; +} + +WavFileResult wavfile_read_info(WAVFILE *p, wavfile_info_t *info) { + WavFileResult result = WavFileResultOK; + + if (p == NULL) { + result = WavFileResultErrorInvalidHandler; + goto finalize; + } + + if (p->info_checked) { + result = WavFileResultErrorAlreadyInfoChecked; + goto finalize; + } + if (p->data_checked) { + result = WavFileResultErrorAlreadyDataChecked; + goto finalize; + } + if (p->mode != WavFileModeRead) { + result = WavFileResultErrorInvalidMode; + goto finalize; + } + + while (1) { + uint32_t chunk_id; + uint32_t chunk_size; + + /* + * Get the chunk ID. + */ + if (READ_U32_BE(p->fp, &chunk_id) != 0) { + if (feof(p->fp)) { + /* + * + */ + result = WavFileResultErrorNoDataChunk; + goto finalize; + } else { + result = WavFileResultErrorBrokenChunkId; + goto finalize; + } + } + + /* + * Verify the chunk size. + */ + if (READ_U32_LE(p->fp, &chunk_size) != 0) { + result = WavFileResultErrorBrokenChunkSize; + goto finalize; + } + +#if WAVFILE_DEBUG_ENABLED + /* + * + */ + DEBUG("chunk_id(0x%04X-%c%c%c%c), chunk_size(%d bytes)\n", + chunk_id, + (chunk_id >> (8 * 3)), + (chunk_id >> (8 * 2)), + (chunk_id >> (8 * 1)), + (chunk_id >> (8 * 0)), + chunk_size); +#endif + + /* + */ + switch (chunk_id) { + case CHUNK_ID_RIFF: { + uint32_t format_id; + result = chunk_reader_riff( + chunk_id, + chunk_size, + p->fp, + &format_id); + +#if WAVFILE_DEBUG_ENABLED + /* + */ + DEBUG("\tformat_id(%d)\n", format_id); +#endif + + if (format_id != RIFF_CHUNK_FORMAT_WAVE) { + return WavFileResultErrorInvalidFormatId; + } + if (result != WavFileResultOK) { + goto finalize; + } + } + break; + case CHUNK_ID_FMT: { + result = chunk_reader_fmt( + chunk_id, + chunk_size, + p->fp, + &(p->info.audio_format), + &(p->info.num_channels), + &(p->info.sample_rate), + &(p->info.byte_rate), + &(p->info.block_align), + &(p->info.bits_per_sample)); + + info->audio_format = p->info.audio_format; + info->num_channels = p->info.num_channels; + info->sample_rate = p->info.sample_rate; + info->byte_rate = p->info.byte_rate; + info->block_align = p->info.block_align; + info->bits_per_sample = p->info.bits_per_sample; + +#if WAVFILE_DEBUG_ENABLED + /* + */ + DEBUG("\taudio_format(%d)\n", p->info.audio_format); + DEBUG("\tnum_channels(%d)\n", p->info.num_channels); + DEBUG("\tsample_rate(%d)\n", p->info.sample_rate); + DEBUG("\tbyte_rate(%d)\n", p->info.byte_rate); + DEBUG("\tblock_align(%d)\n", p->info.block_align); + DEBUG("\tbits_per_sample(%d)\n", p->info.bits_per_sample); +#endif + + if ((p->info.audio_format != WAVFILE_AUDIO_FORMAT_PCM) && (info->audio_format != WAVFILE_AUDIO_FORMAT_EXTENSIBLE)) { + return WavFileResultErrorInvalidAudioFormat; + } + if (result != WavFileResultOK) { + goto finalize; + } + } + break; + case CHUNK_ID_DATA: { + p->info_checked = true; + p->data_byte_count = chunk_size; + goto finalize; + } + break; + default: { + result = chunk_reader_unknown(chunk_id, chunk_size, p->fp); + if (result != WavFileResultOK) { + goto finalize; + } + } + break; + } + } + +finalize: + return result; +} + +/** + */ +WavFileResult wavfile_read_data(WAVFILE *p, wavfile_data_t *data) { + if (p == NULL) { + return WavFileResultErrorInvalidHandler; + } + + if (!p->info_checked) { + return WavFileResultErrorNeedInfoChecked; + } + + if (p->mode != WavFileModeRead) { + return WavFileResultErrorInvalidMode; + } + + if (p->data_byte_count == 0) { + data->num_channels = 0; + for (int i = 0; i < p->info.num_channels; i++) { + data->channel_data[i] = 0.5; + } + return WavFileResultOK; + } + + data->num_channels = p->info.num_channels; + for (int i = 0; i < p->info.num_channels; i++) { + switch (p->info.bits_per_sample) { + case BITS_PER_SAMPLE_8: { + int c = fgetc(p->fp); + if (c == EOF) { + return WavFileResultErrorBrokenChunkData; + } + data->channel_data[i] = (double)c / 0xFF; + } + p->data_byte_count-=1; + break; + case BITS_PER_SAMPLE_16: { +#if 0 + int c1 = fgetc(p->fp); + if (c1 == EOF) { + return WavFileResultErrorBrokenChunkData; + } + int c2 = fgetc(p->fp); + if (c2 == EOF) { + return WavFileResultErrorBrokenChunkData; + } + uint16_t n = (((uint16_t)c2 << 8) | ((uint16_t)c1 << 0)) ^ (1 << 15); + data->channel_data[i] = (double)n / 0xFFFF; +#else + char tmp[2]; + fread(tmp, 2, 1, p->fp); + uint16_t n = (((uint16_t)tmp[1] << 8) | ((uint16_t)tmp[0] << 0)) ^ (1 << 15); + data->channel_data[i] = (double)n / 0xFFFF; +#endif + } + p->data_byte_count-=2; + break; + case BITS_PER_SAMPLE_24: { + #if 0 + int c1 = fgetc(p->fp); + if (c1 == EOF) { + return WavFileResultErrorBrokenChunkData; + } + int c2 = fgetc(p->fp); + if (c2 == EOF) { + return WavFileResultErrorBrokenChunkData; + } + int c3 = fgetc(p->fp); + if (c3 == EOF) { + return WavFileResultErrorBrokenChunkData; + } + uint32_t n = (((uint32_t)c3 << 16) | ((uint32_t)c2 << 8) | ((uint32_t)c1 << 0)) ^ (1 << 23); + data->channel_data[i] = (double)n / 0xFFFFFF; +#else + char tmp[3]; + fread(tmp, 3, 1, p->fp); + uint32_t n = (((uint32_t)tmp[2] << 16) | ((uint32_t)tmp[1] << 8) | ((uint32_t)tmp[0] << 0)) ^ (1 << 23); + data->channel_data[i] = (double)n / 0xFFFFFF; +#endif + } + p->data_byte_count-=3; + break; + default: + return WavFileResultErrorUnsupportedBitsPerSample; + } + } + return WavFileResultOK; +} + +WavFileResult wavfile_write_info(WAVFILE *p, const wavfile_info_t *info) { + WavFileResult result = WavFileResultOK; + + if (p == NULL) { + result = WavFileResultErrorInvalidHandler; + goto finalize; + } + + if (p->info_checked) { + result = WavFileResultErrorAlreadyInfoChecked; + goto finalize; + } + + if (p->mode != WavFileModeWrite) { + result = WavFileResultErrorInvalidMode; + goto finalize; + } + + p->info.audio_format = info->audio_format; + p->info.num_channels = info->num_channels; + p->info.sample_rate = info->sample_rate; + p->info.byte_rate = info->byte_rate; + p->info.block_align = info->block_align; + p->info.bits_per_sample = info->bits_per_sample; + + /* + * + */ + + if ((info->audio_format != WAVFILE_AUDIO_FORMAT_PCM) && (info->audio_format != WAVFILE_AUDIO_FORMAT_EXTENSIBLE)) { + result = WavFileResultErrorInvalidAudioFormat; + goto finalize; + } + + if ((info->bits_per_sample != BITS_PER_SAMPLE_8) + && (info->bits_per_sample != BITS_PER_SAMPLE_16) + && (info->bits_per_sample != BITS_PER_SAMPLE_24)) { + result = WavFileResultErrorUnsupportedBitsPerSample; + goto finalize; + } + + if ((info->num_channels * info->sample_rate * (info->bits_per_sample / 8)) != info->byte_rate) { + result = WavFileResultErrorInvalidByteRate; + goto finalize; + } + + /* + * [RIFF] + * ------------------------------------------ + * Big endian 4 bytes : Chunk ID + * Little endian 4 bytes : Chunk size + * Big endian 4 bytes : Format + * ------------------------------------------ + */ + if (WRITE_U32_BE(p->fp, CHUNK_ID_RIFF) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, 0x00000000) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_BE(p->fp, RIFF_CHUNK_FORMAT_WAVE) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + + /* + * [fmt] + * ------------------------------------------ + * Big endian 4 bytes : Sub chunk ID + * Little endian 4 bytes : Sub chunk size + * Little endian 2 bytes : Audio format + * Little endian 2 bytes : Number of channels + * Little endian 4 bytes : Sample rate + * Little endian 4 bytes : Byte rate + * Little endian 2 bytes : Block align + * Little endian 2 bytes : Bits per sample + * . . + * . Additional bytes here (extensible) . + * . . + * ------------------------------------------ + */ + switch (info->audio_format) { + case WAVFILE_AUDIO_FORMAT_PCM: { + if (WRITE_U32_BE(p->fp, CHUNK_ID_FMT) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, CHUNK_SIZE_FMT_PCM) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->audio_format) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->num_channels) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, info->sample_rate) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, info->byte_rate) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->block_align) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + } + break; + case WAVFILE_AUDIO_FORMAT_EXTENSIBLE: { + if (WRITE_U32_BE(p->fp, CHUNK_ID_FMT) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, CHUNK_SIZE_FMT_EXTENSIBLE) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->audio_format) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->num_channels) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, info->sample_rate) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, info->byte_rate) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->block_align) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + /* + * Additional bytes for the extensible format. + * + * 2 bytes : Size of the extension (0 or 22) + * 2 bytes : Number of valid bits + * 4 bytes : Speaker position mask + * 16 bytes : GUID, including the data format code + */ + if (WRITE_U16_LE(p->fp, 22) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, 0x00000000) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + static const unsigned char sub_format[16] = { + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xAA, + 0x00, 0x38, 0x9B, 0x71 + }; + for (int i = 0; i < sizeof(sub_format); i++) { + fputc((char)sub_format[i], p->fp); + } + } + break; + default: + result = WavFileResultErrorInvalidAudioFormat; + goto finalize; + } + + /* + * [data] + * ------------------------------------------ + * Big endian 4 bytes : Sub chunk ID + * Little endian 4 bytes : Sub chunk size + * ------------------------------------------ + * Little endian 2 bytes : Sample 1 (Ch.1) + * Little endian 2 bytes : Sample 1 (Ch.2) + * . + * . + * . + * Little endian 2 bytes : Sample 1 (Ch.N) + * ------------------------------------------ + * Little endian 2 bytes : Sample 2 (Ch.1) + * Little endian 2 bytes : Sample 2 (Ch.2) + * . + * . + * . + * Little endian 2 bytes : Sample 2 (Ch.N) + * ------------------------------------------ + */ + if (WRITE_U32_BE(p->fp, CHUNK_ID_DATA) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, 0x00000000) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + +finalize: + if (WavFileResultOK == result) { + p->info_checked = true; + } + return result; +} + +/** + */ +WavFileResult wavfile_write_data(WAVFILE *p, const wavfile_data_t *data) { + WavFileResult result = WavFileResultOK; + + if (p == NULL) { + result = WavFileResultErrorInvalidHandler; + goto finalize; + } + + if (!p->info_checked) { + result = WavFileResultErrorNeedInfoChecked; + goto finalize; + } + + if (p->mode != WavFileModeWrite) { + result = WavFileResultErrorInvalidMode; + goto finalize; + } + + if (p->info.num_channels != data->num_channels) { + result = WavFileResultErrorInvalidNumChannels; + goto finalize; + } + + for (int i = 0; i < p->info.num_channels; i++) { + switch (p->info.bits_per_sample) { + case BITS_PER_SAMPLE_8: { + int n = (int)((double)data->channel_data[i] * 0xFF); + if (n < 0x00) { + n = 0x00; + } + if (0xFF < n) { + n = 0xFF; + } + fputc((char)n, p->fp); + } + p->data_byte_count+=1; + break; + case BITS_PER_SAMPLE_16: { + int n = (int)((double)(data->channel_data[i] * 0xFFFF) - 0x8000); + if (0x7FFF < n) { + n = 0x7FFF; + } + if (n < -0x8000) { + n = -0x8000; + } + fputc(((uint16_t)n >> 0) & 0xff, p->fp); + fputc(((uint16_t)n >> 8) & 0xff, p->fp); + } + p->data_byte_count+=2; + break; + case BITS_PER_SAMPLE_24: { + int n = (int)((double)(data->channel_data[i] * 0xFFFFFF) - 0x800000); + if (0x7FFFFF < n) { + n = 0x7FFFFF; + } + if (n < -0x800000) { + n = -0x800000; + } + fputc(((uint32_t)n >> 0) & 0xff, p->fp); + fputc(((uint32_t)n >> 8) & 0xff, p->fp); + fputc(((uint32_t)n >> 16) & 0xff, p->fp); + } + p->data_byte_count+=3; + break; + } + } + p->data_checked = true; + +finalize: + return result; +} + +WavFileResult wavfile_close(WAVFILE *p) { + WavFileResult result = WavFileResultOK; + + switch (p->mode) { + case WavFileModeRead: + break; + case WavFileModeWrite: + if (p->info_checked && p->data_checked) { + switch (p->info.audio_format) { + case WAVFILE_AUDIO_FORMAT_PCM: { + /* + * Fill the RIFF chunk size. + */ + if (fseek(p->fp, 4L, SEEK_SET) == -1) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, 4 + (8 + CHUNK_SIZE_FMT_PCM) + (8 + p->data_byte_count)) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + + /* + * Fill the data sub chunk size. + */ + if (fseek(p->fp, 12 + (8 + CHUNK_SIZE_FMT_PCM) + 4, SEEK_SET) == -1) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, p->data_byte_count) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + } + break; + case WAVFILE_AUDIO_FORMAT_EXTENSIBLE: { + /* + * Fill the RIFF chunk size. + */ + if (fseek(p->fp, 4L, SEEK_SET) == -1) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, 4 + (8 + CHUNK_SIZE_FMT_EXTENSIBLE) + (8 + p->data_byte_count)) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + + /* + * Fill the data sub chunk size. + */ + if (fseek(p->fp, 12 + (8 + CHUNK_SIZE_FMT_EXTENSIBLE) + 4, SEEK_SET) == -1) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + if (WRITE_U32_LE(p->fp, p->data_byte_count) != 0) { + result = WavFileResultErrorFileWrite; + goto finalize; + } + } + break; + } + } + break; + } + +finalize: + fclose(p->fp); + free(p); + return result; +} + +void wavfile_result_string(const WavFileResult result, char *buf, size_t siz) { + switch (result) { + case WavFileResultOK: + strcpy(buf, "OK."); + break; + case WavFileResultErrorInvalidFileName: + strcpy(buf, "Invalid file name found."); + break; + case WavFileResultErrorMemoryAllocation: + strcpy(buf, "Memory allocation error."); + break; + case WavFileResultErrorFileOpen: + strcpy(buf, "File open error found."); + break; + case WavFileResultErrorFileWrite: + strcpy(buf, "File write error found."); + break; + case WavFileResultErrorBrokenChunkId: + strcpy(buf, "Broken chunk ID found."); + break; + case WavFileResultErrorBrokenChunkSize: + strcpy(buf, "Borken chunk size found."); + break; + case WavFileResultErrorBrokenChunkData: + strcpy(buf, "Borken chunk data found."); + break; + case WavFileResultErrorBrokenFormatId: + strcpy(buf, "Broken format ID found."); + break; + case WavFileResultErrorInvalidFormatId: + strcpy(buf, "Invalid format ID found."); + break; + case WavFileResultErrorBrokenAudioFormat: + strcpy(buf, "Broken audio format found."); + break; + case WavFileResultErrorInvalidAudioFormat: + strcpy(buf, "Invalid audio format found."); + break; + case WavFileResultErrorInvalidNumChannels: + strcpy(buf, "Invalid number of channels found."); + break; + case WavFileResultErrorBrokenNumChannels: + strcpy(buf, "Broken number of channels found."); + break; + case WavFileResultErrorBrokenSampleRate: + strcpy(buf, "Broken sample rate found."); + break; + case WavFileResultErrorBrokenByteRate: + strcpy(buf, "Broken byte rate found."); + break; + case WavFileResultErrorInvalidByteRate: + strcpy(buf, "Invalid byte rate found."); + break; + case WavFileResultErrorBrokenBlockAlign: + strcpy(buf, "Broken block alignment found."); + break; + case WavFileResultErrorBrokenBitsPerSample: + strcpy(buf, "Broken bits per sample found."); + break; + case WavFileResultErrorUnsupportedBitsPerSample: + strcpy(buf, "Unsupported bits per sample found."); + break; + case WavFileResultErrorAlreadyInfoChecked: + strcpy(buf, "Already checked info."); + break; + case WavFileResultErrorAlreadyDataChecked: + strcpy(buf, "Already checked data."); + break; + case WavFileResultErrorNoDataChunk: + strcpy(buf, "No data chunk."); + break; + case WavFileResultErrorInvalidMode: + strcpy(buf, "Invalid mode."); + break; + case WavFileResultErrorNeedInfoChecked: + strcpy(buf, "Need check info."); + break; + case WavFileResultErrorNeedDataChecked: + strcpy(buf, "Need check data."); + break; + case WavFileResultErrorInvalidHandler: + strcpy(buf, "Invalid handler."); + break; + default: + strcpy(buf, "Unkonwn error found."); + break; + } +} +
diff -r 000000000000 -r c853ba46d0b9 wavfile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wavfile.h Sat Apr 14 02:13:30 2012 +0000 @@ -0,0 +1,123 @@ +/** + * @file wavfile.h + * @author Shinichiro Nakamura + */ + +/* + * =============================================================== + * Tiny WAV I/O Module + * Version 0.0.1 + * =============================================================== + * Copyright (c) 2011-2012 Shinichiro Nakamura + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * =============================================================== + */ + +#ifndef WAVFILE_H +#define WAVFILE_H + +#include <stdio.h> +#include <stdint.h> + +#define WAVFILE_AUDIO_FORMAT_PCM (1) +#define WAVFILE_AUDIO_FORMAT_EXTENSIBLE (65534) +#define WAVFILE_MAXIMUM_CHANNELS (32) + +#define WAVFILE_INFO_AUDIO_FORMAT(P) ((P)->audio_format) +#define WAVFILE_INFO_NUM_CHANNELS(P) ((P)->num_channels) +#define WAVFILE_INFO_SAMPLE_RATE(P) ((P)->sample_rate) +#define WAVFILE_INFO_BYTE_RATE(P) ((P)->byte_rate) +#define WAVFILE_INFO_BLOCK_ALIGN(P) ((P)->block_align) +#define WAVFILE_INFO_BITS_PER_SAMPLE(P) ((P)->bits_per_sample) + +#define WAVFILE_DATA_IS_END_OF_DATA(P) ((P)->num_channels == 0) +#define WAVFILE_DATA_NUM_CHANNELS(P) ((P)->num_channels) +#define WAVFILE_DATA_CHANNEL_DATA(P,CH) ((P)->channel_data[CH]) + +#define WAVFILE_DEBUG_ENABLED (0) + +typedef struct WAVFILE WAVFILE; + +typedef struct { + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; + uint16_t block_align; + uint16_t bits_per_sample; +} wavfile_info_t; + +typedef struct { + uint16_t num_channels; + double channel_data[WAVFILE_MAXIMUM_CHANNELS]; +} wavfile_data_t; + +/** + */ +enum WavFileResult { + WavFileResultOK, + WavFileResultErrorInvalidFileName, + WavFileResultErrorMemoryAllocation, + WavFileResultErrorFileOpen, + WavFileResultErrorFileWrite, + WavFileResultErrorBrokenChunkId, + WavFileResultErrorBrokenChunkSize, + WavFileResultErrorBrokenChunkData, + WavFileResultErrorBrokenFormatId, + WavFileResultErrorInvalidFormatId, + WavFileResultErrorBrokenAudioFormat, + WavFileResultErrorInvalidAudioFormat, + WavFileResultErrorInvalidNumChannels, + WavFileResultErrorBrokenNumChannels, + WavFileResultErrorBrokenSampleRate, + WavFileResultErrorBrokenByteRate, + WavFileResultErrorInvalidByteRate, + WavFileResultErrorBrokenBlockAlign, + WavFileResultErrorBrokenBitsPerSample, + WavFileResultErrorUnsupportedBitsPerSample, + WavFileResultErrorAlreadyInfoChecked, + WavFileResultErrorAlreadyDataChecked, + WavFileResultErrorNoDataChunk, + WavFileResultErrorInvalidMode, + WavFileResultErrorNeedInfoChecked, + WavFileResultErrorNeedDataChecked, + WavFileResultErrorInvalidHandler, +}; + +/** + */ +enum WavFileMode { + WavFileModeRead, + WavFileModeWrite, +}; + +WAVFILE *wavfile_open(const char *filename, WavFileMode mode, WavFileResult *result); +WavFileResult wavfile_read_info(WAVFILE *p, wavfile_info_t *info); +WavFileResult wavfile_read_data(WAVFILE *p, wavfile_data_t *data); +WavFileResult wavfile_write_info(WAVFILE *p, const wavfile_info_t *info); +WavFileResult wavfile_write_data(WAVFILE *p, const wavfile_data_t *data); +WavFileResult wavfile_close(WAVFILE *p); +void wavfile_result_string(const WavFileResult result, char *buf, size_t siz); + +#endif +