librfn
An ad-hoc utility library
wavheader.c
Go to the documentation of this file.
1 /*
2  * wavheader.c
3  *
4  * Part of librfn (a general utility library from redfelineninja.org.uk)
5  *
6  * Copyright (C) 2012 Daniel Thompson <daniel@redfelineninja.org.uk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published
10  * by the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  */
13 
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 
19 #include "librfn.h"
20 
21 const uint8_t null_id[] = { 0, 0, 0, 0 };
22 const uint8_t riff[] = { 'R', 'I', 'F', 'F' };
23 const uint8_t wave[] = { 'W', 'A', 'V', 'E' };
24 const uint8_t fmt[] = { 'f' , 'm', 't', ' ' };
25 const uint8_t fact[] = { 'f', 'a', 'c', 't' };
26 const uint8_t data[] = { 'd', 'a', 't', 'a' };
27 
28 
29 int rf_wavheader_decode(const uint8_t *p, unsigned int sz, rf_wavheader_t *wh)
30 {
31  rf_pack_t pack;
32  rf_pack_init(&pack, (void *) p, sz);
33 
34  memset(wh, 0, sizeof(*wh));
35 
36  rf_unpack_bytes(&pack, wh->chunk_id, 4);
37  wh->chunk_size = rf_unpack_u32le(&pack);
38  rf_unpack_bytes(&pack, wh->format, 4);
39 
40  rf_unpack_bytes(&pack, wh->fmt_chunk_id, 4);
41  wh->fmt_chunk_size = rf_unpack_u32le(&pack);
42  wh->audio_format = rf_unpack_u16le(&pack);
43  wh->num_channels = rf_unpack_u16le(&pack);
44  wh->sample_rate = rf_unpack_u32le(&pack);
45  wh->byte_rate = rf_unpack_u32le(&pack);
46  wh->block_align = rf_unpack_u16le(&pack);
47  wh->bits_per_sample = rf_unpack_u16le(&pack);
48  if (wh->fmt_chunk_size >= 18) {
49  wh->cb_size = rf_unpack_u16le(&pack);
50 
51  if (22 == wh->cb_size) {
53  wh->channel_mask = rf_unpack_u32le(&pack);
54  rf_unpack_bytes(&pack, wh->sub_format, 16);
55  } else {
56  rf_unpack_bytes(&pack, NULL, (wh->fmt_chunk_size - 18));
57  }
58  }
59 
60  rf_unpack_bytes(&pack, wh->data_chunk_id, 4);
61 
62  /* this might actually be a fact chunk rather than a data chunk,
63  * if so we need to grab that (in order to handle IEEE f.p. samples)
64  */
65  if (0 == memcmp(fact, wh->data_chunk_id, 4)) {
66  memcpy(wh->fact_chunk_id, wh->data_chunk_id, 4);
67  wh->fact_chunk_size = rf_unpack_u32le(&pack);
68  wh->sample_length = rf_unpack_u32le(&pack);
69 
70  rf_unpack_bytes(&pack, wh->data_chunk_id, 4);
71  }
72 
73  wh->data_chunk_size = rf_unpack_u32le(&pack);
74 
75  /* do some basic validation */
76  if (0 != memcmp(riff, wh->chunk_id, 4))
77  return -EINVAL;
78  if (wh->chunk_size < (12 + wh->fmt_chunk_size + wh->fact_chunk_size))
79  return -EINVAL;
80  if (0 != memcmp(wave, wh->format, 4))
81  return -EINVAL;
82 
83  /* if we have tried to read past the end of the buffer then
84  * rf_pack_remaining() will return a -ve number and therefore
85  * the return value will be larger than the value supplied.
86  */
87  return sz - rf_pack_remaining(&pack);
88 }
89 
90 int rf_wavheader_encode(rf_wavheader_t *wh, uint8_t *p, unsigned int sz)
91 {
92  rf_pack_t pack;
93  rf_pack_init(&pack, (void *) p, sz);
94 
95  rf_pack_bytes(&pack, wh->chunk_id, 4);
96  rf_pack_u32le(&pack, wh->chunk_size);
97  rf_pack_bytes(&pack, wh->format, 4);
98 
99  rf_pack_bytes(&pack, wh->fmt_chunk_id, 4);
100  rf_pack_u32le(&pack, wh->fmt_chunk_size);
101  rf_pack_u16le(&pack, wh->audio_format);
102  rf_pack_u16le(&pack, wh->num_channels);
103  rf_pack_u32le(&pack, wh->sample_rate);
104  rf_pack_u32le(&pack, wh->byte_rate);
105  rf_pack_u16le(&pack, wh->block_align);
106  rf_pack_u16le(&pack, wh->bits_per_sample);
107  if (wh->fmt_chunk_size >= 18) {
108  rf_pack_u16le(&pack, wh->cb_size);
109 
110  if (22 == wh->cb_size) {
112  rf_pack_u32le(&pack, wh->channel_mask);
113  rf_pack_bytes(&pack, wh->sub_format, 16);
114  } else {
115  rf_pack_bytes(&pack, NULL, (wh->fmt_chunk_size - 18));
116  }
117  }
118 
119  if (0 == memcmp(fact, wh->fact_chunk_id, 4)) {
120  rf_pack_bytes(&pack, wh->fact_chunk_id, 4);
121  rf_pack_u32le(&pack, wh->fact_chunk_size);
122  rf_pack_u32le(&pack, wh->sample_length);
123  }
124 
125  rf_pack_bytes(&pack, wh->data_chunk_id, 4);
126  rf_pack_u32le(&pack, wh->data_chunk_size);
127 
128  /* if we have tried to write past the end of the buffer then
129  * rf_pack_remaining() will return a -ve number and therefore
130  * the return value will be larger than the value supplied.
131  */
132  return sz - rf_pack_remaining(&pack);
133 }
134 
136 {
137  switch (wh->audio_format) {
138  case 1:
139  return (wh->bits_per_sample == 16 ? RF_WAVHEADER_S16LE :
140  wh->bits_per_sample == 32 ? RF_WAVHEADER_S32LE :
142  case 3:
143  return (wh->bits_per_sample == 32 ? RF_WAVHEADER_FLOAT :
144  RF_WAVHEADER_UNKNOWN);
145  case 0xfffe:
146  // TODO: add support for f.p. sampes in extended format
147  return (wh->bits_per_sample == 16 ? RF_WAVHEADER_S16LE :
149  default:
150  return RF_WAVHEADER_UNKNOWN;
151  }
152 }
153 
154 void rf_wavheader_init(rf_wavheader_t *wh, int sfreq, int num_channels,
155  rf_wavheader_format_t format)
156 {
157  memcpy(wh->chunk_id, riff, 4);
158  wh->chunk_size = 12 + 18 + 12 + 8; // chunks: riff, fmt, fact, data
159  memcpy(wh->format, wave, 4);
160 
161  memcpy(wh->fmt_chunk_id, fmt, 4);
162  wh->fmt_chunk_size = (format == RF_WAVHEADER_FLOAT ? 18 : 16);
163  wh->audio_format = (format == RF_WAVHEADER_FLOAT ? 3 : 1);
164  wh->num_channels = num_channels;
165  wh->sample_rate = sfreq;
166 
167  uint8_t bytes_per_sample = (format == RF_WAVHEADER_S16LE ? 2 : 4);
168  wh->byte_rate = sfreq * bytes_per_sample * num_channels;
169  wh->block_align = bytes_per_sample * num_channels;
170  wh->bits_per_sample = bytes_per_sample * 8;
171  wh->cb_size = 0;
172  // when cb_size is zero the rest of the fmt chunk is absent
173 
174  if (format == RF_WAVHEADER_FLOAT) {
175  memcpy(wh->fact_chunk_id, fact, 4);
176  wh->fact_chunk_size = 12;
177  wh->sample_length = 0; // must be filled in after writing the samples
178  }
179 
180  memcpy(wh->data_chunk_id, data, 4);
181  wh->data_chunk_size = 0;
182 }
183 
184 void rf_wavheader_set_num_frames(rf_wavheader_t *wh, unsigned int num_frames)
185 {
186  // return chunk_size to merely the size of the header
187  wh->chunk_size -= wh->data_chunk_size;
188 
189  wh->data_chunk_size = num_frames * wh->block_align;
190  // doesn't matter if there is no fact chunk, we'll not emit this if this
191  // chunk is absent
192  wh->sample_length = num_frames * wh->num_channels;
193  wh->chunk_size += num_frames * wh->block_align;
194 }
195 
196 static const char *format_tostring(rf_wavheader_format_t format)
197 {
198  switch(format) {
199 #define C(x) case RF_WAVHEADER_##x: return #x
200  C(S16LE);
201  C(S32LE);
202  C(FLOAT);
203 #undef C
204  default:
205  return "UNKNOWN";
206  }
207 }
208 
209 
210 
212 {
213  return strdup_printf("WAVE file: %d samples in %s %dch %dHz",
214  wh->data_chunk_size / (wh->block_align),
215  format_tostring(rf_wavheader_get_format(wh)),
216  wh->num_channels, wh->sample_rate);
217 
218 }
219 
221 {
222  if (0 != memcmp(riff, wh->chunk_id, 4))
223  return -EINVAL;
224  if (wh->chunk_size < (12 + wh->fmt_chunk_size + wh->fact_chunk_size))
225  return -EINVAL;
226  if (0 != memcmp(wave, wh->format, 4))
227  return -EINVAL;
228 
229  if (0 != memcmp(fmt, wh->fmt_chunk_id, 4))
230  return -EINVAL;
231 
232  if (0 != memcmp(data, wh->data_chunk_id, 4))
233  return -EINVAL;
234 
235  return 0;
236 }
const uint8_t fact[]
Definition: wavheader.c:25
uint32_t data_chunk_size
Definition: wavheader.h:51
const uint8_t null_id[]
Definition: wavheader.c:21
void rf_pack_u16le(rf_pack_t *pack, uint16_t u16)
Definition: pack.c:68
uint32_t channel_mask
Definition: wavheader.h:43
uint32_t byte_rate
Definition: wavheader.h:38
void rf_pack_u32le(rf_pack_t *pack, uint32_t u32)
Definition: pack.c:89
uint16_t block_align
Definition: wavheader.h:39
uint32_t sample_rate
Definition: wavheader.h:37
void rf_wavheader_set_num_frames(rf_wavheader_t *wh, unsigned int num_frames)
Definition: wavheader.c:184
int rf_wavheader_decode(const uint8_t *p, unsigned int sz, rf_wavheader_t *wh)
Definition: wavheader.c:29
Definition: pack.h:30
char * rf_wavheader_tostring(rf_wavheader_t *wh)
Definition: wavheader.c:211
uint32_t sample_length
Definition: wavheader.h:48
uint8_t sub_format[16]
Definition: wavheader.h:44
uint16_t valid_bits_per_sample
Definition: wavheader.h:42
void rf_pack_bytes(rf_pack_t *pack, void *p, unsigned int sz)
Definition: pack.c:38
uint32_t fmt_chunk_size
Definition: wavheader.h:34
#define C(x)
uint32_t fact_chunk_size
Definition: wavheader.h:47
void rf_wavheader_init(rf_wavheader_t *wh, int sfreq, int num_channels, rf_wavheader_format_t format)
Definition: wavheader.c:154
rf_wavheader_format_t rf_wavheader_get_format(rf_wavheader_t *wh)
Definition: wavheader.c:135
uint8_t fmt_chunk_id[4]
Definition: wavheader.h:33
uint8_t chunk_id[4]
Definition: wavheader.h:29
int rf_pack_remaining(rf_pack_t *pack)
Definition: pack.c:28
rf_wavheader_format_t
Definition: wavheader.h:54
const uint8_t data[]
Definition: wavheader.c:26
uint8_t data_chunk_id[4]
Definition: wavheader.h:50
uint8_t format[4]
Definition: wavheader.h:31
uint8_t fact_chunk_id[4]
Definition: wavheader.h:46
const uint8_t riff[]
Definition: wavheader.c:22
int rf_wavheader_encode(rf_wavheader_t *wh, uint8_t *p, unsigned int sz)
Definition: wavheader.c:90
uint32_t rf_unpack_u32le(rf_pack_t *pack)
Definition: pack.c:159
uint16_t rf_unpack_u16le(rf_pack_t *pack)
Definition: pack.c:148
void rf_pack_init(rf_pack_t *pack, void *p, unsigned int sz)
Definition: pack.c:21
char * strdup_printf(const char *format,...)
Definition: string.c:88
void rf_unpack_bytes(rf_pack_t *pack, void *p, unsigned int sz)
Definition: pack.c:106
const uint8_t wave[]
Definition: wavheader.c:23
const uint8_t fmt[]
Definition: wavheader.c:24
uint16_t audio_format
Definition: wavheader.h:35
uint16_t bits_per_sample
Definition: wavheader.h:40
uint32_t chunk_size
Definition: wavheader.h:30
uint16_t num_channels
Definition: wavheader.h:36
int rf_wavheader_validate(rf_wavheader_t *wh)
Definition: wavheader.c:220
uint16_t cb_size
Definition: wavheader.h:41