| 1 | /* ------------------------------------------------------------------ |
| 2 | * Copyright (C) 2009 Martin Storsjo |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| 13 | * express or implied. |
| 14 | * See the License for the specific language governing permissions |
| 15 | * and limitations under the License. |
| 16 | * ------------------------------------------------------------------- |
| 17 | */ |
| 18 | |
| 19 | #include "wavreader.h" |
| 20 | #include <stdio.h> |
| 21 | #include <stdlib.h> |
| 22 | #include <string.h> |
| 23 | #include <stdint.h> |
| 24 | |
| 25 | #define TAG(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) |
| 26 | |
| 27 | struct wav_reader { |
| 28 | FILE *wav; |
| 29 | uint32_t data_length; |
| 30 | |
| 31 | int format; |
| 32 | int sample_rate; |
| 33 | int bits_per_sample; |
| 34 | int channels; |
| 35 | int byte_rate; |
| 36 | int block_align; |
| 37 | }; |
| 38 | |
| 39 | static uint32_t read_tag(struct wav_reader* wr) { |
| 40 | uint32_t tag = 0; |
| 41 | tag = (tag << 8) | fgetc(wr->wav); |
| 42 | tag = (tag << 8) | fgetc(wr->wav); |
| 43 | tag = (tag << 8) | fgetc(wr->wav); |
| 44 | tag = (tag << 8) | fgetc(wr->wav); |
| 45 | return tag; |
| 46 | } |
| 47 | |
| 48 | static uint32_t read_int32(struct wav_reader* wr) { |
| 49 | uint32_t value = 0; |
| 50 | value |= fgetc(wr->wav) << 0; |
| 51 | value |= fgetc(wr->wav) << 8; |
| 52 | value |= fgetc(wr->wav) << 16; |
| 53 | value |= fgetc(wr->wav) << 24; |
| 54 | return value; |
| 55 | } |
| 56 | |
| 57 | static uint16_t read_int16(struct wav_reader* wr) { |
| 58 | uint16_t value = 0; |
| 59 | value |= fgetc(wr->wav) << 0; |
| 60 | value |= fgetc(wr->wav) << 8; |
| 61 | return value; |
| 62 | } |
| 63 | |
| 64 | void* wav_read_open(const char *filename) { |
| 65 | struct wav_reader* wr = (struct wav_reader*) malloc(sizeof(*wr)); |
| 66 | long data_pos = 0; |
| 67 | memset(wr, 0, sizeof(*wr)); |
| 68 | |
| 69 | wr->wav = fopen(filename, "rb"); |
| 70 | if (wr->wav == NULL) { |
| 71 | free(wr); |
| 72 | return NULL; |
| 73 | } |
| 74 | |
| 75 | while (1) { |
| 76 | uint32_t tag, tag2, length; |
| 77 | tag = read_tag(wr); |
| 78 | if (feof(wr->wav)) |
| 79 | break; |
| 80 | length = read_int32(wr); |
| 81 | if (tag != TAG('R', 'I', 'F', 'F') || length < 4) { |
| 82 | fseek(wr->wav, length, SEEK_CUR); |
| 83 | continue; |
| 84 | } |
| 85 | tag2 = read_tag(wr); |
| 86 | length -= 4; |
| 87 | if (tag2 != TAG('W', 'A', 'V', 'E')) { |
| 88 | fseek(wr->wav, length, SEEK_CUR); |
| 89 | continue; |
| 90 | } |
| 91 | // RIFF chunk found, iterate through it |
| 92 | while (length >= 8) { |
| 93 | uint32_t subtag, sublength; |
| 94 | subtag = read_tag(wr); |
| 95 | if (feof(wr->wav)) |
| 96 | break; |
| 97 | sublength = read_int32(wr); |
| 98 | length -= 8; |
| 99 | if (length < sublength) |
| 100 | break; |
| 101 | if (subtag == TAG('f', 'm', 't', ' ')) { |
| 102 | if (sublength < 16) { |
| 103 | // Insufficient data for 'fmt ' |
| 104 | break; |
| 105 | } |
| 106 | wr->format = read_int16(wr); |
| 107 | wr->channels = read_int16(wr); |
| 108 | wr->sample_rate = read_int32(wr); |
| 109 | wr->byte_rate = read_int32(wr); |
| 110 | wr->block_align = read_int16(wr); |
| 111 | wr->bits_per_sample = read_int16(wr); |
| 112 | fseek(wr->wav, sublength - 16, SEEK_CUR); |
| 113 | } else if (subtag == TAG('d', 'a', 't', 'a')) { |
| 114 | data_pos = ftell(wr->wav); |
| 115 | wr->data_length = sublength; |
| 116 | fseek(wr->wav, sublength, SEEK_CUR); |
| 117 | } else { |
| 118 | fseek(wr->wav, sublength, SEEK_CUR); |
| 119 | } |
| 120 | length -= sublength; |
| 121 | } |
| 122 | if (length > 0) { |
| 123 | // Bad chunk? |
| 124 | fseek(wr->wav, length, SEEK_CUR); |
| 125 | } |
| 126 | } |
| 127 | fseek(wr->wav, data_pos, SEEK_SET); |
| 128 | return wr; |
| 129 | } |
| 130 | |
| 131 | void wav_read_close(void* obj) { |
| 132 | struct wav_reader* wr = (struct wav_reader*) obj; |
| 133 | fclose(wr->wav); |
| 134 | free(wr); |
| 135 | } |
| 136 | |
| 137 | int wav_get_header(void* obj, int* format, int* channels, int* sample_rate, int* bits_per_sample, unsigned int* data_length) { |
| 138 | struct wav_reader* wr = (struct wav_reader*) obj; |
| 139 | if (format) |
| 140 | *format = wr->format; |
| 141 | if (channels) |
| 142 | *channels = wr->channels; |
| 143 | if (sample_rate) |
| 144 | *sample_rate = wr->sample_rate; |
| 145 | if (bits_per_sample) |
| 146 | *bits_per_sample = wr->bits_per_sample; |
| 147 | if (data_length) |
| 148 | *data_length = wr->data_length; |
| 149 | return wr->format && wr->sample_rate; |
| 150 | } |
| 151 | |
| 152 | int wav_read_data(void* obj, unsigned char* data, unsigned int length) { |
| 153 | struct wav_reader* wr = (struct wav_reader*) obj; |
| 154 | int n; |
| 155 | if (wr->wav == NULL) |
| 156 | return -1; |
| 157 | if (length > wr->data_length) |
| 158 | length = wr->data_length; |
| 159 | n = fread(data, 1, length, wr->wav); |
| 160 | wr->data_length -= length; |
| 161 | return n; |
| 162 | } |
| 163 | |