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
wavfile.cpp@0:c853ba46d0b9, 2012-04-14 (annotated)
- Committer:
- shintamainjp
- Date:
- Sat Apr 14 02:13:30 2012 +0000
- Revision:
- 0:c853ba46d0b9
Initial version of the tiny wav I/O module for mbed.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
shintamainjp | 0:c853ba46d0b9 | 1 | /** |
shintamainjp | 0:c853ba46d0b9 | 2 | * @file wavfile.cpp |
shintamainjp | 0:c853ba46d0b9 | 3 | * @author Shinichiro Nakamura |
shintamainjp | 0:c853ba46d0b9 | 4 | */ |
shintamainjp | 0:c853ba46d0b9 | 5 | |
shintamainjp | 0:c853ba46d0b9 | 6 | /* |
shintamainjp | 0:c853ba46d0b9 | 7 | * =============================================================== |
shintamainjp | 0:c853ba46d0b9 | 8 | * Tiny WAV I/O Module |
shintamainjp | 0:c853ba46d0b9 | 9 | * Version 0.0.1 |
shintamainjp | 0:c853ba46d0b9 | 10 | * =============================================================== |
shintamainjp | 0:c853ba46d0b9 | 11 | * Copyright (c) 2011-2012 Shinichiro Nakamura |
shintamainjp | 0:c853ba46d0b9 | 12 | * |
shintamainjp | 0:c853ba46d0b9 | 13 | * Permission is hereby granted, free of charge, to any person |
shintamainjp | 0:c853ba46d0b9 | 14 | * obtaining a copy of this software and associated documentation |
shintamainjp | 0:c853ba46d0b9 | 15 | * files (the "Software"), to deal in the Software without |
shintamainjp | 0:c853ba46d0b9 | 16 | * restriction, including without limitation the rights to use, |
shintamainjp | 0:c853ba46d0b9 | 17 | * copy, modify, merge, publish, distribute, sublicense, and/or |
shintamainjp | 0:c853ba46d0b9 | 18 | * sell copies of the Software, and to permit persons to whom the |
shintamainjp | 0:c853ba46d0b9 | 19 | * Software is furnished to do so, subject to the following |
shintamainjp | 0:c853ba46d0b9 | 20 | * conditions: |
shintamainjp | 0:c853ba46d0b9 | 21 | * |
shintamainjp | 0:c853ba46d0b9 | 22 | * The above copyright notice and this permission notice shall be |
shintamainjp | 0:c853ba46d0b9 | 23 | * included in all copies or substantial portions of the Software. |
shintamainjp | 0:c853ba46d0b9 | 24 | * |
shintamainjp | 0:c853ba46d0b9 | 25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
shintamainjp | 0:c853ba46d0b9 | 26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
shintamainjp | 0:c853ba46d0b9 | 27 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
shintamainjp | 0:c853ba46d0b9 | 28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
shintamainjp | 0:c853ba46d0b9 | 29 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
shintamainjp | 0:c853ba46d0b9 | 30 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
shintamainjp | 0:c853ba46d0b9 | 31 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
shintamainjp | 0:c853ba46d0b9 | 32 | * OTHER DEALINGS IN THE SOFTWARE. |
shintamainjp | 0:c853ba46d0b9 | 33 | * =============================================================== |
shintamainjp | 0:c853ba46d0b9 | 34 | */ |
shintamainjp | 0:c853ba46d0b9 | 35 | |
shintamainjp | 0:c853ba46d0b9 | 36 | #include <stdio.h> |
shintamainjp | 0:c853ba46d0b9 | 37 | #include <stdlib.h> |
shintamainjp | 0:c853ba46d0b9 | 38 | #include <string.h> |
shintamainjp | 0:c853ba46d0b9 | 39 | #include "wavfile.h" |
shintamainjp | 0:c853ba46d0b9 | 40 | |
shintamainjp | 0:c853ba46d0b9 | 41 | #define DEBUG printf |
shintamainjp | 0:c853ba46d0b9 | 42 | |
shintamainjp | 0:c853ba46d0b9 | 43 | #define CHUNK_ID_RIFF (('R' << 24) | ('I' << 16) | ('F' << 8) | ('F' << 0)) |
shintamainjp | 0:c853ba46d0b9 | 44 | #define CHUNK_ID_FMT (('f' << 24) | ('m' << 16) | ('t' << 8) | (' ' << 0)) |
shintamainjp | 0:c853ba46d0b9 | 45 | #define CHUNK_ID_DATA (('d' << 24) | ('a' << 16) | ('t' << 8) | ('a' << 0)) |
shintamainjp | 0:c853ba46d0b9 | 46 | |
shintamainjp | 0:c853ba46d0b9 | 47 | #define RIFF_CHUNK_FORMAT_WAVE (('W' << 24) | ('A' << 16) | ('V' << 8) | ('E' << 0)) |
shintamainjp | 0:c853ba46d0b9 | 48 | |
shintamainjp | 0:c853ba46d0b9 | 49 | #define BITS_PER_SAMPLE_8 (8) |
shintamainjp | 0:c853ba46d0b9 | 50 | #define BITS_PER_SAMPLE_16 (16) |
shintamainjp | 0:c853ba46d0b9 | 51 | #define BITS_PER_SAMPLE_24 (24) |
shintamainjp | 0:c853ba46d0b9 | 52 | |
shintamainjp | 0:c853ba46d0b9 | 53 | #define CHUNK_SIZE_FMT_PCM (16) |
shintamainjp | 0:c853ba46d0b9 | 54 | #define CHUNK_SIZE_FMT_EXTENSIBLE (40) |
shintamainjp | 0:c853ba46d0b9 | 55 | |
shintamainjp | 0:c853ba46d0b9 | 56 | #define CHANNEL_MASK_SPEAKER_FRONT_LEFT (0x00000001) |
shintamainjp | 0:c853ba46d0b9 | 57 | #define CHANNEL_MASK_SPEAKER_FRONT_RIGHT (0x00000002) |
shintamainjp | 0:c853ba46d0b9 | 58 | #define CHANNEL_MASK_SPEAKER_FRONT_CENTER (0x00000004) |
shintamainjp | 0:c853ba46d0b9 | 59 | #define CHANNEL_MASK_SPEAKER_LOW_FREQUENCY (0x00000008) |
shintamainjp | 0:c853ba46d0b9 | 60 | #define CHANNEL_MASK_SPEAKER_BACK_LEFT (0x00000010) |
shintamainjp | 0:c853ba46d0b9 | 61 | #define CHANNEL_MASK_SPEAKER_BACK_RIGHT (0x00000020) |
shintamainjp | 0:c853ba46d0b9 | 62 | #define CHANNEL_MASK_SPEAKER_FRONT_LEFT_OF_CENTER (0x00000040) |
shintamainjp | 0:c853ba46d0b9 | 63 | #define CHANNEL_MASK_SPEAKER_FRONT_RIGHT_OF_CENTER (0x00000080) |
shintamainjp | 0:c853ba46d0b9 | 64 | #define CHANNEL_MASK_SPEAKER_BACK_CENTER (0x00000100) |
shintamainjp | 0:c853ba46d0b9 | 65 | #define CHANNEL_MASK_SPEAKER_SIDE_LEFT (0x00000200) |
shintamainjp | 0:c853ba46d0b9 | 66 | #define CHANNEL_MASK_SPEAKER_SIDE_RIGHT (0x00000400) |
shintamainjp | 0:c853ba46d0b9 | 67 | #define CHANNEL_MASK_SPEAKER_TOP_CENTER (0x00000800) |
shintamainjp | 0:c853ba46d0b9 | 68 | #define CHANNEL_MASK_SPEAKER_TOP_FRONT_LEFT (0x00001000) |
shintamainjp | 0:c853ba46d0b9 | 69 | #define CHANNEL_MASK_SPEAKER_TOP_FRONT_CENTER (0x00002000) |
shintamainjp | 0:c853ba46d0b9 | 70 | #define CHANNEL_MASK_SPEAKER_TOP_FRONT_RIGHT (0x00004000) |
shintamainjp | 0:c853ba46d0b9 | 71 | #define CHANNEL_MASK_SPEAKER_TOP_BACK_LEFT (0x00008000) |
shintamainjp | 0:c853ba46d0b9 | 72 | #define CHANNEL_MASK_SPEAKER_TOP_BACK_CENTER (0x00010000) |
shintamainjp | 0:c853ba46d0b9 | 73 | #define CHANNEL_MASK_SPEAKER_TOP_BACK_RIGHT (0x00020000) |
shintamainjp | 0:c853ba46d0b9 | 74 | #define CHANNEL_MASK_SPEAKER_RESERVED (0x80000000) |
shintamainjp | 0:c853ba46d0b9 | 75 | |
shintamainjp | 0:c853ba46d0b9 | 76 | struct WAVFILE { |
shintamainjp | 0:c853ba46d0b9 | 77 | FILE *fp; |
shintamainjp | 0:c853ba46d0b9 | 78 | char filename[BUFSIZ]; |
shintamainjp | 0:c853ba46d0b9 | 79 | WavFileMode mode; |
shintamainjp | 0:c853ba46d0b9 | 80 | bool info_checked; |
shintamainjp | 0:c853ba46d0b9 | 81 | bool data_checked; |
shintamainjp | 0:c853ba46d0b9 | 82 | uint32_t data_byte_count; |
shintamainjp | 0:c853ba46d0b9 | 83 | wavfile_info_t info; |
shintamainjp | 0:c853ba46d0b9 | 84 | }; |
shintamainjp | 0:c853ba46d0b9 | 85 | |
shintamainjp | 0:c853ba46d0b9 | 86 | static int WRITE_U32_BE(FILE *fp, const uint32_t value) { |
shintamainjp | 0:c853ba46d0b9 | 87 | for (int i = 0; i < 4; i++) { |
shintamainjp | 0:c853ba46d0b9 | 88 | if (fputc((value >> (8 * (3 - i))), fp) == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 89 | return -1; |
shintamainjp | 0:c853ba46d0b9 | 90 | } |
shintamainjp | 0:c853ba46d0b9 | 91 | } |
shintamainjp | 0:c853ba46d0b9 | 92 | return 0; |
shintamainjp | 0:c853ba46d0b9 | 93 | } |
shintamainjp | 0:c853ba46d0b9 | 94 | |
shintamainjp | 0:c853ba46d0b9 | 95 | static int WRITE_U32_LE(FILE *fp, const uint32_t value) { |
shintamainjp | 0:c853ba46d0b9 | 96 | for (int i = 0; i < 4; i++) { |
shintamainjp | 0:c853ba46d0b9 | 97 | if (fputc((value >> (8 * i)), fp) == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 98 | return -1; |
shintamainjp | 0:c853ba46d0b9 | 99 | } |
shintamainjp | 0:c853ba46d0b9 | 100 | } |
shintamainjp | 0:c853ba46d0b9 | 101 | return 0; |
shintamainjp | 0:c853ba46d0b9 | 102 | } |
shintamainjp | 0:c853ba46d0b9 | 103 | |
shintamainjp | 0:c853ba46d0b9 | 104 | static int WRITE_U16_LE(FILE *fp, const uint16_t value) { |
shintamainjp | 0:c853ba46d0b9 | 105 | for (int i = 0; i < 2; i++) { |
shintamainjp | 0:c853ba46d0b9 | 106 | if (fputc((value >> (8 * i)), fp) == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 107 | return -1; |
shintamainjp | 0:c853ba46d0b9 | 108 | } |
shintamainjp | 0:c853ba46d0b9 | 109 | } |
shintamainjp | 0:c853ba46d0b9 | 110 | return 0; |
shintamainjp | 0:c853ba46d0b9 | 111 | } |
shintamainjp | 0:c853ba46d0b9 | 112 | |
shintamainjp | 0:c853ba46d0b9 | 113 | static int READ_U32_BE(FILE *fp, uint32_t *value) { |
shintamainjp | 0:c853ba46d0b9 | 114 | int raw[4]; |
shintamainjp | 0:c853ba46d0b9 | 115 | for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) { |
shintamainjp | 0:c853ba46d0b9 | 116 | raw[i] = fgetc(fp); |
shintamainjp | 0:c853ba46d0b9 | 117 | if (raw[i] == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 118 | *value = 0x00000000; |
shintamainjp | 0:c853ba46d0b9 | 119 | return -1; |
shintamainjp | 0:c853ba46d0b9 | 120 | } |
shintamainjp | 0:c853ba46d0b9 | 121 | } |
shintamainjp | 0:c853ba46d0b9 | 122 | *value = |
shintamainjp | 0:c853ba46d0b9 | 123 | ((uint32_t)raw[0] << 24) | |
shintamainjp | 0:c853ba46d0b9 | 124 | ((uint32_t)raw[1] << 16) | |
shintamainjp | 0:c853ba46d0b9 | 125 | ((uint32_t)raw[2] << 8) | |
shintamainjp | 0:c853ba46d0b9 | 126 | ((uint32_t)raw[3] << 0); |
shintamainjp | 0:c853ba46d0b9 | 127 | return 0; |
shintamainjp | 0:c853ba46d0b9 | 128 | } |
shintamainjp | 0:c853ba46d0b9 | 129 | |
shintamainjp | 0:c853ba46d0b9 | 130 | static int READ_U32_LE(FILE *fp, uint32_t *value) { |
shintamainjp | 0:c853ba46d0b9 | 131 | int raw[4]; |
shintamainjp | 0:c853ba46d0b9 | 132 | for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) { |
shintamainjp | 0:c853ba46d0b9 | 133 | raw[i] = fgetc(fp); |
shintamainjp | 0:c853ba46d0b9 | 134 | if (raw[i] == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 135 | *value = 0x00000000; |
shintamainjp | 0:c853ba46d0b9 | 136 | return -1; |
shintamainjp | 0:c853ba46d0b9 | 137 | } |
shintamainjp | 0:c853ba46d0b9 | 138 | } |
shintamainjp | 0:c853ba46d0b9 | 139 | *value = |
shintamainjp | 0:c853ba46d0b9 | 140 | ((uint32_t)raw[3] << 24) | |
shintamainjp | 0:c853ba46d0b9 | 141 | ((uint32_t)raw[2] << 16) | |
shintamainjp | 0:c853ba46d0b9 | 142 | ((uint32_t)raw[1] << 8) | |
shintamainjp | 0:c853ba46d0b9 | 143 | ((uint32_t)raw[0] << 0); |
shintamainjp | 0:c853ba46d0b9 | 144 | return 0; |
shintamainjp | 0:c853ba46d0b9 | 145 | } |
shintamainjp | 0:c853ba46d0b9 | 146 | |
shintamainjp | 0:c853ba46d0b9 | 147 | static int READ_U16_LE(FILE *fp, uint16_t *value) { |
shintamainjp | 0:c853ba46d0b9 | 148 | int raw[2]; |
shintamainjp | 0:c853ba46d0b9 | 149 | for (int i = 0; i < (int)(sizeof(raw) / sizeof(raw[0])); i++) { |
shintamainjp | 0:c853ba46d0b9 | 150 | raw[i] = fgetc(fp); |
shintamainjp | 0:c853ba46d0b9 | 151 | if (raw[i] == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 152 | *value = 0x00000000; |
shintamainjp | 0:c853ba46d0b9 | 153 | return -1; |
shintamainjp | 0:c853ba46d0b9 | 154 | } |
shintamainjp | 0:c853ba46d0b9 | 155 | } |
shintamainjp | 0:c853ba46d0b9 | 156 | *value = |
shintamainjp | 0:c853ba46d0b9 | 157 | ((uint16_t)raw[1] << 8) | |
shintamainjp | 0:c853ba46d0b9 | 158 | ((uint16_t)raw[0] << 0); |
shintamainjp | 0:c853ba46d0b9 | 159 | return 0; |
shintamainjp | 0:c853ba46d0b9 | 160 | } |
shintamainjp | 0:c853ba46d0b9 | 161 | |
shintamainjp | 0:c853ba46d0b9 | 162 | static WavFileResult chunk_reader_unknown( |
shintamainjp | 0:c853ba46d0b9 | 163 | const uint32_t chunk_id, |
shintamainjp | 0:c853ba46d0b9 | 164 | const uint32_t chunk_size, |
shintamainjp | 0:c853ba46d0b9 | 165 | FILE *fp) { |
shintamainjp | 0:c853ba46d0b9 | 166 | for (int i = 0; i < (int)chunk_size; i++) { |
shintamainjp | 0:c853ba46d0b9 | 167 | int c = fgetc(fp); |
shintamainjp | 0:c853ba46d0b9 | 168 | if (c == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 169 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 170 | } |
shintamainjp | 0:c853ba46d0b9 | 171 | } |
shintamainjp | 0:c853ba46d0b9 | 172 | return WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 173 | } |
shintamainjp | 0:c853ba46d0b9 | 174 | |
shintamainjp | 0:c853ba46d0b9 | 175 | static WavFileResult chunk_reader_riff( |
shintamainjp | 0:c853ba46d0b9 | 176 | const uint32_t chunk_id, |
shintamainjp | 0:c853ba46d0b9 | 177 | const uint32_t chunk_size, |
shintamainjp | 0:c853ba46d0b9 | 178 | FILE *fp, |
shintamainjp | 0:c853ba46d0b9 | 179 | uint32_t *format_id) { |
shintamainjp | 0:c853ba46d0b9 | 180 | if (READ_U32_BE(fp, format_id) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 181 | return WavFileResultErrorBrokenFormatId; |
shintamainjp | 0:c853ba46d0b9 | 182 | } |
shintamainjp | 0:c853ba46d0b9 | 183 | return WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 184 | } |
shintamainjp | 0:c853ba46d0b9 | 185 | |
shintamainjp | 0:c853ba46d0b9 | 186 | static WavFileResult chunk_reader_fmt( |
shintamainjp | 0:c853ba46d0b9 | 187 | const uint32_t chunk_id, |
shintamainjp | 0:c853ba46d0b9 | 188 | const uint32_t chunk_size, |
shintamainjp | 0:c853ba46d0b9 | 189 | FILE *fp, |
shintamainjp | 0:c853ba46d0b9 | 190 | uint16_t *audio_format, |
shintamainjp | 0:c853ba46d0b9 | 191 | uint16_t *num_channels, |
shintamainjp | 0:c853ba46d0b9 | 192 | uint32_t *sample_rate, |
shintamainjp | 0:c853ba46d0b9 | 193 | uint32_t *byte_rate, |
shintamainjp | 0:c853ba46d0b9 | 194 | uint16_t *block_align, |
shintamainjp | 0:c853ba46d0b9 | 195 | uint16_t *bits_per_sample) { |
shintamainjp | 0:c853ba46d0b9 | 196 | uint32_t read_byte_count = 0; |
shintamainjp | 0:c853ba46d0b9 | 197 | |
shintamainjp | 0:c853ba46d0b9 | 198 | /* |
shintamainjp | 0:c853ba46d0b9 | 199 | * 2 |
shintamainjp | 0:c853ba46d0b9 | 200 | */ |
shintamainjp | 0:c853ba46d0b9 | 201 | if (read_byte_count < chunk_size) { |
shintamainjp | 0:c853ba46d0b9 | 202 | if (READ_U16_LE(fp, audio_format) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 203 | return WavFileResultErrorBrokenAudioFormat; |
shintamainjp | 0:c853ba46d0b9 | 204 | } |
shintamainjp | 0:c853ba46d0b9 | 205 | } |
shintamainjp | 0:c853ba46d0b9 | 206 | read_byte_count+=2; |
shintamainjp | 0:c853ba46d0b9 | 207 | |
shintamainjp | 0:c853ba46d0b9 | 208 | /* |
shintamainjp | 0:c853ba46d0b9 | 209 | * 2 + 2 |
shintamainjp | 0:c853ba46d0b9 | 210 | */ |
shintamainjp | 0:c853ba46d0b9 | 211 | if (read_byte_count < chunk_size) { |
shintamainjp | 0:c853ba46d0b9 | 212 | if (READ_U16_LE(fp, num_channels) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 213 | return WavFileResultErrorBrokenNumChannels; |
shintamainjp | 0:c853ba46d0b9 | 214 | } |
shintamainjp | 0:c853ba46d0b9 | 215 | } |
shintamainjp | 0:c853ba46d0b9 | 216 | read_byte_count+=2; |
shintamainjp | 0:c853ba46d0b9 | 217 | |
shintamainjp | 0:c853ba46d0b9 | 218 | /* |
shintamainjp | 0:c853ba46d0b9 | 219 | * 2 + 2 + 4 |
shintamainjp | 0:c853ba46d0b9 | 220 | */ |
shintamainjp | 0:c853ba46d0b9 | 221 | if (read_byte_count < chunk_size) { |
shintamainjp | 0:c853ba46d0b9 | 222 | if (READ_U32_LE(fp, sample_rate) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 223 | return WavFileResultErrorBrokenSampleRate; |
shintamainjp | 0:c853ba46d0b9 | 224 | } |
shintamainjp | 0:c853ba46d0b9 | 225 | } |
shintamainjp | 0:c853ba46d0b9 | 226 | read_byte_count+=4; |
shintamainjp | 0:c853ba46d0b9 | 227 | |
shintamainjp | 0:c853ba46d0b9 | 228 | /* |
shintamainjp | 0:c853ba46d0b9 | 229 | * 2 + 2 + 4 + 4 |
shintamainjp | 0:c853ba46d0b9 | 230 | */ |
shintamainjp | 0:c853ba46d0b9 | 231 | if (read_byte_count < chunk_size) { |
shintamainjp | 0:c853ba46d0b9 | 232 | if (READ_U32_LE(fp, byte_rate) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 233 | return WavFileResultErrorBrokenByteRate; |
shintamainjp | 0:c853ba46d0b9 | 234 | } |
shintamainjp | 0:c853ba46d0b9 | 235 | } |
shintamainjp | 0:c853ba46d0b9 | 236 | read_byte_count+=4; |
shintamainjp | 0:c853ba46d0b9 | 237 | |
shintamainjp | 0:c853ba46d0b9 | 238 | /* |
shintamainjp | 0:c853ba46d0b9 | 239 | * 2 + 2 + 4 + 4 + 2 |
shintamainjp | 0:c853ba46d0b9 | 240 | */ |
shintamainjp | 0:c853ba46d0b9 | 241 | if (read_byte_count < chunk_size) { |
shintamainjp | 0:c853ba46d0b9 | 242 | if (READ_U16_LE(fp, block_align) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 243 | return WavFileResultErrorBrokenBlockAlign; |
shintamainjp | 0:c853ba46d0b9 | 244 | } |
shintamainjp | 0:c853ba46d0b9 | 245 | } |
shintamainjp | 0:c853ba46d0b9 | 246 | read_byte_count+=2; |
shintamainjp | 0:c853ba46d0b9 | 247 | |
shintamainjp | 0:c853ba46d0b9 | 248 | /* |
shintamainjp | 0:c853ba46d0b9 | 249 | * 2 + 2 + 4 + 4 + 2 + 2 |
shintamainjp | 0:c853ba46d0b9 | 250 | */ |
shintamainjp | 0:c853ba46d0b9 | 251 | if (read_byte_count < chunk_size) { |
shintamainjp | 0:c853ba46d0b9 | 252 | if (READ_U16_LE(fp, bits_per_sample) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 253 | return WavFileResultErrorBrokenBitsPerSample; |
shintamainjp | 0:c853ba46d0b9 | 254 | } |
shintamainjp | 0:c853ba46d0b9 | 255 | } |
shintamainjp | 0:c853ba46d0b9 | 256 | read_byte_count+=2; |
shintamainjp | 0:c853ba46d0b9 | 257 | |
shintamainjp | 0:c853ba46d0b9 | 258 | /* |
shintamainjp | 0:c853ba46d0b9 | 259 | * 2 + 2 + 4 + 4 + 2 + 2 |
shintamainjp | 0:c853ba46d0b9 | 260 | */ |
shintamainjp | 0:c853ba46d0b9 | 261 | while (read_byte_count < chunk_size) { |
shintamainjp | 0:c853ba46d0b9 | 262 | if (fgetc(fp) == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 263 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 264 | } |
shintamainjp | 0:c853ba46d0b9 | 265 | read_byte_count++; |
shintamainjp | 0:c853ba46d0b9 | 266 | } |
shintamainjp | 0:c853ba46d0b9 | 267 | |
shintamainjp | 0:c853ba46d0b9 | 268 | return WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 269 | } |
shintamainjp | 0:c853ba46d0b9 | 270 | |
shintamainjp | 0:c853ba46d0b9 | 271 | WAVFILE *wavfile_open(const char *filename, WavFileMode mode, WavFileResult *result) { |
shintamainjp | 0:c853ba46d0b9 | 272 | /* |
shintamainjp | 0:c853ba46d0b9 | 273 | * Verify the filename. |
shintamainjp | 0:c853ba46d0b9 | 274 | */ |
shintamainjp | 0:c853ba46d0b9 | 275 | if (filename == NULL) { |
shintamainjp | 0:c853ba46d0b9 | 276 | *result = WavFileResultErrorInvalidFileName; |
shintamainjp | 0:c853ba46d0b9 | 277 | return NULL; |
shintamainjp | 0:c853ba46d0b9 | 278 | } |
shintamainjp | 0:c853ba46d0b9 | 279 | |
shintamainjp | 0:c853ba46d0b9 | 280 | /* |
shintamainjp | 0:c853ba46d0b9 | 281 | * Open the file. |
shintamainjp | 0:c853ba46d0b9 | 282 | */ |
shintamainjp | 0:c853ba46d0b9 | 283 | FILE *fp = NULL; |
shintamainjp | 0:c853ba46d0b9 | 284 | switch (mode) { |
shintamainjp | 0:c853ba46d0b9 | 285 | case WavFileModeRead: |
shintamainjp | 0:c853ba46d0b9 | 286 | fp = fopen(filename, "rb"); |
shintamainjp | 0:c853ba46d0b9 | 287 | break; |
shintamainjp | 0:c853ba46d0b9 | 288 | case WavFileModeWrite: |
shintamainjp | 0:c853ba46d0b9 | 289 | fp = fopen(filename, "wb"); |
shintamainjp | 0:c853ba46d0b9 | 290 | break; |
shintamainjp | 0:c853ba46d0b9 | 291 | default: |
shintamainjp | 0:c853ba46d0b9 | 292 | fp = NULL; |
shintamainjp | 0:c853ba46d0b9 | 293 | break; |
shintamainjp | 0:c853ba46d0b9 | 294 | } |
shintamainjp | 0:c853ba46d0b9 | 295 | if (fp == NULL) { |
shintamainjp | 0:c853ba46d0b9 | 296 | *result = WavFileResultErrorFileOpen; |
shintamainjp | 0:c853ba46d0b9 | 297 | return NULL; |
shintamainjp | 0:c853ba46d0b9 | 298 | } |
shintamainjp | 0:c853ba46d0b9 | 299 | |
shintamainjp | 0:c853ba46d0b9 | 300 | /* |
shintamainjp | 0:c853ba46d0b9 | 301 | * Allocate the handler. |
shintamainjp | 0:c853ba46d0b9 | 302 | */ |
shintamainjp | 0:c853ba46d0b9 | 303 | WAVFILE *p = (WAVFILE *)malloc(sizeof(WAVFILE)); |
shintamainjp | 0:c853ba46d0b9 | 304 | if (p == NULL) { |
shintamainjp | 0:c853ba46d0b9 | 305 | *result = WavFileResultErrorMemoryAllocation; |
shintamainjp | 0:c853ba46d0b9 | 306 | return NULL; |
shintamainjp | 0:c853ba46d0b9 | 307 | } |
shintamainjp | 0:c853ba46d0b9 | 308 | |
shintamainjp | 0:c853ba46d0b9 | 309 | /* |
shintamainjp | 0:c853ba46d0b9 | 310 | * Fill the fields. |
shintamainjp | 0:c853ba46d0b9 | 311 | */ |
shintamainjp | 0:c853ba46d0b9 | 312 | p->fp = fp; |
shintamainjp | 0:c853ba46d0b9 | 313 | strcpy(p->filename, filename); |
shintamainjp | 0:c853ba46d0b9 | 314 | p->mode = mode; |
shintamainjp | 0:c853ba46d0b9 | 315 | p->info_checked = false; |
shintamainjp | 0:c853ba46d0b9 | 316 | p->data_checked = false; |
shintamainjp | 0:c853ba46d0b9 | 317 | p->data_byte_count = 0; |
shintamainjp | 0:c853ba46d0b9 | 318 | WAVFILE_INFO_AUDIO_FORMAT(&(p->info)) = 0; |
shintamainjp | 0:c853ba46d0b9 | 319 | WAVFILE_INFO_NUM_CHANNELS(&(p->info)) = 0; |
shintamainjp | 0:c853ba46d0b9 | 320 | WAVFILE_INFO_SAMPLE_RATE(&(p->info)) = 0; |
shintamainjp | 0:c853ba46d0b9 | 321 | WAVFILE_INFO_BYTE_RATE(&(p->info)) = 0; |
shintamainjp | 0:c853ba46d0b9 | 322 | WAVFILE_INFO_BLOCK_ALIGN(&(p->info)) = 0; |
shintamainjp | 0:c853ba46d0b9 | 323 | WAVFILE_INFO_BITS_PER_SAMPLE(&(p->info)) = 0; |
shintamainjp | 0:c853ba46d0b9 | 324 | |
shintamainjp | 0:c853ba46d0b9 | 325 | *result = WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 326 | return p; |
shintamainjp | 0:c853ba46d0b9 | 327 | } |
shintamainjp | 0:c853ba46d0b9 | 328 | |
shintamainjp | 0:c853ba46d0b9 | 329 | WavFileResult wavfile_read_info(WAVFILE *p, wavfile_info_t *info) { |
shintamainjp | 0:c853ba46d0b9 | 330 | WavFileResult result = WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 331 | |
shintamainjp | 0:c853ba46d0b9 | 332 | if (p == NULL) { |
shintamainjp | 0:c853ba46d0b9 | 333 | result = WavFileResultErrorInvalidHandler; |
shintamainjp | 0:c853ba46d0b9 | 334 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 335 | } |
shintamainjp | 0:c853ba46d0b9 | 336 | |
shintamainjp | 0:c853ba46d0b9 | 337 | if (p->info_checked) { |
shintamainjp | 0:c853ba46d0b9 | 338 | result = WavFileResultErrorAlreadyInfoChecked; |
shintamainjp | 0:c853ba46d0b9 | 339 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 340 | } |
shintamainjp | 0:c853ba46d0b9 | 341 | if (p->data_checked) { |
shintamainjp | 0:c853ba46d0b9 | 342 | result = WavFileResultErrorAlreadyDataChecked; |
shintamainjp | 0:c853ba46d0b9 | 343 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 344 | } |
shintamainjp | 0:c853ba46d0b9 | 345 | if (p->mode != WavFileModeRead) { |
shintamainjp | 0:c853ba46d0b9 | 346 | result = WavFileResultErrorInvalidMode; |
shintamainjp | 0:c853ba46d0b9 | 347 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 348 | } |
shintamainjp | 0:c853ba46d0b9 | 349 | |
shintamainjp | 0:c853ba46d0b9 | 350 | while (1) { |
shintamainjp | 0:c853ba46d0b9 | 351 | uint32_t chunk_id; |
shintamainjp | 0:c853ba46d0b9 | 352 | uint32_t chunk_size; |
shintamainjp | 0:c853ba46d0b9 | 353 | |
shintamainjp | 0:c853ba46d0b9 | 354 | /* |
shintamainjp | 0:c853ba46d0b9 | 355 | * Get the chunk ID. |
shintamainjp | 0:c853ba46d0b9 | 356 | */ |
shintamainjp | 0:c853ba46d0b9 | 357 | if (READ_U32_BE(p->fp, &chunk_id) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 358 | if (feof(p->fp)) { |
shintamainjp | 0:c853ba46d0b9 | 359 | /* |
shintamainjp | 0:c853ba46d0b9 | 360 | * |
shintamainjp | 0:c853ba46d0b9 | 361 | */ |
shintamainjp | 0:c853ba46d0b9 | 362 | result = WavFileResultErrorNoDataChunk; |
shintamainjp | 0:c853ba46d0b9 | 363 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 364 | } else { |
shintamainjp | 0:c853ba46d0b9 | 365 | result = WavFileResultErrorBrokenChunkId; |
shintamainjp | 0:c853ba46d0b9 | 366 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 367 | } |
shintamainjp | 0:c853ba46d0b9 | 368 | } |
shintamainjp | 0:c853ba46d0b9 | 369 | |
shintamainjp | 0:c853ba46d0b9 | 370 | /* |
shintamainjp | 0:c853ba46d0b9 | 371 | * Verify the chunk size. |
shintamainjp | 0:c853ba46d0b9 | 372 | */ |
shintamainjp | 0:c853ba46d0b9 | 373 | if (READ_U32_LE(p->fp, &chunk_size) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 374 | result = WavFileResultErrorBrokenChunkSize; |
shintamainjp | 0:c853ba46d0b9 | 375 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 376 | } |
shintamainjp | 0:c853ba46d0b9 | 377 | |
shintamainjp | 0:c853ba46d0b9 | 378 | #if WAVFILE_DEBUG_ENABLED |
shintamainjp | 0:c853ba46d0b9 | 379 | /* |
shintamainjp | 0:c853ba46d0b9 | 380 | * |
shintamainjp | 0:c853ba46d0b9 | 381 | */ |
shintamainjp | 0:c853ba46d0b9 | 382 | DEBUG("chunk_id(0x%04X-%c%c%c%c), chunk_size(%d bytes)\n", |
shintamainjp | 0:c853ba46d0b9 | 383 | chunk_id, |
shintamainjp | 0:c853ba46d0b9 | 384 | (chunk_id >> (8 * 3)), |
shintamainjp | 0:c853ba46d0b9 | 385 | (chunk_id >> (8 * 2)), |
shintamainjp | 0:c853ba46d0b9 | 386 | (chunk_id >> (8 * 1)), |
shintamainjp | 0:c853ba46d0b9 | 387 | (chunk_id >> (8 * 0)), |
shintamainjp | 0:c853ba46d0b9 | 388 | chunk_size); |
shintamainjp | 0:c853ba46d0b9 | 389 | #endif |
shintamainjp | 0:c853ba46d0b9 | 390 | |
shintamainjp | 0:c853ba46d0b9 | 391 | /* |
shintamainjp | 0:c853ba46d0b9 | 392 | */ |
shintamainjp | 0:c853ba46d0b9 | 393 | switch (chunk_id) { |
shintamainjp | 0:c853ba46d0b9 | 394 | case CHUNK_ID_RIFF: { |
shintamainjp | 0:c853ba46d0b9 | 395 | uint32_t format_id; |
shintamainjp | 0:c853ba46d0b9 | 396 | result = chunk_reader_riff( |
shintamainjp | 0:c853ba46d0b9 | 397 | chunk_id, |
shintamainjp | 0:c853ba46d0b9 | 398 | chunk_size, |
shintamainjp | 0:c853ba46d0b9 | 399 | p->fp, |
shintamainjp | 0:c853ba46d0b9 | 400 | &format_id); |
shintamainjp | 0:c853ba46d0b9 | 401 | |
shintamainjp | 0:c853ba46d0b9 | 402 | #if WAVFILE_DEBUG_ENABLED |
shintamainjp | 0:c853ba46d0b9 | 403 | /* |
shintamainjp | 0:c853ba46d0b9 | 404 | */ |
shintamainjp | 0:c853ba46d0b9 | 405 | DEBUG("\tformat_id(%d)\n", format_id); |
shintamainjp | 0:c853ba46d0b9 | 406 | #endif |
shintamainjp | 0:c853ba46d0b9 | 407 | |
shintamainjp | 0:c853ba46d0b9 | 408 | if (format_id != RIFF_CHUNK_FORMAT_WAVE) { |
shintamainjp | 0:c853ba46d0b9 | 409 | return WavFileResultErrorInvalidFormatId; |
shintamainjp | 0:c853ba46d0b9 | 410 | } |
shintamainjp | 0:c853ba46d0b9 | 411 | if (result != WavFileResultOK) { |
shintamainjp | 0:c853ba46d0b9 | 412 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 413 | } |
shintamainjp | 0:c853ba46d0b9 | 414 | } |
shintamainjp | 0:c853ba46d0b9 | 415 | break; |
shintamainjp | 0:c853ba46d0b9 | 416 | case CHUNK_ID_FMT: { |
shintamainjp | 0:c853ba46d0b9 | 417 | result = chunk_reader_fmt( |
shintamainjp | 0:c853ba46d0b9 | 418 | chunk_id, |
shintamainjp | 0:c853ba46d0b9 | 419 | chunk_size, |
shintamainjp | 0:c853ba46d0b9 | 420 | p->fp, |
shintamainjp | 0:c853ba46d0b9 | 421 | &(p->info.audio_format), |
shintamainjp | 0:c853ba46d0b9 | 422 | &(p->info.num_channels), |
shintamainjp | 0:c853ba46d0b9 | 423 | &(p->info.sample_rate), |
shintamainjp | 0:c853ba46d0b9 | 424 | &(p->info.byte_rate), |
shintamainjp | 0:c853ba46d0b9 | 425 | &(p->info.block_align), |
shintamainjp | 0:c853ba46d0b9 | 426 | &(p->info.bits_per_sample)); |
shintamainjp | 0:c853ba46d0b9 | 427 | |
shintamainjp | 0:c853ba46d0b9 | 428 | info->audio_format = p->info.audio_format; |
shintamainjp | 0:c853ba46d0b9 | 429 | info->num_channels = p->info.num_channels; |
shintamainjp | 0:c853ba46d0b9 | 430 | info->sample_rate = p->info.sample_rate; |
shintamainjp | 0:c853ba46d0b9 | 431 | info->byte_rate = p->info.byte_rate; |
shintamainjp | 0:c853ba46d0b9 | 432 | info->block_align = p->info.block_align; |
shintamainjp | 0:c853ba46d0b9 | 433 | info->bits_per_sample = p->info.bits_per_sample; |
shintamainjp | 0:c853ba46d0b9 | 434 | |
shintamainjp | 0:c853ba46d0b9 | 435 | #if WAVFILE_DEBUG_ENABLED |
shintamainjp | 0:c853ba46d0b9 | 436 | /* |
shintamainjp | 0:c853ba46d0b9 | 437 | */ |
shintamainjp | 0:c853ba46d0b9 | 438 | DEBUG("\taudio_format(%d)\n", p->info.audio_format); |
shintamainjp | 0:c853ba46d0b9 | 439 | DEBUG("\tnum_channels(%d)\n", p->info.num_channels); |
shintamainjp | 0:c853ba46d0b9 | 440 | DEBUG("\tsample_rate(%d)\n", p->info.sample_rate); |
shintamainjp | 0:c853ba46d0b9 | 441 | DEBUG("\tbyte_rate(%d)\n", p->info.byte_rate); |
shintamainjp | 0:c853ba46d0b9 | 442 | DEBUG("\tblock_align(%d)\n", p->info.block_align); |
shintamainjp | 0:c853ba46d0b9 | 443 | DEBUG("\tbits_per_sample(%d)\n", p->info.bits_per_sample); |
shintamainjp | 0:c853ba46d0b9 | 444 | #endif |
shintamainjp | 0:c853ba46d0b9 | 445 | |
shintamainjp | 0:c853ba46d0b9 | 446 | if ((p->info.audio_format != WAVFILE_AUDIO_FORMAT_PCM) && (info->audio_format != WAVFILE_AUDIO_FORMAT_EXTENSIBLE)) { |
shintamainjp | 0:c853ba46d0b9 | 447 | return WavFileResultErrorInvalidAudioFormat; |
shintamainjp | 0:c853ba46d0b9 | 448 | } |
shintamainjp | 0:c853ba46d0b9 | 449 | if (result != WavFileResultOK) { |
shintamainjp | 0:c853ba46d0b9 | 450 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 451 | } |
shintamainjp | 0:c853ba46d0b9 | 452 | } |
shintamainjp | 0:c853ba46d0b9 | 453 | break; |
shintamainjp | 0:c853ba46d0b9 | 454 | case CHUNK_ID_DATA: { |
shintamainjp | 0:c853ba46d0b9 | 455 | p->info_checked = true; |
shintamainjp | 0:c853ba46d0b9 | 456 | p->data_byte_count = chunk_size; |
shintamainjp | 0:c853ba46d0b9 | 457 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 458 | } |
shintamainjp | 0:c853ba46d0b9 | 459 | break; |
shintamainjp | 0:c853ba46d0b9 | 460 | default: { |
shintamainjp | 0:c853ba46d0b9 | 461 | result = chunk_reader_unknown(chunk_id, chunk_size, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 462 | if (result != WavFileResultOK) { |
shintamainjp | 0:c853ba46d0b9 | 463 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 464 | } |
shintamainjp | 0:c853ba46d0b9 | 465 | } |
shintamainjp | 0:c853ba46d0b9 | 466 | break; |
shintamainjp | 0:c853ba46d0b9 | 467 | } |
shintamainjp | 0:c853ba46d0b9 | 468 | } |
shintamainjp | 0:c853ba46d0b9 | 469 | |
shintamainjp | 0:c853ba46d0b9 | 470 | finalize: |
shintamainjp | 0:c853ba46d0b9 | 471 | return result; |
shintamainjp | 0:c853ba46d0b9 | 472 | } |
shintamainjp | 0:c853ba46d0b9 | 473 | |
shintamainjp | 0:c853ba46d0b9 | 474 | /** |
shintamainjp | 0:c853ba46d0b9 | 475 | */ |
shintamainjp | 0:c853ba46d0b9 | 476 | WavFileResult wavfile_read_data(WAVFILE *p, wavfile_data_t *data) { |
shintamainjp | 0:c853ba46d0b9 | 477 | if (p == NULL) { |
shintamainjp | 0:c853ba46d0b9 | 478 | return WavFileResultErrorInvalidHandler; |
shintamainjp | 0:c853ba46d0b9 | 479 | } |
shintamainjp | 0:c853ba46d0b9 | 480 | |
shintamainjp | 0:c853ba46d0b9 | 481 | if (!p->info_checked) { |
shintamainjp | 0:c853ba46d0b9 | 482 | return WavFileResultErrorNeedInfoChecked; |
shintamainjp | 0:c853ba46d0b9 | 483 | } |
shintamainjp | 0:c853ba46d0b9 | 484 | |
shintamainjp | 0:c853ba46d0b9 | 485 | if (p->mode != WavFileModeRead) { |
shintamainjp | 0:c853ba46d0b9 | 486 | return WavFileResultErrorInvalidMode; |
shintamainjp | 0:c853ba46d0b9 | 487 | } |
shintamainjp | 0:c853ba46d0b9 | 488 | |
shintamainjp | 0:c853ba46d0b9 | 489 | if (p->data_byte_count == 0) { |
shintamainjp | 0:c853ba46d0b9 | 490 | data->num_channels = 0; |
shintamainjp | 0:c853ba46d0b9 | 491 | for (int i = 0; i < p->info.num_channels; i++) { |
shintamainjp | 0:c853ba46d0b9 | 492 | data->channel_data[i] = 0.5; |
shintamainjp | 0:c853ba46d0b9 | 493 | } |
shintamainjp | 0:c853ba46d0b9 | 494 | return WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 495 | } |
shintamainjp | 0:c853ba46d0b9 | 496 | |
shintamainjp | 0:c853ba46d0b9 | 497 | data->num_channels = p->info.num_channels; |
shintamainjp | 0:c853ba46d0b9 | 498 | for (int i = 0; i < p->info.num_channels; i++) { |
shintamainjp | 0:c853ba46d0b9 | 499 | switch (p->info.bits_per_sample) { |
shintamainjp | 0:c853ba46d0b9 | 500 | case BITS_PER_SAMPLE_8: { |
shintamainjp | 0:c853ba46d0b9 | 501 | int c = fgetc(p->fp); |
shintamainjp | 0:c853ba46d0b9 | 502 | if (c == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 503 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 504 | } |
shintamainjp | 0:c853ba46d0b9 | 505 | data->channel_data[i] = (double)c / 0xFF; |
shintamainjp | 0:c853ba46d0b9 | 506 | } |
shintamainjp | 0:c853ba46d0b9 | 507 | p->data_byte_count-=1; |
shintamainjp | 0:c853ba46d0b9 | 508 | break; |
shintamainjp | 0:c853ba46d0b9 | 509 | case BITS_PER_SAMPLE_16: { |
shintamainjp | 0:c853ba46d0b9 | 510 | #if 0 |
shintamainjp | 0:c853ba46d0b9 | 511 | int c1 = fgetc(p->fp); |
shintamainjp | 0:c853ba46d0b9 | 512 | if (c1 == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 513 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 514 | } |
shintamainjp | 0:c853ba46d0b9 | 515 | int c2 = fgetc(p->fp); |
shintamainjp | 0:c853ba46d0b9 | 516 | if (c2 == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 517 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 518 | } |
shintamainjp | 0:c853ba46d0b9 | 519 | uint16_t n = (((uint16_t)c2 << 8) | ((uint16_t)c1 << 0)) ^ (1 << 15); |
shintamainjp | 0:c853ba46d0b9 | 520 | data->channel_data[i] = (double)n / 0xFFFF; |
shintamainjp | 0:c853ba46d0b9 | 521 | #else |
shintamainjp | 0:c853ba46d0b9 | 522 | char tmp[2]; |
shintamainjp | 0:c853ba46d0b9 | 523 | fread(tmp, 2, 1, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 524 | uint16_t n = (((uint16_t)tmp[1] << 8) | ((uint16_t)tmp[0] << 0)) ^ (1 << 15); |
shintamainjp | 0:c853ba46d0b9 | 525 | data->channel_data[i] = (double)n / 0xFFFF; |
shintamainjp | 0:c853ba46d0b9 | 526 | #endif |
shintamainjp | 0:c853ba46d0b9 | 527 | } |
shintamainjp | 0:c853ba46d0b9 | 528 | p->data_byte_count-=2; |
shintamainjp | 0:c853ba46d0b9 | 529 | break; |
shintamainjp | 0:c853ba46d0b9 | 530 | case BITS_PER_SAMPLE_24: { |
shintamainjp | 0:c853ba46d0b9 | 531 | #if 0 |
shintamainjp | 0:c853ba46d0b9 | 532 | int c1 = fgetc(p->fp); |
shintamainjp | 0:c853ba46d0b9 | 533 | if (c1 == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 534 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 535 | } |
shintamainjp | 0:c853ba46d0b9 | 536 | int c2 = fgetc(p->fp); |
shintamainjp | 0:c853ba46d0b9 | 537 | if (c2 == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 538 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 539 | } |
shintamainjp | 0:c853ba46d0b9 | 540 | int c3 = fgetc(p->fp); |
shintamainjp | 0:c853ba46d0b9 | 541 | if (c3 == EOF) { |
shintamainjp | 0:c853ba46d0b9 | 542 | return WavFileResultErrorBrokenChunkData; |
shintamainjp | 0:c853ba46d0b9 | 543 | } |
shintamainjp | 0:c853ba46d0b9 | 544 | uint32_t n = (((uint32_t)c3 << 16) | ((uint32_t)c2 << 8) | ((uint32_t)c1 << 0)) ^ (1 << 23); |
shintamainjp | 0:c853ba46d0b9 | 545 | data->channel_data[i] = (double)n / 0xFFFFFF; |
shintamainjp | 0:c853ba46d0b9 | 546 | #else |
shintamainjp | 0:c853ba46d0b9 | 547 | char tmp[3]; |
shintamainjp | 0:c853ba46d0b9 | 548 | fread(tmp, 3, 1, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 549 | uint32_t n = (((uint32_t)tmp[2] << 16) | ((uint32_t)tmp[1] << 8) | ((uint32_t)tmp[0] << 0)) ^ (1 << 23); |
shintamainjp | 0:c853ba46d0b9 | 550 | data->channel_data[i] = (double)n / 0xFFFFFF; |
shintamainjp | 0:c853ba46d0b9 | 551 | #endif |
shintamainjp | 0:c853ba46d0b9 | 552 | } |
shintamainjp | 0:c853ba46d0b9 | 553 | p->data_byte_count-=3; |
shintamainjp | 0:c853ba46d0b9 | 554 | break; |
shintamainjp | 0:c853ba46d0b9 | 555 | default: |
shintamainjp | 0:c853ba46d0b9 | 556 | return WavFileResultErrorUnsupportedBitsPerSample; |
shintamainjp | 0:c853ba46d0b9 | 557 | } |
shintamainjp | 0:c853ba46d0b9 | 558 | } |
shintamainjp | 0:c853ba46d0b9 | 559 | return WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 560 | } |
shintamainjp | 0:c853ba46d0b9 | 561 | |
shintamainjp | 0:c853ba46d0b9 | 562 | WavFileResult wavfile_write_info(WAVFILE *p, const wavfile_info_t *info) { |
shintamainjp | 0:c853ba46d0b9 | 563 | WavFileResult result = WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 564 | |
shintamainjp | 0:c853ba46d0b9 | 565 | if (p == NULL) { |
shintamainjp | 0:c853ba46d0b9 | 566 | result = WavFileResultErrorInvalidHandler; |
shintamainjp | 0:c853ba46d0b9 | 567 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 568 | } |
shintamainjp | 0:c853ba46d0b9 | 569 | |
shintamainjp | 0:c853ba46d0b9 | 570 | if (p->info_checked) { |
shintamainjp | 0:c853ba46d0b9 | 571 | result = WavFileResultErrorAlreadyInfoChecked; |
shintamainjp | 0:c853ba46d0b9 | 572 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 573 | } |
shintamainjp | 0:c853ba46d0b9 | 574 | |
shintamainjp | 0:c853ba46d0b9 | 575 | if (p->mode != WavFileModeWrite) { |
shintamainjp | 0:c853ba46d0b9 | 576 | result = WavFileResultErrorInvalidMode; |
shintamainjp | 0:c853ba46d0b9 | 577 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 578 | } |
shintamainjp | 0:c853ba46d0b9 | 579 | |
shintamainjp | 0:c853ba46d0b9 | 580 | p->info.audio_format = info->audio_format; |
shintamainjp | 0:c853ba46d0b9 | 581 | p->info.num_channels = info->num_channels; |
shintamainjp | 0:c853ba46d0b9 | 582 | p->info.sample_rate = info->sample_rate; |
shintamainjp | 0:c853ba46d0b9 | 583 | p->info.byte_rate = info->byte_rate; |
shintamainjp | 0:c853ba46d0b9 | 584 | p->info.block_align = info->block_align; |
shintamainjp | 0:c853ba46d0b9 | 585 | p->info.bits_per_sample = info->bits_per_sample; |
shintamainjp | 0:c853ba46d0b9 | 586 | |
shintamainjp | 0:c853ba46d0b9 | 587 | /* |
shintamainjp | 0:c853ba46d0b9 | 588 | * |
shintamainjp | 0:c853ba46d0b9 | 589 | */ |
shintamainjp | 0:c853ba46d0b9 | 590 | |
shintamainjp | 0:c853ba46d0b9 | 591 | if ((info->audio_format != WAVFILE_AUDIO_FORMAT_PCM) && (info->audio_format != WAVFILE_AUDIO_FORMAT_EXTENSIBLE)) { |
shintamainjp | 0:c853ba46d0b9 | 592 | result = WavFileResultErrorInvalidAudioFormat; |
shintamainjp | 0:c853ba46d0b9 | 593 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 594 | } |
shintamainjp | 0:c853ba46d0b9 | 595 | |
shintamainjp | 0:c853ba46d0b9 | 596 | if ((info->bits_per_sample != BITS_PER_SAMPLE_8) |
shintamainjp | 0:c853ba46d0b9 | 597 | && (info->bits_per_sample != BITS_PER_SAMPLE_16) |
shintamainjp | 0:c853ba46d0b9 | 598 | && (info->bits_per_sample != BITS_PER_SAMPLE_24)) { |
shintamainjp | 0:c853ba46d0b9 | 599 | result = WavFileResultErrorUnsupportedBitsPerSample; |
shintamainjp | 0:c853ba46d0b9 | 600 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 601 | } |
shintamainjp | 0:c853ba46d0b9 | 602 | |
shintamainjp | 0:c853ba46d0b9 | 603 | if ((info->num_channels * info->sample_rate * (info->bits_per_sample / 8)) != info->byte_rate) { |
shintamainjp | 0:c853ba46d0b9 | 604 | result = WavFileResultErrorInvalidByteRate; |
shintamainjp | 0:c853ba46d0b9 | 605 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 606 | } |
shintamainjp | 0:c853ba46d0b9 | 607 | |
shintamainjp | 0:c853ba46d0b9 | 608 | /* |
shintamainjp | 0:c853ba46d0b9 | 609 | * [RIFF] |
shintamainjp | 0:c853ba46d0b9 | 610 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 611 | * Big endian 4 bytes : Chunk ID |
shintamainjp | 0:c853ba46d0b9 | 612 | * Little endian 4 bytes : Chunk size |
shintamainjp | 0:c853ba46d0b9 | 613 | * Big endian 4 bytes : Format |
shintamainjp | 0:c853ba46d0b9 | 614 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 615 | */ |
shintamainjp | 0:c853ba46d0b9 | 616 | if (WRITE_U32_BE(p->fp, CHUNK_ID_RIFF) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 617 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 618 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 619 | } |
shintamainjp | 0:c853ba46d0b9 | 620 | if (WRITE_U32_LE(p->fp, 0x00000000) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 621 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 622 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 623 | } |
shintamainjp | 0:c853ba46d0b9 | 624 | if (WRITE_U32_BE(p->fp, RIFF_CHUNK_FORMAT_WAVE) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 625 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 626 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 627 | } |
shintamainjp | 0:c853ba46d0b9 | 628 | |
shintamainjp | 0:c853ba46d0b9 | 629 | /* |
shintamainjp | 0:c853ba46d0b9 | 630 | * [fmt] |
shintamainjp | 0:c853ba46d0b9 | 631 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 632 | * Big endian 4 bytes : Sub chunk ID |
shintamainjp | 0:c853ba46d0b9 | 633 | * Little endian 4 bytes : Sub chunk size |
shintamainjp | 0:c853ba46d0b9 | 634 | * Little endian 2 bytes : Audio format |
shintamainjp | 0:c853ba46d0b9 | 635 | * Little endian 2 bytes : Number of channels |
shintamainjp | 0:c853ba46d0b9 | 636 | * Little endian 4 bytes : Sample rate |
shintamainjp | 0:c853ba46d0b9 | 637 | * Little endian 4 bytes : Byte rate |
shintamainjp | 0:c853ba46d0b9 | 638 | * Little endian 2 bytes : Block align |
shintamainjp | 0:c853ba46d0b9 | 639 | * Little endian 2 bytes : Bits per sample |
shintamainjp | 0:c853ba46d0b9 | 640 | * . . |
shintamainjp | 0:c853ba46d0b9 | 641 | * . Additional bytes here (extensible) . |
shintamainjp | 0:c853ba46d0b9 | 642 | * . . |
shintamainjp | 0:c853ba46d0b9 | 643 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 644 | */ |
shintamainjp | 0:c853ba46d0b9 | 645 | switch (info->audio_format) { |
shintamainjp | 0:c853ba46d0b9 | 646 | case WAVFILE_AUDIO_FORMAT_PCM: { |
shintamainjp | 0:c853ba46d0b9 | 647 | if (WRITE_U32_BE(p->fp, CHUNK_ID_FMT) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 648 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 649 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 650 | } |
shintamainjp | 0:c853ba46d0b9 | 651 | if (WRITE_U32_LE(p->fp, CHUNK_SIZE_FMT_PCM) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 652 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 653 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 654 | } |
shintamainjp | 0:c853ba46d0b9 | 655 | if (WRITE_U16_LE(p->fp, info->audio_format) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 656 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 657 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 658 | } |
shintamainjp | 0:c853ba46d0b9 | 659 | if (WRITE_U16_LE(p->fp, info->num_channels) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 660 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 661 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 662 | } |
shintamainjp | 0:c853ba46d0b9 | 663 | if (WRITE_U32_LE(p->fp, info->sample_rate) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 664 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 665 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 666 | } |
shintamainjp | 0:c853ba46d0b9 | 667 | if (WRITE_U32_LE(p->fp, info->byte_rate) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 668 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 669 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 670 | } |
shintamainjp | 0:c853ba46d0b9 | 671 | if (WRITE_U16_LE(p->fp, info->block_align) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 672 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 673 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 674 | } |
shintamainjp | 0:c853ba46d0b9 | 675 | if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 676 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 677 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 678 | } |
shintamainjp | 0:c853ba46d0b9 | 679 | } |
shintamainjp | 0:c853ba46d0b9 | 680 | break; |
shintamainjp | 0:c853ba46d0b9 | 681 | case WAVFILE_AUDIO_FORMAT_EXTENSIBLE: { |
shintamainjp | 0:c853ba46d0b9 | 682 | if (WRITE_U32_BE(p->fp, CHUNK_ID_FMT) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 683 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 684 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 685 | } |
shintamainjp | 0:c853ba46d0b9 | 686 | if (WRITE_U32_LE(p->fp, CHUNK_SIZE_FMT_EXTENSIBLE) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 687 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 688 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 689 | } |
shintamainjp | 0:c853ba46d0b9 | 690 | if (WRITE_U16_LE(p->fp, info->audio_format) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 691 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 692 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 693 | } |
shintamainjp | 0:c853ba46d0b9 | 694 | if (WRITE_U16_LE(p->fp, info->num_channels) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 695 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 696 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 697 | } |
shintamainjp | 0:c853ba46d0b9 | 698 | if (WRITE_U32_LE(p->fp, info->sample_rate) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 699 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 700 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 701 | } |
shintamainjp | 0:c853ba46d0b9 | 702 | if (WRITE_U32_LE(p->fp, info->byte_rate) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 703 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 704 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 705 | } |
shintamainjp | 0:c853ba46d0b9 | 706 | if (WRITE_U16_LE(p->fp, info->block_align) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 707 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 708 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 709 | } |
shintamainjp | 0:c853ba46d0b9 | 710 | if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 711 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 712 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 713 | } |
shintamainjp | 0:c853ba46d0b9 | 714 | /* |
shintamainjp | 0:c853ba46d0b9 | 715 | * Additional bytes for the extensible format. |
shintamainjp | 0:c853ba46d0b9 | 716 | * |
shintamainjp | 0:c853ba46d0b9 | 717 | * 2 bytes : Size of the extension (0 or 22) |
shintamainjp | 0:c853ba46d0b9 | 718 | * 2 bytes : Number of valid bits |
shintamainjp | 0:c853ba46d0b9 | 719 | * 4 bytes : Speaker position mask |
shintamainjp | 0:c853ba46d0b9 | 720 | * 16 bytes : GUID, including the data format code |
shintamainjp | 0:c853ba46d0b9 | 721 | */ |
shintamainjp | 0:c853ba46d0b9 | 722 | if (WRITE_U16_LE(p->fp, 22) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 723 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 724 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 725 | } |
shintamainjp | 0:c853ba46d0b9 | 726 | if (WRITE_U16_LE(p->fp, info->bits_per_sample) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 727 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 728 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 729 | } |
shintamainjp | 0:c853ba46d0b9 | 730 | if (WRITE_U32_LE(p->fp, 0x00000000) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 731 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 732 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 733 | } |
shintamainjp | 0:c853ba46d0b9 | 734 | static const unsigned char sub_format[16] = { |
shintamainjp | 0:c853ba46d0b9 | 735 | 0x01, 0x00, 0x00, 0x00, |
shintamainjp | 0:c853ba46d0b9 | 736 | 0x00, 0x00, 0x10, 0x00, |
shintamainjp | 0:c853ba46d0b9 | 737 | 0x80, 0x00, 0x00, 0xAA, |
shintamainjp | 0:c853ba46d0b9 | 738 | 0x00, 0x38, 0x9B, 0x71 |
shintamainjp | 0:c853ba46d0b9 | 739 | }; |
shintamainjp | 0:c853ba46d0b9 | 740 | for (int i = 0; i < sizeof(sub_format); i++) { |
shintamainjp | 0:c853ba46d0b9 | 741 | fputc((char)sub_format[i], p->fp); |
shintamainjp | 0:c853ba46d0b9 | 742 | } |
shintamainjp | 0:c853ba46d0b9 | 743 | } |
shintamainjp | 0:c853ba46d0b9 | 744 | break; |
shintamainjp | 0:c853ba46d0b9 | 745 | default: |
shintamainjp | 0:c853ba46d0b9 | 746 | result = WavFileResultErrorInvalidAudioFormat; |
shintamainjp | 0:c853ba46d0b9 | 747 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 748 | } |
shintamainjp | 0:c853ba46d0b9 | 749 | |
shintamainjp | 0:c853ba46d0b9 | 750 | /* |
shintamainjp | 0:c853ba46d0b9 | 751 | * [data] |
shintamainjp | 0:c853ba46d0b9 | 752 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 753 | * Big endian 4 bytes : Sub chunk ID |
shintamainjp | 0:c853ba46d0b9 | 754 | * Little endian 4 bytes : Sub chunk size |
shintamainjp | 0:c853ba46d0b9 | 755 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 756 | * Little endian 2 bytes : Sample 1 (Ch.1) |
shintamainjp | 0:c853ba46d0b9 | 757 | * Little endian 2 bytes : Sample 1 (Ch.2) |
shintamainjp | 0:c853ba46d0b9 | 758 | * . |
shintamainjp | 0:c853ba46d0b9 | 759 | * . |
shintamainjp | 0:c853ba46d0b9 | 760 | * . |
shintamainjp | 0:c853ba46d0b9 | 761 | * Little endian 2 bytes : Sample 1 (Ch.N) |
shintamainjp | 0:c853ba46d0b9 | 762 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 763 | * Little endian 2 bytes : Sample 2 (Ch.1) |
shintamainjp | 0:c853ba46d0b9 | 764 | * Little endian 2 bytes : Sample 2 (Ch.2) |
shintamainjp | 0:c853ba46d0b9 | 765 | * . |
shintamainjp | 0:c853ba46d0b9 | 766 | * . |
shintamainjp | 0:c853ba46d0b9 | 767 | * . |
shintamainjp | 0:c853ba46d0b9 | 768 | * Little endian 2 bytes : Sample 2 (Ch.N) |
shintamainjp | 0:c853ba46d0b9 | 769 | * ------------------------------------------ |
shintamainjp | 0:c853ba46d0b9 | 770 | */ |
shintamainjp | 0:c853ba46d0b9 | 771 | if (WRITE_U32_BE(p->fp, CHUNK_ID_DATA) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 772 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 773 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 774 | } |
shintamainjp | 0:c853ba46d0b9 | 775 | if (WRITE_U32_LE(p->fp, 0x00000000) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 776 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 777 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 778 | } |
shintamainjp | 0:c853ba46d0b9 | 779 | |
shintamainjp | 0:c853ba46d0b9 | 780 | finalize: |
shintamainjp | 0:c853ba46d0b9 | 781 | if (WavFileResultOK == result) { |
shintamainjp | 0:c853ba46d0b9 | 782 | p->info_checked = true; |
shintamainjp | 0:c853ba46d0b9 | 783 | } |
shintamainjp | 0:c853ba46d0b9 | 784 | return result; |
shintamainjp | 0:c853ba46d0b9 | 785 | } |
shintamainjp | 0:c853ba46d0b9 | 786 | |
shintamainjp | 0:c853ba46d0b9 | 787 | /** |
shintamainjp | 0:c853ba46d0b9 | 788 | */ |
shintamainjp | 0:c853ba46d0b9 | 789 | WavFileResult wavfile_write_data(WAVFILE *p, const wavfile_data_t *data) { |
shintamainjp | 0:c853ba46d0b9 | 790 | WavFileResult result = WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 791 | |
shintamainjp | 0:c853ba46d0b9 | 792 | if (p == NULL) { |
shintamainjp | 0:c853ba46d0b9 | 793 | result = WavFileResultErrorInvalidHandler; |
shintamainjp | 0:c853ba46d0b9 | 794 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 795 | } |
shintamainjp | 0:c853ba46d0b9 | 796 | |
shintamainjp | 0:c853ba46d0b9 | 797 | if (!p->info_checked) { |
shintamainjp | 0:c853ba46d0b9 | 798 | result = WavFileResultErrorNeedInfoChecked; |
shintamainjp | 0:c853ba46d0b9 | 799 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 800 | } |
shintamainjp | 0:c853ba46d0b9 | 801 | |
shintamainjp | 0:c853ba46d0b9 | 802 | if (p->mode != WavFileModeWrite) { |
shintamainjp | 0:c853ba46d0b9 | 803 | result = WavFileResultErrorInvalidMode; |
shintamainjp | 0:c853ba46d0b9 | 804 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 805 | } |
shintamainjp | 0:c853ba46d0b9 | 806 | |
shintamainjp | 0:c853ba46d0b9 | 807 | if (p->info.num_channels != data->num_channels) { |
shintamainjp | 0:c853ba46d0b9 | 808 | result = WavFileResultErrorInvalidNumChannels; |
shintamainjp | 0:c853ba46d0b9 | 809 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 810 | } |
shintamainjp | 0:c853ba46d0b9 | 811 | |
shintamainjp | 0:c853ba46d0b9 | 812 | for (int i = 0; i < p->info.num_channels; i++) { |
shintamainjp | 0:c853ba46d0b9 | 813 | switch (p->info.bits_per_sample) { |
shintamainjp | 0:c853ba46d0b9 | 814 | case BITS_PER_SAMPLE_8: { |
shintamainjp | 0:c853ba46d0b9 | 815 | int n = (int)((double)data->channel_data[i] * 0xFF); |
shintamainjp | 0:c853ba46d0b9 | 816 | if (n < 0x00) { |
shintamainjp | 0:c853ba46d0b9 | 817 | n = 0x00; |
shintamainjp | 0:c853ba46d0b9 | 818 | } |
shintamainjp | 0:c853ba46d0b9 | 819 | if (0xFF < n) { |
shintamainjp | 0:c853ba46d0b9 | 820 | n = 0xFF; |
shintamainjp | 0:c853ba46d0b9 | 821 | } |
shintamainjp | 0:c853ba46d0b9 | 822 | fputc((char)n, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 823 | } |
shintamainjp | 0:c853ba46d0b9 | 824 | p->data_byte_count+=1; |
shintamainjp | 0:c853ba46d0b9 | 825 | break; |
shintamainjp | 0:c853ba46d0b9 | 826 | case BITS_PER_SAMPLE_16: { |
shintamainjp | 0:c853ba46d0b9 | 827 | int n = (int)((double)(data->channel_data[i] * 0xFFFF) - 0x8000); |
shintamainjp | 0:c853ba46d0b9 | 828 | if (0x7FFF < n) { |
shintamainjp | 0:c853ba46d0b9 | 829 | n = 0x7FFF; |
shintamainjp | 0:c853ba46d0b9 | 830 | } |
shintamainjp | 0:c853ba46d0b9 | 831 | if (n < -0x8000) { |
shintamainjp | 0:c853ba46d0b9 | 832 | n = -0x8000; |
shintamainjp | 0:c853ba46d0b9 | 833 | } |
shintamainjp | 0:c853ba46d0b9 | 834 | fputc(((uint16_t)n >> 0) & 0xff, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 835 | fputc(((uint16_t)n >> 8) & 0xff, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 836 | } |
shintamainjp | 0:c853ba46d0b9 | 837 | p->data_byte_count+=2; |
shintamainjp | 0:c853ba46d0b9 | 838 | break; |
shintamainjp | 0:c853ba46d0b9 | 839 | case BITS_PER_SAMPLE_24: { |
shintamainjp | 0:c853ba46d0b9 | 840 | int n = (int)((double)(data->channel_data[i] * 0xFFFFFF) - 0x800000); |
shintamainjp | 0:c853ba46d0b9 | 841 | if (0x7FFFFF < n) { |
shintamainjp | 0:c853ba46d0b9 | 842 | n = 0x7FFFFF; |
shintamainjp | 0:c853ba46d0b9 | 843 | } |
shintamainjp | 0:c853ba46d0b9 | 844 | if (n < -0x800000) { |
shintamainjp | 0:c853ba46d0b9 | 845 | n = -0x800000; |
shintamainjp | 0:c853ba46d0b9 | 846 | } |
shintamainjp | 0:c853ba46d0b9 | 847 | fputc(((uint32_t)n >> 0) & 0xff, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 848 | fputc(((uint32_t)n >> 8) & 0xff, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 849 | fputc(((uint32_t)n >> 16) & 0xff, p->fp); |
shintamainjp | 0:c853ba46d0b9 | 850 | } |
shintamainjp | 0:c853ba46d0b9 | 851 | p->data_byte_count+=3; |
shintamainjp | 0:c853ba46d0b9 | 852 | break; |
shintamainjp | 0:c853ba46d0b9 | 853 | } |
shintamainjp | 0:c853ba46d0b9 | 854 | } |
shintamainjp | 0:c853ba46d0b9 | 855 | p->data_checked = true; |
shintamainjp | 0:c853ba46d0b9 | 856 | |
shintamainjp | 0:c853ba46d0b9 | 857 | finalize: |
shintamainjp | 0:c853ba46d0b9 | 858 | return result; |
shintamainjp | 0:c853ba46d0b9 | 859 | } |
shintamainjp | 0:c853ba46d0b9 | 860 | |
shintamainjp | 0:c853ba46d0b9 | 861 | WavFileResult wavfile_close(WAVFILE *p) { |
shintamainjp | 0:c853ba46d0b9 | 862 | WavFileResult result = WavFileResultOK; |
shintamainjp | 0:c853ba46d0b9 | 863 | |
shintamainjp | 0:c853ba46d0b9 | 864 | switch (p->mode) { |
shintamainjp | 0:c853ba46d0b9 | 865 | case WavFileModeRead: |
shintamainjp | 0:c853ba46d0b9 | 866 | break; |
shintamainjp | 0:c853ba46d0b9 | 867 | case WavFileModeWrite: |
shintamainjp | 0:c853ba46d0b9 | 868 | if (p->info_checked && p->data_checked) { |
shintamainjp | 0:c853ba46d0b9 | 869 | switch (p->info.audio_format) { |
shintamainjp | 0:c853ba46d0b9 | 870 | case WAVFILE_AUDIO_FORMAT_PCM: { |
shintamainjp | 0:c853ba46d0b9 | 871 | /* |
shintamainjp | 0:c853ba46d0b9 | 872 | * Fill the RIFF chunk size. |
shintamainjp | 0:c853ba46d0b9 | 873 | */ |
shintamainjp | 0:c853ba46d0b9 | 874 | if (fseek(p->fp, 4L, SEEK_SET) == -1) { |
shintamainjp | 0:c853ba46d0b9 | 875 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 876 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 877 | } |
shintamainjp | 0:c853ba46d0b9 | 878 | if (WRITE_U32_LE(p->fp, 4 + (8 + CHUNK_SIZE_FMT_PCM) + (8 + p->data_byte_count)) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 879 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 880 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 881 | } |
shintamainjp | 0:c853ba46d0b9 | 882 | |
shintamainjp | 0:c853ba46d0b9 | 883 | /* |
shintamainjp | 0:c853ba46d0b9 | 884 | * Fill the data sub chunk size. |
shintamainjp | 0:c853ba46d0b9 | 885 | */ |
shintamainjp | 0:c853ba46d0b9 | 886 | if (fseek(p->fp, 12 + (8 + CHUNK_SIZE_FMT_PCM) + 4, SEEK_SET) == -1) { |
shintamainjp | 0:c853ba46d0b9 | 887 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 888 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 889 | } |
shintamainjp | 0:c853ba46d0b9 | 890 | if (WRITE_U32_LE(p->fp, p->data_byte_count) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 891 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 892 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 893 | } |
shintamainjp | 0:c853ba46d0b9 | 894 | } |
shintamainjp | 0:c853ba46d0b9 | 895 | break; |
shintamainjp | 0:c853ba46d0b9 | 896 | case WAVFILE_AUDIO_FORMAT_EXTENSIBLE: { |
shintamainjp | 0:c853ba46d0b9 | 897 | /* |
shintamainjp | 0:c853ba46d0b9 | 898 | * Fill the RIFF chunk size. |
shintamainjp | 0:c853ba46d0b9 | 899 | */ |
shintamainjp | 0:c853ba46d0b9 | 900 | if (fseek(p->fp, 4L, SEEK_SET) == -1) { |
shintamainjp | 0:c853ba46d0b9 | 901 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 902 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 903 | } |
shintamainjp | 0:c853ba46d0b9 | 904 | if (WRITE_U32_LE(p->fp, 4 + (8 + CHUNK_SIZE_FMT_EXTENSIBLE) + (8 + p->data_byte_count)) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 905 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 906 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 907 | } |
shintamainjp | 0:c853ba46d0b9 | 908 | |
shintamainjp | 0:c853ba46d0b9 | 909 | /* |
shintamainjp | 0:c853ba46d0b9 | 910 | * Fill the data sub chunk size. |
shintamainjp | 0:c853ba46d0b9 | 911 | */ |
shintamainjp | 0:c853ba46d0b9 | 912 | if (fseek(p->fp, 12 + (8 + CHUNK_SIZE_FMT_EXTENSIBLE) + 4, SEEK_SET) == -1) { |
shintamainjp | 0:c853ba46d0b9 | 913 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 914 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 915 | } |
shintamainjp | 0:c853ba46d0b9 | 916 | if (WRITE_U32_LE(p->fp, p->data_byte_count) != 0) { |
shintamainjp | 0:c853ba46d0b9 | 917 | result = WavFileResultErrorFileWrite; |
shintamainjp | 0:c853ba46d0b9 | 918 | goto finalize; |
shintamainjp | 0:c853ba46d0b9 | 919 | } |
shintamainjp | 0:c853ba46d0b9 | 920 | } |
shintamainjp | 0:c853ba46d0b9 | 921 | break; |
shintamainjp | 0:c853ba46d0b9 | 922 | } |
shintamainjp | 0:c853ba46d0b9 | 923 | } |
shintamainjp | 0:c853ba46d0b9 | 924 | break; |
shintamainjp | 0:c853ba46d0b9 | 925 | } |
shintamainjp | 0:c853ba46d0b9 | 926 | |
shintamainjp | 0:c853ba46d0b9 | 927 | finalize: |
shintamainjp | 0:c853ba46d0b9 | 928 | fclose(p->fp); |
shintamainjp | 0:c853ba46d0b9 | 929 | free(p); |
shintamainjp | 0:c853ba46d0b9 | 930 | return result; |
shintamainjp | 0:c853ba46d0b9 | 931 | } |
shintamainjp | 0:c853ba46d0b9 | 932 | |
shintamainjp | 0:c853ba46d0b9 | 933 | void wavfile_result_string(const WavFileResult result, char *buf, size_t siz) { |
shintamainjp | 0:c853ba46d0b9 | 934 | switch (result) { |
shintamainjp | 0:c853ba46d0b9 | 935 | case WavFileResultOK: |
shintamainjp | 0:c853ba46d0b9 | 936 | strcpy(buf, "OK."); |
shintamainjp | 0:c853ba46d0b9 | 937 | break; |
shintamainjp | 0:c853ba46d0b9 | 938 | case WavFileResultErrorInvalidFileName: |
shintamainjp | 0:c853ba46d0b9 | 939 | strcpy(buf, "Invalid file name found."); |
shintamainjp | 0:c853ba46d0b9 | 940 | break; |
shintamainjp | 0:c853ba46d0b9 | 941 | case WavFileResultErrorMemoryAllocation: |
shintamainjp | 0:c853ba46d0b9 | 942 | strcpy(buf, "Memory allocation error."); |
shintamainjp | 0:c853ba46d0b9 | 943 | break; |
shintamainjp | 0:c853ba46d0b9 | 944 | case WavFileResultErrorFileOpen: |
shintamainjp | 0:c853ba46d0b9 | 945 | strcpy(buf, "File open error found."); |
shintamainjp | 0:c853ba46d0b9 | 946 | break; |
shintamainjp | 0:c853ba46d0b9 | 947 | case WavFileResultErrorFileWrite: |
shintamainjp | 0:c853ba46d0b9 | 948 | strcpy(buf, "File write error found."); |
shintamainjp | 0:c853ba46d0b9 | 949 | break; |
shintamainjp | 0:c853ba46d0b9 | 950 | case WavFileResultErrorBrokenChunkId: |
shintamainjp | 0:c853ba46d0b9 | 951 | strcpy(buf, "Broken chunk ID found."); |
shintamainjp | 0:c853ba46d0b9 | 952 | break; |
shintamainjp | 0:c853ba46d0b9 | 953 | case WavFileResultErrorBrokenChunkSize: |
shintamainjp | 0:c853ba46d0b9 | 954 | strcpy(buf, "Borken chunk size found."); |
shintamainjp | 0:c853ba46d0b9 | 955 | break; |
shintamainjp | 0:c853ba46d0b9 | 956 | case WavFileResultErrorBrokenChunkData: |
shintamainjp | 0:c853ba46d0b9 | 957 | strcpy(buf, "Borken chunk data found."); |
shintamainjp | 0:c853ba46d0b9 | 958 | break; |
shintamainjp | 0:c853ba46d0b9 | 959 | case WavFileResultErrorBrokenFormatId: |
shintamainjp | 0:c853ba46d0b9 | 960 | strcpy(buf, "Broken format ID found."); |
shintamainjp | 0:c853ba46d0b9 | 961 | break; |
shintamainjp | 0:c853ba46d0b9 | 962 | case WavFileResultErrorInvalidFormatId: |
shintamainjp | 0:c853ba46d0b9 | 963 | strcpy(buf, "Invalid format ID found."); |
shintamainjp | 0:c853ba46d0b9 | 964 | break; |
shintamainjp | 0:c853ba46d0b9 | 965 | case WavFileResultErrorBrokenAudioFormat: |
shintamainjp | 0:c853ba46d0b9 | 966 | strcpy(buf, "Broken audio format found."); |
shintamainjp | 0:c853ba46d0b9 | 967 | break; |
shintamainjp | 0:c853ba46d0b9 | 968 | case WavFileResultErrorInvalidAudioFormat: |
shintamainjp | 0:c853ba46d0b9 | 969 | strcpy(buf, "Invalid audio format found."); |
shintamainjp | 0:c853ba46d0b9 | 970 | break; |
shintamainjp | 0:c853ba46d0b9 | 971 | case WavFileResultErrorInvalidNumChannels: |
shintamainjp | 0:c853ba46d0b9 | 972 | strcpy(buf, "Invalid number of channels found."); |
shintamainjp | 0:c853ba46d0b9 | 973 | break; |
shintamainjp | 0:c853ba46d0b9 | 974 | case WavFileResultErrorBrokenNumChannels: |
shintamainjp | 0:c853ba46d0b9 | 975 | strcpy(buf, "Broken number of channels found."); |
shintamainjp | 0:c853ba46d0b9 | 976 | break; |
shintamainjp | 0:c853ba46d0b9 | 977 | case WavFileResultErrorBrokenSampleRate: |
shintamainjp | 0:c853ba46d0b9 | 978 | strcpy(buf, "Broken sample rate found."); |
shintamainjp | 0:c853ba46d0b9 | 979 | break; |
shintamainjp | 0:c853ba46d0b9 | 980 | case WavFileResultErrorBrokenByteRate: |
shintamainjp | 0:c853ba46d0b9 | 981 | strcpy(buf, "Broken byte rate found."); |
shintamainjp | 0:c853ba46d0b9 | 982 | break; |
shintamainjp | 0:c853ba46d0b9 | 983 | case WavFileResultErrorInvalidByteRate: |
shintamainjp | 0:c853ba46d0b9 | 984 | strcpy(buf, "Invalid byte rate found."); |
shintamainjp | 0:c853ba46d0b9 | 985 | break; |
shintamainjp | 0:c853ba46d0b9 | 986 | case WavFileResultErrorBrokenBlockAlign: |
shintamainjp | 0:c853ba46d0b9 | 987 | strcpy(buf, "Broken block alignment found."); |
shintamainjp | 0:c853ba46d0b9 | 988 | break; |
shintamainjp | 0:c853ba46d0b9 | 989 | case WavFileResultErrorBrokenBitsPerSample: |
shintamainjp | 0:c853ba46d0b9 | 990 | strcpy(buf, "Broken bits per sample found."); |
shintamainjp | 0:c853ba46d0b9 | 991 | break; |
shintamainjp | 0:c853ba46d0b9 | 992 | case WavFileResultErrorUnsupportedBitsPerSample: |
shintamainjp | 0:c853ba46d0b9 | 993 | strcpy(buf, "Unsupported bits per sample found."); |
shintamainjp | 0:c853ba46d0b9 | 994 | break; |
shintamainjp | 0:c853ba46d0b9 | 995 | case WavFileResultErrorAlreadyInfoChecked: |
shintamainjp | 0:c853ba46d0b9 | 996 | strcpy(buf, "Already checked info."); |
shintamainjp | 0:c853ba46d0b9 | 997 | break; |
shintamainjp | 0:c853ba46d0b9 | 998 | case WavFileResultErrorAlreadyDataChecked: |
shintamainjp | 0:c853ba46d0b9 | 999 | strcpy(buf, "Already checked data."); |
shintamainjp | 0:c853ba46d0b9 | 1000 | break; |
shintamainjp | 0:c853ba46d0b9 | 1001 | case WavFileResultErrorNoDataChunk: |
shintamainjp | 0:c853ba46d0b9 | 1002 | strcpy(buf, "No data chunk."); |
shintamainjp | 0:c853ba46d0b9 | 1003 | break; |
shintamainjp | 0:c853ba46d0b9 | 1004 | case WavFileResultErrorInvalidMode: |
shintamainjp | 0:c853ba46d0b9 | 1005 | strcpy(buf, "Invalid mode."); |
shintamainjp | 0:c853ba46d0b9 | 1006 | break; |
shintamainjp | 0:c853ba46d0b9 | 1007 | case WavFileResultErrorNeedInfoChecked: |
shintamainjp | 0:c853ba46d0b9 | 1008 | strcpy(buf, "Need check info."); |
shintamainjp | 0:c853ba46d0b9 | 1009 | break; |
shintamainjp | 0:c853ba46d0b9 | 1010 | case WavFileResultErrorNeedDataChecked: |
shintamainjp | 0:c853ba46d0b9 | 1011 | strcpy(buf, "Need check data."); |
shintamainjp | 0:c853ba46d0b9 | 1012 | break; |
shintamainjp | 0:c853ba46d0b9 | 1013 | case WavFileResultErrorInvalidHandler: |
shintamainjp | 0:c853ba46d0b9 | 1014 | strcpy(buf, "Invalid handler."); |
shintamainjp | 0:c853ba46d0b9 | 1015 | break; |
shintamainjp | 0:c853ba46d0b9 | 1016 | default: |
shintamainjp | 0:c853ba46d0b9 | 1017 | strcpy(buf, "Unkonwn error found."); |
shintamainjp | 0:c853ba46d0b9 | 1018 | break; |
shintamainjp | 0:c853ba46d0b9 | 1019 | } |
shintamainjp | 0:c853ba46d0b9 | 1020 | } |
shintamainjp | 0:c853ba46d0b9 | 1021 |