1 /**
2  * @file aufile.c  Audio File interface
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <string.h>
7 #include <re.h>
8 #include <rem_au.h>
9 #include <rem_aufile.h>
10 #include "aufile.h"
11 
12 
13 /** Audio file state */
14 struct aufile {
15 	struct aufile_prm prm;
16 	enum aufile_mode mode;
17 	size_t datasize;
18 	size_t nread;
19 	size_t nwritten;
20 	FILE *f;
21 };
22 
23 
24 static int wavfmt_to_aufmt(enum wavfmt fmt, uint16_t bps)
25 {
26 	switch (fmt) {
27 
28 	case WAVE_FMT_PCM:
29 		if (bps != 16)
30 			return -1;
31 
32 		return AUFMT_S16LE;
33 
34 	case WAVE_FMT_ALAW:
35 		if (bps != 8)
36 			return -1;
37 
38 		return AUFMT_PCMA;
39 
40 	case WAVE_FMT_ULAW:
41 		if (bps != 8)
42 			return -1;
43 
44 		return AUFMT_PCMU;
45 
46 	default:
47 		return -1;
48 	}
49 }
50 
51 
52 static enum wavfmt aufmt_to_wavfmt(enum aufmt fmt)
53 {
54 	switch (fmt) {
55 
56 	case AUFMT_S16LE:  return WAVE_FMT_PCM;
57 	case AUFMT_PCMA:   return WAVE_FMT_ALAW;
58 	case AUFMT_PCMU:   return WAVE_FMT_ULAW;
59 	default:           return -1;
60 	}
61 }
62 
63 
64 static uint16_t aufmt_to_bps(enum aufmt fmt)
65 {
66 	switch (fmt) {
67 
68 	case AUFMT_S16LE: return 16;
69 	case AUFMT_PCMA:  return 8;
70 	case AUFMT_PCMU:  return 8;
71 	default:          return 0;
72 	}
73 }
74 
75 
76 static void destructor(void *arg)
77 {
78 	struct aufile *af = arg;
79 
80 	if (!af->f)
81 		return;
82 
83 	/* Update WAV header in write-mode */
84 	if (af->mode == AUFILE_WRITE && af->nwritten > 0) {
85 
86 		rewind(af->f);
87 
88 		(void)wav_header_encode(af->f, aufmt_to_wavfmt(af->prm.fmt),
89 					af->prm.channels, af->prm.srate,
90 					aufmt_to_bps(af->prm.fmt),
91 					af->nwritten);
92 	}
93 
94 	(void)fclose(af->f);
95 }
96 
97 
98 /**
99  * Open a WAVE file for reading or writing
100  *
101  * Supported formats:  16-bit PCM, A-law, U-law
102  *
103  * @param afp       Pointer to allocated Audio file
104  * @param prm       Audio format of the file
105  * @param filename  Filename of the WAV-file to load
106  * @param mode      Read or write mode
107  *
108  * @return 0 if success, otherwise errorcode
109  */
110 int aufile_open(struct aufile **afp, struct aufile_prm *prm,
111 		const char *filename, enum aufile_mode mode)
112 {
113 	struct wav_fmt fmt;
114 	struct aufile *af;
115 	int aufmt;
116 	int err;
117 
118 	if (!afp || !filename || (mode == AUFILE_WRITE && !prm))
119 		return EINVAL;
120 
121 	af = mem_zalloc(sizeof(*af), destructor);
122 	if (!af)
123 		return ENOMEM;
124 
125 	af->mode = mode;
126 
127 	af->f = fopen(filename, mode == AUFILE_READ ? "rb" : "wb");
128 	if (!af->f) {
129 		err = errno;
130 		goto out;
131 	}
132 
133 	switch (mode) {
134 
135 	case AUFILE_READ:
136 		err = wav_header_decode(&fmt, &af->datasize, af->f);
137 		if (err)
138 			goto out;
139 
140 		aufmt = wavfmt_to_aufmt(fmt.format, fmt.bps);
141 		if (aufmt < 0) {
142 			err = ENOSYS;
143 			goto out;
144 		}
145 
146 		if (prm) {
147 			prm->srate    = fmt.srate;
148 			prm->channels = (uint8_t)fmt.channels;
149 			prm->fmt      = aufmt;
150 		}
151 		break;
152 
153 	case AUFILE_WRITE:
154 		af->prm = *prm;
155 
156 		err = wav_header_encode(af->f, aufmt_to_wavfmt(prm->fmt),
157 					prm->channels, prm->srate,
158 					aufmt_to_bps(prm->fmt), 0);
159 		break;
160 
161 	default:
162 		err = ENOSYS;
163 		break;
164 	}
165 
166  out:
167 	if (err)
168 		mem_deref(af);
169 	else
170 		*afp = af;
171 
172 	return err;
173 }
174 
175 
176 /**
177  * Read PCM-samples from a WAV file
178  *
179  * @param af  Audio-file
180  * @param p   Read buffer
181  * @param sz  Size of buffer, on return contains actual read
182  *
183  * @return 0 if success, otherwise errorcode
184  */
185 int aufile_read(struct aufile *af, uint8_t *p, size_t *sz)
186 {
187 	size_t n;
188 
189 	if (!af || !p || !sz || af->mode != AUFILE_READ)
190 		return EINVAL;
191 
192 	if (af->nread >= af->datasize) {
193 		*sz = 0;
194 		return 0;
195 	}
196 
197 	n = min(*sz, af->datasize - af->nread);
198 
199 	n = fread(p, 1, n, af->f);
200 	if (ferror(af->f))
201 		return errno;
202 
203 	*sz = n;
204 	af->nread += n;
205 
206 	return 0;
207 }
208 
209 
210 /**
211  * Write PCM-samples to a WAV file
212  *
213  * @param af  Audio-file
214  * @param p   Write buffer
215  * @param sz  Size of buffer
216  *
217  * @return 0 if success, otherwise errorcode
218  */
219 int aufile_write(struct aufile *af, const uint8_t *p, size_t sz)
220 {
221 	if (!af || !p || !sz || af->mode != AUFILE_WRITE)
222 		return EINVAL;
223 
224 	if (1 != fwrite(p, sz, 1, af->f))
225 		return ferror(af->f);
226 
227 	af->nwritten += sz;
228 
229 	return 0;
230 }
231