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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers wavfile.cpp Source File

wavfile.cpp

Go to the documentation of this file.
00001 /**
00002  * @file wavfile.cpp
00003  * @author Shinichiro Nakamura
00004  */
00005 
00006 /*
00007  * ===============================================================
00008  *  Tiny WAV I/O Module
00009  *  Version 0.0.1
00010  * ===============================================================
00011  * Copyright (c) 2011-2012 Shinichiro Nakamura
00012  *
00013  * Permission is hereby granted, free of charge, to any person
00014  * obtaining a copy of this software and associated documentation
00015  * files (the "Software"), to deal in the Software without
00016  * restriction, including without limitation the rights to use,
00017  * copy, modify, merge, publish, distribute, sublicense, and/or
00018  * sell copies of the Software, and to permit persons to whom the
00019  * Software is furnished to do so, subject to the following
00020  * conditions:
00021  *
00022  * The above copyright notice and this permission notice shall be
00023  * included in all copies or substantial portions of the Software.
00024  *
00025  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00026  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
00027  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00028  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
00029  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
00030  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00031  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00032  * OTHER DEALINGS IN THE SOFTWARE.
00033  * ===============================================================
00034  */
00035 
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include "wavfile.h "
00040 
00041 #define DEBUG printf
00042 
00043 #define CHUNK_ID_RIFF           (('R' << 24) | ('I' << 16) | ('F' << 8) | ('F' << 0))
00044 #define CHUNK_ID_FMT            (('f' << 24) | ('m' << 16) | ('t' << 8) | (' ' << 0))
00045 #define CHUNK_ID_DATA           (('d' << 24) | ('a' << 16) | ('t' << 8) | ('a' << 0))
00046 
00047 #define RIFF_CHUNK_FORMAT_WAVE  (('W' << 24) | ('A' << 16) | ('V' << 8) | ('E' << 0))
00048 
00049 #define BITS_PER_SAMPLE_8           (8)
00050 #define BITS_PER_SAMPLE_16          (16)
00051 #define BITS_PER_SAMPLE_24          (24)
00052 
00053 #define CHUNK_SIZE_FMT_PCM          (16)
00054 #define CHUNK_SIZE_FMT_EXTENSIBLE   (40)
00055 
00056 #define CHANNEL_MASK_SPEAKER_FRONT_LEFT             (0x00000001)
00057 #define CHANNEL_MASK_SPEAKER_FRONT_RIGHT            (0x00000002)
00058 #define CHANNEL_MASK_SPEAKER_FRONT_CENTER           (0x00000004)
00059 #define CHANNEL_MASK_SPEAKER_LOW_FREQUENCY          (0x00000008)
00060 #define CHANNEL_MASK_SPEAKER_BACK_LEFT              (0x00000010)
00061 #define CHANNEL_MASK_SPEAKER_BACK_RIGHT             (0x00000020)
00062 #define CHANNEL_MASK_SPEAKER_FRONT_LEFT_OF_CENTER   (0x00000040)
00063 #define CHANNEL_MASK_SPEAKER_FRONT_RIGHT_OF_CENTER  (0x00000080)
00064 #define CHANNEL_MASK_SPEAKER_BACK_CENTER            (0x00000100)
00065 #define CHANNEL_MASK_SPEAKER_SIDE_LEFT              (0x00000200)
00066 #define CHANNEL_MASK_SPEAKER_SIDE_RIGHT             (0x00000400)
00067 #define CHANNEL_MASK_SPEAKER_TOP_CENTER             (0x00000800)
00068 #define CHANNEL_MASK_SPEAKER_TOP_FRONT_LEFT         (0x00001000)
00069 #define CHANNEL_MASK_SPEAKER_TOP_FRONT_CENTER       (0x00002000)
00070 #define CHANNEL_MASK_SPEAKER_TOP_FRONT_RIGHT        (0x00004000)
00071 #define CHANNEL_MASK_SPEAKER_TOP_BACK_LEFT          (0x00008000)
00072 #define CHANNEL_MASK_SPEAKER_TOP_BACK_CENTER        (0x00010000)
00073 #define CHANNEL_MASK_SPEAKER_TOP_BACK_RIGHT         (0x00020000)
00074 #define CHANNEL_MASK_SPEAKER_RESERVED               (0x80000000)
00075 
00076 struct WAVFILE {
00077     FILE *fp;
00078     char filename[BUFSIZ];
00079     WavFileMode mode;
00080     bool info_checked;
00081     bool data_checked;
00082     uint32_t data_byte_count;
00083     wavfile_info_t info;
00084 };
00085 
00086 static int WRITE_U32_BE(FILE *fp, const uint32_t value) {
00087     for (int i = 0; i < 4; i++) {
00088         if (fputc((value >> (8 * (3 - i))), fp) == EOF) {
00089             return -1;
00090         }
00091     }
00092     return 0;
00093 }
00094 
00095 static int WRITE_U32_LE(FILE *fp, const uint32_t value) {
00096     for (int i = 0; i < 4; i++) {
00097         if (fputc((value >> (8 * i)), fp) == EOF) {
00098             return -1;
00099         }
00100     }
00101     return 0;
00102 }
00103 
00104 static int WRITE_U16_LE(FILE *fp, const uint16_t value) {
00105     for (int i = 0; i < 2; i++) {
00106         if (fputc((value >> (8 * i)), fp) == EOF) {
00107             return -1;
00108         }
00109     }
00110     return 0;
00111 }
00112 
00113 static int READ_U32_BE(FILE *fp, uint32_t *value) {
00114     int raw[4];
00115     for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) {
00116         raw[i] = fgetc(fp);
00117         if (raw[i] == EOF) {
00118             *value = 0x00000000;
00119             return -1;
00120         }
00121     }
00122     *value =
00123         ((uint32_t)raw[0] << 24) |
00124         ((uint32_t)raw[1] << 16) |
00125         ((uint32_t)raw[2] <<  8) |
00126         ((uint32_t)raw[3] <<  0);
00127     return 0;
00128 }
00129 
00130 static int READ_U32_LE(FILE *fp, uint32_t *value) {
00131     int raw[4];
00132     for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) {
00133         raw[i] = fgetc(fp);
00134         if (raw[i] == EOF) {
00135             *value = 0x00000000;
00136             return -1;
00137         }
00138     }
00139     *value =
00140         ((uint32_t)raw[3] << 24) |
00141         ((uint32_t)raw[2] << 16) |
00142         ((uint32_t)raw[1] <<  8) |
00143         ((uint32_t)raw[0] <<  0);
00144     return 0;
00145 }
00146 
00147 static int READ_U16_LE(FILE *fp, uint16_t *value) {
00148     int raw[2];
00149     for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) {
00150         raw[i] = fgetc(fp);
00151         if (raw[i] == EOF) {
00152             *value = 0x00000000;
00153             return -1;
00154         }
00155     }
00156     *value =
00157         ((uint16_t)raw[1] <<  8) |
00158         ((uint16_t)raw[0] <<  0);
00159     return 0;
00160 }
00161 
00162 static WavFileResult chunk_reader_unknown(
00163     const uint32_t chunk_id,
00164     const uint32_t chunk_size,
00165     FILE *fp) {
00166     for (int i = 0; i < (int)chunk_size; i++) {
00167         int c = fgetc(fp);
00168         if (c == EOF) {
00169             return WavFileResultErrorBrokenChunkData;
00170         }
00171     }
00172     return WavFileResultOK;
00173 }
00174 
00175 static WavFileResult chunk_reader_riff(
00176     const uint32_t chunk_id,
00177     const uint32_t chunk_size,
00178     FILE *fp,
00179     uint32_t *format_id) {
00180     if (READ_U32_BE(fp, format_id) != 0) {
00181         return WavFileResultErrorBrokenFormatId;
00182     }
00183     return WavFileResultOK;
00184 }
00185 
00186 static WavFileResult chunk_reader_fmt(
00187     const uint32_t chunk_id,
00188     const uint32_t chunk_size,
00189     FILE *fp,
00190     uint16_t *audio_format,
00191     uint16_t *num_channels,
00192     uint32_t *sample_rate,
00193     uint32_t *byte_rate,
00194     uint16_t *block_align,
00195     uint16_t *bits_per_sample) {
00196     uint32_t read_byte_count = 0;
00197 
00198     /*
00199      * 2
00200      */
00201     if (read_byte_count < chunk_size) {
00202         if (READ_U16_LE(fp, audio_format) != 0) {
00203             return WavFileResultErrorBrokenAudioFormat;
00204         }
00205     }
00206     read_byte_count+=2;
00207 
00208     /*
00209      * 2 + 2
00210      */
00211     if (read_byte_count < chunk_size) {
00212         if (READ_U16_LE(fp, num_channels) != 0) {
00213             return WavFileResultErrorBrokenNumChannels;
00214         }
00215     }
00216     read_byte_count+=2;
00217 
00218     /*
00219      * 2 + 2 + 4
00220      */
00221     if (read_byte_count < chunk_size) {
00222         if (READ_U32_LE(fp, sample_rate) != 0) {
00223             return WavFileResultErrorBrokenSampleRate;
00224         }
00225     }
00226     read_byte_count+=4;
00227 
00228     /*
00229      * 2 + 2 + 4 + 4
00230      */
00231     if (read_byte_count < chunk_size) {
00232         if (READ_U32_LE(fp, byte_rate) != 0) {
00233             return WavFileResultErrorBrokenByteRate;
00234         }
00235     }
00236     read_byte_count+=4;
00237 
00238     /*
00239      * 2 + 2 + 4 + 4 + 2
00240      */
00241     if (read_byte_count < chunk_size) {
00242         if (READ_U16_LE(fp, block_align) != 0) {
00243             return WavFileResultErrorBrokenBlockAlign;
00244         }
00245     }
00246     read_byte_count+=2;
00247 
00248     /*
00249      * 2 + 2 + 4 + 4 + 2 + 2
00250      */
00251     if (read_byte_count < chunk_size) {
00252         if (READ_U16_LE(fp, bits_per_sample) != 0) {
00253             return WavFileResultErrorBrokenBitsPerSample;
00254         }
00255     }
00256     read_byte_count+=2;
00257 
00258     /*
00259      * 2 + 2 + 4 + 4 + 2 + 2
00260      */
00261     while (read_byte_count < chunk_size) {
00262         if (fgetc(fp) == EOF) {
00263             return WavFileResultErrorBrokenChunkData;
00264         }
00265         read_byte_count++;
00266     }
00267 
00268     return WavFileResultOK;
00269 }
00270 
00271 WAVFILE *wavfile_open(const char *filename, WavFileMode mode, WavFileResult *result) {
00272     /*
00273      * Verify the filename.
00274      */
00275     if (filename == NULL) {
00276         *result = WavFileResultErrorInvalidFileName;
00277         return NULL;
00278     }
00279 
00280     /*
00281      * Open the file.
00282      */
00283     FILE *fp = NULL;
00284     switch (mode) {
00285         case WavFileModeRead:
00286             fp = fopen(filename, "rb");
00287             break;
00288         case WavFileModeWrite:
00289             fp = fopen(filename, "wb");
00290             break;
00291         default:
00292             fp = NULL;
00293             break;
00294     }
00295     if (fp == NULL) {
00296         *result = WavFileResultErrorFileOpen;
00297         return NULL;
00298     }
00299 
00300     /*
00301      * Allocate the handler.
00302      */
00303     WAVFILE *p = (WAVFILE *)malloc(sizeof(WAVFILE));
00304     if (p == NULL) {
00305         *result = WavFileResultErrorMemoryAllocation;
00306         return NULL;
00307     }
00308 
00309     /*
00310      * Fill the fields.
00311      */
00312     p->fp = fp;
00313     strcpy(p->filename, filename);
00314     p->mode = mode;
00315     p->info_checked = false;
00316     p->data_checked = false;
00317     p->data_byte_count = 0;
00318     WAVFILE_INFO_AUDIO_FORMAT(&(p->info)) = 0;
00319     WAVFILE_INFO_NUM_CHANNELS(&(p->info)) = 0;
00320     WAVFILE_INFO_SAMPLE_RATE(&(p->info)) = 0;
00321     WAVFILE_INFO_BYTE_RATE(&(p->info)) = 0;
00322     WAVFILE_INFO_BLOCK_ALIGN(&(p->info)) = 0;
00323     WAVFILE_INFO_BITS_PER_SAMPLE(&(p->info)) = 0;
00324 
00325     *result = WavFileResultOK;
00326     return p;
00327 }
00328 
00329 WavFileResult wavfile_read_info(WAVFILE *p, wavfile_info_t *info) {
00330     WavFileResult result = WavFileResultOK;
00331 
00332     if (p == NULL) {
00333         result = WavFileResultErrorInvalidHandler;
00334         goto finalize;
00335     }
00336 
00337     if (p->info_checked) {
00338         result = WavFileResultErrorAlreadyInfoChecked;
00339         goto finalize;
00340     }
00341     if (p->data_checked) {
00342         result = WavFileResultErrorAlreadyDataChecked;
00343         goto finalize;
00344     }
00345     if (p->mode != WavFileModeRead) {
00346         result = WavFileResultErrorInvalidMode;
00347         goto finalize;
00348     }
00349 
00350     while (1) {
00351         uint32_t chunk_id;
00352         uint32_t chunk_size;
00353 
00354         /*
00355          * Get the chunk ID.
00356          */
00357         if (READ_U32_BE(p->fp, &chunk_id) != 0) {
00358             if (feof(p->fp)) {
00359                 /*
00360                  * 
00361                  */
00362                 result = WavFileResultErrorNoDataChunk;
00363                 goto finalize;
00364             } else {
00365                 result = WavFileResultErrorBrokenChunkId;
00366                 goto finalize;
00367             }
00368         }
00369 
00370         /*
00371          * Verify the chunk size.
00372          */
00373         if (READ_U32_LE(p->fp, &chunk_size) != 0) {
00374             result = WavFileResultErrorBrokenChunkSize;
00375             goto finalize;
00376         }
00377 
00378 #if WAVFILE_DEBUG_ENABLED
00379         /*
00380          * 
00381          */
00382         DEBUG("chunk_id(0x%04X-%c%c%c%c), chunk_size(%d bytes)\n",
00383               chunk_id,
00384               (chunk_id >> (8 * 3)),
00385               (chunk_id >> (8 * 2)),
00386               (chunk_id >> (8 * 1)),
00387               (chunk_id >> (8 * 0)),
00388               chunk_size);
00389 #endif
00390 
00391         /*
00392          */
00393         switch (chunk_id) {
00394             case CHUNK_ID_RIFF: {
00395                 uint32_t format_id;
00396                 result = chunk_reader_riff(
00397                              chunk_id,
00398                              chunk_size,
00399                              p->fp,
00400                              &format_id);
00401 
00402 #if WAVFILE_DEBUG_ENABLED
00403                 /*
00404                  */
00405                 DEBUG("\tformat_id(%d)\n", format_id);
00406 #endif
00407 
00408                 if (format_id != RIFF_CHUNK_FORMAT_WAVE) {
00409                     return WavFileResultErrorInvalidFormatId;
00410                 }
00411                 if (result != WavFileResultOK) {
00412                     goto finalize;
00413                 }
00414             }
00415             break;
00416             case CHUNK_ID_FMT: {
00417                 result = chunk_reader_fmt(
00418                              chunk_id,
00419                              chunk_size,
00420                              p->fp,
00421                              &(p->info.audio_format),
00422                              &(p->info.num_channels),
00423                              &(p->info.sample_rate),
00424                              &(p->info.byte_rate),
00425                              &(p->info.block_align),
00426                              &(p->info.bits_per_sample));
00427 
00428                 info->audio_format = p->info.audio_format;
00429                 info->num_channels = p->info.num_channels;
00430                 info->sample_rate = p->info.sample_rate;
00431                 info->byte_rate = p->info.byte_rate;
00432                 info->block_align = p->info.block_align;
00433                 info->bits_per_sample = p->info.bits_per_sample;
00434 
00435 #if WAVFILE_DEBUG_ENABLED
00436                 /*
00437                  */
00438                 DEBUG("\taudio_format(%d)\n", p->info.audio_format);
00439                 DEBUG("\tnum_channels(%d)\n", p->info.num_channels);
00440                 DEBUG("\tsample_rate(%d)\n", p->info.sample_rate);
00441                 DEBUG("\tbyte_rate(%d)\n", p->info.byte_rate);
00442                 DEBUG("\tblock_align(%d)\n", p->info.block_align);
00443                 DEBUG("\tbits_per_sample(%d)\n", p->info.bits_per_sample);
00444 #endif
00445 
00446                 if ((p->info.audio_format != WAVFILE_AUDIO_FORMAT_PCM) && (info->audio_format != WAVFILE_AUDIO_FORMAT_EXTENSIBLE)) {
00447                     return WavFileResultErrorInvalidAudioFormat;
00448                 }
00449                 if (result != WavFileResultOK) {
00450                     goto finalize;
00451                 }
00452             }
00453             break;
00454             case CHUNK_ID_DATA: {
00455                 p->info_checked = true;
00456                 p->data_byte_count = chunk_size;
00457                 goto finalize;
00458             }
00459             break;
00460             default: {
00461                 result = chunk_reader_unknown(chunk_id, chunk_size, p->fp);
00462                 if (result != WavFileResultOK) {
00463                     goto finalize;
00464                 }
00465             }
00466             break;
00467         }
00468     }
00469 
00470 finalize:
00471     return result;
00472 }
00473 
00474 /**
00475  */
00476 WavFileResult wavfile_read_data(WAVFILE *p, wavfile_data_t *data) {
00477     if (p == NULL) {
00478         return WavFileResultErrorInvalidHandler;
00479     }
00480 
00481     if (!p->info_checked) {
00482         return WavFileResultErrorNeedInfoChecked;
00483     }
00484 
00485     if (p->mode != WavFileModeRead) {
00486         return WavFileResultErrorInvalidMode;
00487     }
00488 
00489     if (p->data_byte_count == 0) {
00490         data->num_channels = 0;
00491         for (int i = 0; i < p->info.num_channels; i++) {
00492             data->channel_data[i] = 0.5;
00493         }
00494         return WavFileResultOK;
00495     }
00496 
00497     data->num_channels = p->info.num_channels;
00498     for (int i = 0; i < p->info.num_channels; i++) {
00499         switch (p->info.bits_per_sample) {
00500             case BITS_PER_SAMPLE_8: {
00501                 int c = fgetc(p->fp);
00502                 if (c == EOF) {
00503                     return WavFileResultErrorBrokenChunkData;
00504                 }
00505                 data->channel_data[i] = (double)c / 0xFF;
00506             }
00507             p->data_byte_count-=1;
00508             break;
00509             case BITS_PER_SAMPLE_16: {
00510 #if 0
00511                 int c1 = fgetc(p->fp);
00512                 if (c1 == EOF) {
00513                     return WavFileResultErrorBrokenChunkData;
00514                 }
00515                 int c2 = fgetc(p->fp);
00516                 if (c2 == EOF) {
00517                     return WavFileResultErrorBrokenChunkData;
00518                 }
00519                 uint16_t n = (((uint16_t)c2 << 8) | ((uint16_t)c1 << 0)) ^ (1 << 15);
00520                 data->channel_data[i] = (double)n / 0xFFFF;
00521 #else
00522                 char tmp[2];
00523                 fread(tmp, 2, 1, p->fp);
00524                 uint16_t n = (((uint16_t)tmp[1] << 8) | ((uint16_t)tmp[0] << 0)) ^ (1 << 15);
00525                 data->channel_data[i] = (double)n / 0xFFFF;
00526 #endif
00527             }
00528             p->data_byte_count-=2;
00529             break;
00530             case BITS_PER_SAMPLE_24: {
00531             #if 0
00532                 int c1 = fgetc(p->fp);
00533                 if (c1 == EOF) {
00534                     return WavFileResultErrorBrokenChunkData;
00535                 }
00536                 int c2 = fgetc(p->fp);
00537                 if (c2 == EOF) {
00538                     return WavFileResultErrorBrokenChunkData;
00539                 }
00540                 int c3 = fgetc(p->fp);
00541                 if (c3 == EOF) {
00542                     return WavFileResultErrorBrokenChunkData;
00543                 }
00544                 uint32_t n = (((uint32_t)c3 << 16) | ((uint32_t)c2 << 8) | ((uint32_t)c1 << 0)) ^ (1 << 23);
00545                 data->channel_data[i] = (double)n / 0xFFFFFF;
00546 #else
00547                 char tmp[3];
00548                 fread(tmp, 3, 1, p->fp);
00549                 uint32_t n = (((uint32_t)tmp[2] << 16) | ((uint32_t)tmp[1] << 8) | ((uint32_t)tmp[0] << 0)) ^ (1 << 23);
00550                 data->channel_data[i] = (double)n / 0xFFFFFF;
00551 #endif
00552             }
00553             p->data_byte_count-=3;
00554             break;
00555             default:
00556                 return WavFileResultErrorUnsupportedBitsPerSample;
00557         }
00558     }
00559     return WavFileResultOK;
00560 }
00561 
00562 WavFileResult wavfile_write_info(WAVFILE *p, const wavfile_info_t *info) {
00563     WavFileResult result = WavFileResultOK;
00564 
00565     if (p == NULL) {
00566         result = WavFileResultErrorInvalidHandler;
00567         goto finalize;
00568     }
00569 
00570     if (p->info_checked) {
00571         result = WavFileResultErrorAlreadyInfoChecked;
00572         goto finalize;
00573     }
00574 
00575     if (p->mode != WavFileModeWrite) {
00576         result = WavFileResultErrorInvalidMode;
00577         goto finalize;
00578     }
00579 
00580     p->info.audio_format = info->audio_format;
00581     p->info.num_channels = info->num_channels;
00582     p->info.sample_rate = info->sample_rate;
00583     p->info.byte_rate = info->byte_rate;
00584     p->info.block_align = info->block_align;
00585     p->info.bits_per_sample = info->bits_per_sample;
00586 
00587     /*
00588      *
00589      */
00590 
00591     if ((info->audio_format != WAVFILE_AUDIO_FORMAT_PCM) && (info->audio_format != WAVFILE_AUDIO_FORMAT_EXTENSIBLE)) {
00592         result = WavFileResultErrorInvalidAudioFormat;
00593         goto finalize;
00594     }
00595 
00596     if ((info->bits_per_sample != BITS_PER_SAMPLE_8)
00597             && (info->bits_per_sample != BITS_PER_SAMPLE_16)
00598             && (info->bits_per_sample != BITS_PER_SAMPLE_24)) {
00599         result = WavFileResultErrorUnsupportedBitsPerSample;
00600         goto finalize;
00601     }
00602 
00603     if ((info->num_channels * info->sample_rate * (info->bits_per_sample / 8)) != info->byte_rate) {
00604         result = WavFileResultErrorInvalidByteRate;
00605         goto finalize;
00606     }
00607 
00608     /*
00609      * [RIFF]
00610      * ------------------------------------------
00611      * Big endian    4 bytes : Chunk ID
00612      * Little endian 4 bytes : Chunk size
00613      * Big endian    4 bytes : Format
00614      * ------------------------------------------
00615      */
00616     if (WRITE_U32_BE(p->fp, CHUNK_ID_RIFF) != 0) {
00617         result = WavFileResultErrorFileWrite;
00618         goto finalize;
00619     }
00620     if (WRITE_U32_LE(p->fp, 0x00000000) != 0) {
00621         result = WavFileResultErrorFileWrite;
00622         goto finalize;
00623     }
00624     if (WRITE_U32_BE(p->fp, RIFF_CHUNK_FORMAT_WAVE) != 0) {
00625         result = WavFileResultErrorFileWrite;
00626         goto finalize;
00627     }
00628 
00629     /*
00630      * [fmt]
00631      * ------------------------------------------
00632      * Big endian    4 bytes : Sub chunk ID
00633      * Little endian 4 bytes : Sub chunk size
00634      * Little endian 2 bytes : Audio format
00635      * Little endian 2 bytes : Number of channels
00636      * Little endian 4 bytes : Sample rate
00637      * Little endian 4 bytes : Byte rate
00638      * Little endian 2 bytes : Block align
00639      * Little endian 2 bytes : Bits per sample
00640      *  .                                      .
00641      *  .  Additional bytes here (extensible)  .
00642      *  .                                      .
00643      * ------------------------------------------
00644      */
00645     switch (info->audio_format) {
00646         case WAVFILE_AUDIO_FORMAT_PCM: {
00647             if (WRITE_U32_BE(p->fp, CHUNK_ID_FMT) != 0) {
00648                 result = WavFileResultErrorFileWrite;
00649                 goto finalize;
00650             }
00651             if (WRITE_U32_LE(p->fp, CHUNK_SIZE_FMT_PCM) != 0) {
00652                 result = WavFileResultErrorFileWrite;
00653                 goto finalize;
00654             }
00655             if (WRITE_U16_LE(p->fp, info->audio_format) != 0) {
00656                 result = WavFileResultErrorFileWrite;
00657                 goto finalize;
00658             }
00659             if (WRITE_U16_LE(p->fp, info->num_channels) != 0) {
00660                 result = WavFileResultErrorFileWrite;
00661                 goto finalize;
00662             }
00663             if (WRITE_U32_LE(p->fp, info->sample_rate) != 0) {
00664                 result = WavFileResultErrorFileWrite;
00665                 goto finalize;
00666             }
00667             if (WRITE_U32_LE(p->fp, info->byte_rate) != 0) {
00668                 result = WavFileResultErrorFileWrite;
00669                 goto finalize;
00670             }
00671             if (WRITE_U16_LE(p->fp, info->block_align) != 0) {
00672                 result = WavFileResultErrorFileWrite;
00673                 goto finalize;
00674             }
00675             if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) {
00676                 result = WavFileResultErrorFileWrite;
00677                 goto finalize;
00678             }
00679         }
00680         break;
00681         case WAVFILE_AUDIO_FORMAT_EXTENSIBLE: {
00682             if (WRITE_U32_BE(p->fp, CHUNK_ID_FMT) != 0) {
00683                 result = WavFileResultErrorFileWrite;
00684                 goto finalize;
00685             }
00686             if (WRITE_U32_LE(p->fp, CHUNK_SIZE_FMT_EXTENSIBLE) != 0) {
00687                 result = WavFileResultErrorFileWrite;
00688                 goto finalize;
00689             }
00690             if (WRITE_U16_LE(p->fp, info->audio_format) != 0) {
00691                 result = WavFileResultErrorFileWrite;
00692                 goto finalize;
00693             }
00694             if (WRITE_U16_LE(p->fp, info->num_channels) != 0) {
00695                 result = WavFileResultErrorFileWrite;
00696                 goto finalize;
00697             }
00698             if (WRITE_U32_LE(p->fp, info->sample_rate) != 0) {
00699                 result = WavFileResultErrorFileWrite;
00700                 goto finalize;
00701             }
00702             if (WRITE_U32_LE(p->fp, info->byte_rate) != 0) {
00703                 result = WavFileResultErrorFileWrite;
00704                 goto finalize;
00705             }
00706             if (WRITE_U16_LE(p->fp, info->block_align) != 0) {
00707                 result = WavFileResultErrorFileWrite;
00708                 goto finalize;
00709             }
00710             if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) {
00711                 result = WavFileResultErrorFileWrite;
00712                 goto finalize;
00713             }
00714             /*
00715              * Additional bytes for the extensible format.
00716              *
00717              *  2 bytes : Size of the extension (0 or 22)
00718              *  2 bytes : Number of valid bits
00719              *  4 bytes : Speaker position mask
00720              * 16 bytes : GUID, including the data format code
00721              */
00722             if (WRITE_U16_LE(p->fp, 22) != 0) {
00723                 result = WavFileResultErrorFileWrite;
00724                 goto finalize;
00725             }
00726             if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) {
00727                 result = WavFileResultErrorFileWrite;
00728                 goto finalize;
00729             }
00730             if (WRITE_U32_LE(p->fp, 0x00000000) != 0) {
00731                 result = WavFileResultErrorFileWrite;
00732                 goto finalize;
00733             }
00734             static const unsigned char sub_format[16] = {
00735                 0x01, 0x00, 0x00, 0x00,
00736                 0x00, 0x00, 0x10, 0x00,
00737                 0x80, 0x00, 0x00, 0xAA,
00738                 0x00, 0x38, 0x9B, 0x71
00739             };
00740             for (int i = 0; i < sizeof(sub_format); i++) {
00741                 fputc((char)sub_format[i], p->fp);
00742             }
00743         }
00744         break;
00745         default:
00746             result = WavFileResultErrorInvalidAudioFormat;
00747             goto finalize;
00748     }
00749 
00750     /*
00751      * [data]
00752      * ------------------------------------------
00753      * Big endian    4 bytes : Sub chunk ID
00754      * Little endian 4 bytes : Sub chunk size
00755      * ------------------------------------------
00756      * Little endian 2 bytes : Sample 1 (Ch.1)
00757      * Little endian 2 bytes : Sample 1 (Ch.2)
00758      *                    .
00759      *                    .
00760      *                    .
00761      * Little endian 2 bytes : Sample 1 (Ch.N)
00762      * ------------------------------------------
00763      * Little endian 2 bytes : Sample 2 (Ch.1)
00764      * Little endian 2 bytes : Sample 2 (Ch.2)
00765      *                    .
00766      *                    .
00767      *                    .
00768      * Little endian 2 bytes : Sample 2 (Ch.N)
00769      * ------------------------------------------
00770      */
00771     if (WRITE_U32_BE(p->fp, CHUNK_ID_DATA) != 0) {
00772         result = WavFileResultErrorFileWrite;
00773         goto finalize;
00774     }
00775     if (WRITE_U32_LE(p->fp, 0x00000000) != 0) {
00776         result = WavFileResultErrorFileWrite;
00777         goto finalize;
00778     }
00779 
00780 finalize:
00781     if (WavFileResultOK == result) {
00782         p->info_checked = true;
00783     }
00784     return result;
00785 }
00786 
00787 /**
00788  */
00789 WavFileResult wavfile_write_data(WAVFILE *p, const wavfile_data_t *data) {
00790     WavFileResult result = WavFileResultOK;
00791 
00792     if (p == NULL) {
00793         result = WavFileResultErrorInvalidHandler;
00794         goto finalize;
00795     }
00796 
00797     if (!p->info_checked) {
00798         result = WavFileResultErrorNeedInfoChecked;
00799         goto finalize;
00800     }
00801 
00802     if (p->mode != WavFileModeWrite) {
00803         result = WavFileResultErrorInvalidMode;
00804         goto finalize;
00805     }
00806 
00807     if (p->info.num_channels != data->num_channels) {
00808         result = WavFileResultErrorInvalidNumChannels;
00809         goto finalize;
00810     }
00811 
00812     for (int i = 0; i < p->info.num_channels; i++) {
00813         switch (p->info.bits_per_sample) {
00814             case BITS_PER_SAMPLE_8: {
00815                 int n = (int)((double)data->channel_data[i] * 0xFF);
00816                 if (n < 0x00) {
00817                     n = 0x00;
00818                 }
00819                 if (0xFF < n) {
00820                     n = 0xFF;
00821                 }
00822                 fputc((char)n, p->fp);
00823             }
00824             p->data_byte_count+=1;
00825             break;
00826             case BITS_PER_SAMPLE_16: {
00827                 int n = (int)((double)(data->channel_data[i] * 0xFFFF) - 0x8000);
00828                 if (0x7FFF < n) {
00829                     n = 0x7FFF;
00830                 }
00831                 if (n < -0x8000) {
00832                     n = -0x8000;
00833                 }
00834                 fputc(((uint16_t)n >> 0) & 0xff, p->fp);
00835                 fputc(((uint16_t)n >> 8) & 0xff, p->fp);
00836             }
00837             p->data_byte_count+=2;
00838             break;
00839             case BITS_PER_SAMPLE_24: {
00840                 int n = (int)((double)(data->channel_data[i] * 0xFFFFFF) - 0x800000);
00841                 if (0x7FFFFF < n) {
00842                     n = 0x7FFFFF;
00843                 }
00844                 if (n < -0x800000) {
00845                     n = -0x800000;
00846                 }
00847                 fputc(((uint32_t)n >>  0) & 0xff, p->fp);
00848                 fputc(((uint32_t)n >>  8) & 0xff, p->fp);
00849                 fputc(((uint32_t)n >> 16) & 0xff, p->fp);
00850             }
00851             p->data_byte_count+=3;
00852             break;
00853         }
00854     }
00855     p->data_checked = true;
00856 
00857 finalize:
00858     return result;
00859 }
00860 
00861 WavFileResult wavfile_close(WAVFILE *p) {
00862     WavFileResult result = WavFileResultOK;
00863 
00864     switch (p->mode) {
00865         case WavFileModeRead:
00866             break;
00867         case WavFileModeWrite:
00868             if (p->info_checked && p->data_checked) {
00869                 switch (p->info.audio_format) {
00870                     case WAVFILE_AUDIO_FORMAT_PCM: {
00871                         /*
00872                          * Fill the RIFF chunk size.
00873                          */
00874                         if (fseek(p->fp, 4L, SEEK_SET) == -1) {
00875                             result = WavFileResultErrorFileWrite;
00876                             goto finalize;
00877                         }
00878                         if (WRITE_U32_LE(p->fp, 4 + (8 + CHUNK_SIZE_FMT_PCM) + (8 + p->data_byte_count)) != 0) {
00879                             result = WavFileResultErrorFileWrite;
00880                             goto finalize;
00881                         }
00882 
00883                         /*
00884                          * Fill the data sub chunk size.
00885                          */
00886                         if (fseek(p->fp, 12 + (8 + CHUNK_SIZE_FMT_PCM) + 4, SEEK_SET) == -1) {
00887                             result = WavFileResultErrorFileWrite;
00888                             goto finalize;
00889                         }
00890                         if (WRITE_U32_LE(p->fp, p->data_byte_count) != 0) {
00891                             result = WavFileResultErrorFileWrite;
00892                             goto finalize;
00893                         }
00894                     }
00895                     break;
00896                     case WAVFILE_AUDIO_FORMAT_EXTENSIBLE: {
00897                         /*
00898                          * Fill the RIFF chunk size.
00899                          */
00900                         if (fseek(p->fp, 4L, SEEK_SET) == -1) {
00901                             result = WavFileResultErrorFileWrite;
00902                             goto finalize;
00903                         }
00904                         if (WRITE_U32_LE(p->fp, 4 + (8 + CHUNK_SIZE_FMT_EXTENSIBLE) + (8 + p->data_byte_count)) != 0) {
00905                             result = WavFileResultErrorFileWrite;
00906                             goto finalize;
00907                         }
00908 
00909                         /*
00910                          * Fill the data sub chunk size.
00911                          */
00912                         if (fseek(p->fp, 12 + (8 + CHUNK_SIZE_FMT_EXTENSIBLE) + 4, SEEK_SET) == -1) {
00913                             result = WavFileResultErrorFileWrite;
00914                             goto finalize;
00915                         }
00916                         if (WRITE_U32_LE(p->fp, p->data_byte_count) != 0) {
00917                             result = WavFileResultErrorFileWrite;
00918                             goto finalize;
00919                         }
00920                     }
00921                     break;
00922                 }
00923             }
00924             break;
00925     }
00926 
00927 finalize:
00928     fclose(p->fp);
00929     free(p);
00930     return result;
00931 }
00932 
00933 void wavfile_result_string(const WavFileResult result, char *buf, size_t siz) {
00934     switch (result) {
00935         case WavFileResultOK:
00936             strcpy(buf, "OK.");
00937             break;
00938         case WavFileResultErrorInvalidFileName:
00939             strcpy(buf, "Invalid file name found.");
00940             break;
00941         case WavFileResultErrorMemoryAllocation:
00942             strcpy(buf, "Memory allocation error.");
00943             break;
00944         case WavFileResultErrorFileOpen:
00945             strcpy(buf, "File open error found.");
00946             break;
00947         case WavFileResultErrorFileWrite:
00948             strcpy(buf, "File write error found.");
00949             break;
00950         case WavFileResultErrorBrokenChunkId:
00951             strcpy(buf, "Broken chunk ID found.");
00952             break;
00953         case WavFileResultErrorBrokenChunkSize:
00954             strcpy(buf, "Borken chunk size found.");
00955             break;
00956         case WavFileResultErrorBrokenChunkData:
00957             strcpy(buf, "Borken chunk data found.");
00958             break;
00959         case WavFileResultErrorBrokenFormatId:
00960             strcpy(buf, "Broken format ID found.");
00961             break;
00962         case WavFileResultErrorInvalidFormatId:
00963             strcpy(buf, "Invalid format ID found.");
00964             break;
00965         case WavFileResultErrorBrokenAudioFormat:
00966             strcpy(buf, "Broken audio format found.");
00967             break;
00968         case WavFileResultErrorInvalidAudioFormat:
00969             strcpy(buf, "Invalid audio format found.");
00970             break;
00971         case WavFileResultErrorInvalidNumChannels:
00972             strcpy(buf, "Invalid number of channels found.");
00973             break;
00974         case WavFileResultErrorBrokenNumChannels:
00975             strcpy(buf, "Broken number of channels found.");
00976             break;
00977         case WavFileResultErrorBrokenSampleRate:
00978             strcpy(buf, "Broken sample rate found.");
00979             break;
00980         case WavFileResultErrorBrokenByteRate:
00981             strcpy(buf, "Broken byte rate found.");
00982             break;
00983         case WavFileResultErrorInvalidByteRate:
00984             strcpy(buf, "Invalid byte rate found.");
00985             break;
00986         case WavFileResultErrorBrokenBlockAlign:
00987             strcpy(buf, "Broken block alignment found.");
00988             break;
00989         case WavFileResultErrorBrokenBitsPerSample:
00990             strcpy(buf, "Broken bits per sample found.");
00991             break;
00992         case WavFileResultErrorUnsupportedBitsPerSample:
00993             strcpy(buf, "Unsupported bits per sample found.");
00994             break;
00995         case WavFileResultErrorAlreadyInfoChecked:
00996             strcpy(buf, "Already checked info.");
00997             break;
00998         case WavFileResultErrorAlreadyDataChecked:
00999             strcpy(buf, "Already checked data.");
01000             break;
01001         case WavFileResultErrorNoDataChunk:
01002             strcpy(buf, "No data chunk.");
01003             break;
01004         case WavFileResultErrorInvalidMode:
01005             strcpy(buf, "Invalid mode.");
01006             break;
01007         case WavFileResultErrorNeedInfoChecked:
01008             strcpy(buf, "Need check info.");
01009             break;
01010         case WavFileResultErrorNeedDataChecked:
01011             strcpy(buf, "Need check data.");
01012             break;
01013         case WavFileResultErrorInvalidHandler:
01014             strcpy(buf, "Invalid handler.");
01015             break;
01016         default:
01017             strcpy(buf, "Unkonwn error found.");
01018             break;
01019     }
01020 }
01021