1 /******************************************************************************
2  *
3  *  Copyright 2022 Google LLC
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #include <stdint.h>
20 #include "wave.h"
21 
22 
23 /**
24  * Id formatting
25  */
26 
27 #define __WAVE_ID(s) \
28     (uint32_t)( s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24) )
29 
30 
31 /**
32  * File format statement
33  * | type_id     WAVE_FILE_TYPE_ID
34  * | size        File size - 8 bytes
35  * | type_id     WAVE_FILE_FMT_ID
36  */
37 
38 #define WAVE_FILE_TYPE_ID  __WAVE_ID("RIFF")
39 #define WAVE_FILE_FMT_ID   __WAVE_ID("WAVE")
40 
41 struct wave_file {
42     uint32_t type_id;
43     uint32_t size;
44     uint32_t fmt_id;
45 };
46 
47 
48 /**
49  * Audio format statement
50  * | id          WAVE_FORMAT_ID
51  * | size        Size of the block - 8 bytes (= 16 bytes)
52  * | format      WAVE_FORMAT_PCM or WAVE_FORMAT_EXT
53  * | channels    Number of channels
54  * | samplerate  Sampling rate
55  * | byterate    Bytes per secondes = `samplerate * framesize`
56  * | framesize   Bytes per sampling time = `channels * bitdepth / 8`
57  * | bitdepth    Number of bits per sample
58  */
59 
60 #define WAVE_FORMAT_ID   __WAVE_ID("fmt ")
61 #define WAVE_FORMAT_PCM  0x0001
62 #define WAVE_FORMAT_EXT  0xfffe
63 
64 struct wave_format {
65     uint32_t id;
66     uint32_t size;
67     uint16_t fmt;
68     uint16_t channels;
69     uint32_t samplerate;
70     uint32_t byterate;
71     uint16_t framesize;
72     uint16_t bitdepth;
73 };
74 
75 
76 /**
77  * Audio data statement
78  * | id          WAV_DATA_ID
79  * | size        Size of the data following
80  */
81 
82 #define WAVE_DATA_ID  __WAVE_ID("data")
83 
84 struct wave_data {
85     uint32_t id;
86     uint32_t size;
87 };
88 
89 
90 /**
91  * Read WAVE file header
92  */
wave_read_header(FILE * fp,int * bitdepth,int * samplesize,int * samplerate,int * nchannels,int * nframes)93 int wave_read_header(FILE *fp, int *bitdepth, int *samplesize,
94     int *samplerate, int *nchannels, int *nframes)
95 {
96     struct wave_file file;
97     struct wave_format format;
98     struct wave_data data;
99 
100     if (fread(&file, sizeof(file), 1, fp) != 1
101             || file.type_id != WAVE_FILE_TYPE_ID
102             || file.fmt_id  != WAVE_FILE_FMT_ID)
103         return -1;
104 
105     if (fread(&format, sizeof(format), 1, fp) != 1
106             || format.id         != WAVE_FORMAT_ID
107             || ( format.fmt      != WAVE_FORMAT_PCM &&
108                  format.fmt      != WAVE_FORMAT_EXT   )
109             || format.channels   <= 0
110             || format.samplerate <= 0
111             || format.framesize  <= 0
112             || format.byterate   != format.samplerate * format.framesize)
113         return -1;
114 
115     fseek(fp, sizeof(format) - (8 + format.size), SEEK_CUR);
116 
117     for ( ; fread(&data, sizeof(data), 1, fp) == 1 && data.id != WAVE_DATA_ID
118           ; fseek(fp, data.size, SEEK_CUR) );
119 
120     if (feof(fp))
121       return -1;
122 
123     *bitdepth = format.bitdepth;
124     *samplesize = format.framesize / format.channels;
125     *samplerate = format.samplerate;
126     *nchannels = format.channels;
127     *nframes = data.size / format.framesize;
128 
129     return 0;
130 }
131 
132 /**
133  * Read PCM samples from wave file
134  */
wave_read_pcm(FILE * fp,int samplesize,int nch,int count,void * buffer)135 int wave_read_pcm(FILE *fp, int samplesize,
136     int nch, int count, void *buffer)
137 {
138     return fread(buffer, nch * samplesize, count, fp);
139 }
140 
141 /**
142  * Write WAVE file header
143  */
wave_write_header(FILE * fp,int bitdepth,int samplesize,int samplerate,int nchannels,int nframes)144 void wave_write_header(FILE *fp, int bitdepth, int samplesize,
145     int samplerate, int nchannels, int nframes)
146 {
147     struct {
148         struct wave_file file;
149         struct wave_format format;
150         struct wave_data data;
151     } header;
152 
153     long data_size = nchannels * nframes * samplesize;
154     long file_size = sizeof(header) + data_size;
155 
156     header.file = (struct wave_file){
157         WAVE_FILE_TYPE_ID, file_size - 8,
158         .fmt_id = WAVE_FILE_FMT_ID
159     };
160 
161     header.format = (struct wave_format){
162         WAVE_FORMAT_ID, sizeof(header.format) - 8,
163         .fmt = WAVE_FORMAT_PCM,
164         .channels = nchannels,
165         .samplerate = samplerate,
166         .byterate = samplerate * nchannels * samplesize,
167         .framesize = nchannels * samplesize,
168         .bitdepth = bitdepth,
169     };
170 
171     header.data = (struct wave_data){
172         WAVE_DATA_ID, data_size
173     };
174 
175     fwrite(&header, sizeof(header), 1, fp);
176 }
177 
178 /**
179  * Write PCM samples to wave file
180  */
wave_write_pcm(FILE * fp,int samplesize,const void * _pcm,int nch,int off,int count)181 void wave_write_pcm(FILE *fp, int samplesize,
182     const void *_pcm, int nch, int off, int count)
183 {
184     const int8_t *pcm = _pcm;
185     fwrite(pcm + nch * off * samplesize, nch * samplesize, count, fp);
186 }
187