diff options
Diffstat (limited to 'apps/codecs/wav_enc.c')
-rw-r--r-- | apps/codecs/wav_enc.c | 419 |
1 files changed, 322 insertions, 97 deletions
diff --git a/apps/codecs/wav_enc.c b/apps/codecs/wav_enc.c index 5aabb5d8e8..974a903310 100644 --- a/apps/codecs/wav_enc.c +++ b/apps/codecs/wav_enc.c | |||
@@ -19,140 +19,364 @@ | |||
19 | 19 | ||
20 | #ifndef SIMULATOR | 20 | #ifndef SIMULATOR |
21 | 21 | ||
22 | #include <inttypes.h> | ||
22 | #include "codeclib.h" | 23 | #include "codeclib.h" |
23 | 24 | ||
24 | CODEC_HEADER | 25 | CODEC_ENC_HEADER |
26 | |||
27 | #ifdef USE_IRAM | ||
28 | extern char iramcopy[]; | ||
29 | extern char iramstart[]; | ||
30 | extern char iramend[]; | ||
31 | extern char iedata[]; | ||
32 | extern char iend[]; | ||
33 | #endif | ||
34 | |||
35 | struct riff_header | ||
36 | { | ||
37 | uint8_t riff_id[4]; /* 00h - "RIFF" */ | ||
38 | uint32_t riff_size; /* 04h - sz following headers + data_size */ | ||
39 | /* format header */ | ||
40 | uint8_t format[4]; /* 08h - "WAVE" */ | ||
41 | uint8_t format_id[4]; /* 0Ch - "fmt " */ | ||
42 | uint32_t format_size; /* 10h - 16 for PCM (sz format data) */ | ||
43 | /* format data */ | ||
44 | uint16_t audio_format; /* 14h - 1=PCM */ | ||
45 | uint16_t num_channels; /* 16h - 1=M, 2=S, etc. */ | ||
46 | uint32_t sample_rate; /* 18h - HZ */ | ||
47 | uint32_t byte_rate; /* 1Ch - num_channels*sample_rate*bits_per_sample/8 */ | ||
48 | uint16_t block_align; /* 20h - num_channels*bits_per_samples/8 */ | ||
49 | uint16_t bits_per_sample; /* 22h - 8=8 bits, 16=16 bits, etc. */ | ||
50 | /* Not for audio_format=1 (PCM) */ | ||
51 | /* unsigned short extra_param_size; 24h - size of extra data */ | ||
52 | /* unsigned char *extra_params; */ | ||
53 | /* data header */ | ||
54 | uint8_t data_id[4]; /* 24h - "data" */ | ||
55 | uint32_t data_size; /* 28h - num_samples*num_channels*bits_per_sample/8 */ | ||
56 | /* unsigned char *data; 2ch - actual sound data */ | ||
57 | }; | ||
58 | |||
59 | #define RIFF_FMT_HEADER_SIZE 12 /* format -> format_size */ | ||
60 | #define RIFF_FMT_DATA_SIZE 16 /* audio_format -> bits_per_sample */ | ||
61 | #define RIFF_DATA_HEADER_SIZE 8 /* data_id -> data_size */ | ||
62 | |||
63 | #define PCM_DEPTH_BYTES 2 | ||
64 | #define PCM_DEPTH_BITS 16 | ||
65 | #define PCM_SAMP_PER_CHUNK 2048 | ||
66 | #define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4) | ||
25 | 67 | ||
26 | static struct codec_api *ci; | 68 | static struct codec_api *ci; |
27 | static int enc_channels; | 69 | static int num_channels; |
70 | uint32_t sample_rate; | ||
71 | uint32_t enc_size; | ||
28 | 72 | ||
29 | #define CHUNK_SIZE 8192 | 73 | static const struct riff_header riff_header = |
74 | { | ||
75 | /* "RIFF" header */ | ||
76 | { 'R', 'I', 'F', 'F' }, /* riff_id */ | ||
77 | 0, /* riff_size (*) */ | ||
78 | /* format header */ | ||
79 | { 'W', 'A', 'V', 'E' }, /* format */ | ||
80 | { 'f', 'm', 't', ' ' }, /* format_id */ | ||
81 | H_TO_LE32(16), /* format_size */ | ||
82 | /* format data */ | ||
83 | H_TO_LE16(1), /* audio_format */ | ||
84 | 0, /* num_channels (*) */ | ||
85 | 0, /* sample_rate (*) */ | ||
86 | 0, /* byte_rate (*) */ | ||
87 | 0, /* block_align (*) */ | ||
88 | H_TO_LE16(PCM_DEPTH_BITS), /* bits_per_sample */ | ||
89 | /* data header */ | ||
90 | { 'd', 'a', 't', 'a' }, /* data_id */ | ||
91 | 0 /* data_size (*) */ | ||
92 | /* (*) updated during ENC_END_FILE event */ | ||
93 | }; | ||
30 | 94 | ||
31 | static unsigned char wav_header[44] = | 95 | /* called version often - inline */ |
32 | {'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16, | 96 | static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; |
33 | 0,0,0,1,0,2,0,0x44,0xac,0,0,0x10,0xb1,2,0,4,0,16,0,'d','a','t','a',0,0,0,0}; | 97 | static inline bool is_file_data_ok(struct enc_file_event_data *data) |
98 | { | ||
99 | return data->rec_file >= 0 && (long)data->chunk->flags >= 0; | ||
100 | } /* is_file_data_ok */ | ||
34 | 101 | ||
35 | static unsigned char wav_header_mono[44] = | 102 | /* called version often - inline */ |
36 | {'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',16, | 103 | static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; |
37 | 0,0,0,1,0,1,0,0x44,0xac,0,0,0x88,0x58,1,0,2,0,16,0,'d','a','t','a',0,0,0,0}; | 104 | static inline bool on_write_chunk(struct enc_file_event_data *data) |
105 | { | ||
106 | if (!is_file_data_ok(data)) | ||
107 | return false; | ||
38 | 108 | ||
39 | /* update file header info callback function (called by main application) */ | 109 | if (data->chunk->enc_data == NULL) |
40 | void enc_set_header(void *head_buffer, /* ptr to the file header data */ | 110 | { |
41 | int head_size, /* size of this header data */ | 111 | #ifdef ROCKBOX_HAS_LOGF |
42 | int num_pcm_samples, /* amount of processed pcm samples */ | 112 | ci->logf("wav enc: NULL data"); |
43 | bool is_file_header) | 113 | #endif |
114 | return true; | ||
115 | } | ||
116 | |||
117 | if (ci->write(data->rec_file, data->chunk->enc_data, | ||
118 | data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) | ||
119 | return false; | ||
120 | |||
121 | data->num_pcm_samples += data->chunk->num_pcm; | ||
122 | return true; | ||
123 | } /* on_write_chunk */ | ||
124 | |||
125 | static bool on_start_file(struct enc_file_event_data *data) | ||
44 | { | 126 | { |
45 | int num_file_bytes = num_pcm_samples * 2 * enc_channels; | 127 | if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') |
128 | return false; | ||
129 | |||
130 | data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC); | ||
131 | |||
132 | if (data->rec_file < 0) | ||
133 | return false; | ||
134 | |||
135 | /* reset sample count */ | ||
136 | data->num_pcm_samples = 0; | ||
46 | 137 | ||
47 | if(is_file_header) | 138 | /* write template header */ |
139 | if (ci->write(data->rec_file, &riff_header, sizeof (riff_header)) | ||
140 | != sizeof (riff_header)) | ||
48 | { | 141 | { |
49 | /* update file header before file closing */ | 142 | return false; |
50 | if((int)sizeof(wav_header) < head_size) | 143 | } |
144 | |||
145 | data->new_enc_size += sizeof (riff_header); | ||
146 | return true; | ||
147 | } /* on_start_file */ | ||
148 | |||
149 | static bool on_end_file(struct enc_file_event_data *data) | ||
150 | { | ||
151 | /* update template header */ | ||
152 | struct riff_header hdr; | ||
153 | uint32_t data_size; | ||
154 | |||
155 | if (!is_file_data_ok(data)) | ||
156 | return false; | ||
157 | |||
158 | if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || | ||
159 | ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) | ||
160 | { | ||
161 | return false; | ||
162 | } | ||
163 | |||
164 | data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; | ||
165 | |||
166 | /* "RIFF" header */ | ||
167 | hdr.riff_size = htole32(RIFF_FMT_HEADER_SIZE + RIFF_FMT_DATA_SIZE | ||
168 | + RIFF_DATA_HEADER_SIZE + data_size); | ||
169 | |||
170 | /* format data */ | ||
171 | hdr.num_channels = htole16(num_channels); | ||
172 | hdr.sample_rate = htole32(sample_rate); | ||
173 | hdr.byte_rate = htole32(sample_rate*num_channels* PCM_DEPTH_BYTES); | ||
174 | hdr.block_align = htole16(num_channels*PCM_DEPTH_BYTES); | ||
175 | |||
176 | /* data header */ | ||
177 | hdr.data_size = htole32(data_size); | ||
178 | |||
179 | if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || | ||
180 | ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) | ||
181 | { | ||
182 | return false; | ||
183 | } | ||
184 | |||
185 | ci->fsync(data->rec_file); | ||
186 | ci->close(data->rec_file); | ||
187 | data->rec_file = -1; | ||
188 | |||
189 | return true; | ||
190 | } /* on_end_file */ | ||
191 | |||
192 | static void enc_events_callback(enum enc_events event, void *data) ICODE_ATTR; | ||
193 | static void enc_events_callback(enum enc_events event, void *data) | ||
194 | { | ||
195 | if (event == ENC_WRITE_CHUNK) | ||
196 | { | ||
197 | if (on_write_chunk((struct enc_file_event_data *)data)) | ||
198 | return; | ||
199 | } | ||
200 | else if (event == ENC_START_FILE) | ||
201 | { | ||
202 | if (on_start_file((struct enc_file_event_data *)data)) | ||
203 | return; | ||
204 | } | ||
205 | else if (event == ENC_END_FILE) | ||
206 | { | ||
207 | if (on_end_file((struct enc_file_event_data *)data)) | ||
208 | return; | ||
209 | } | ||
210 | else | ||
211 | { | ||
212 | return; | ||
213 | } | ||
214 | |||
215 | ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; | ||
216 | } /* enc_events_callback */ | ||
217 | |||
218 | /* convert native pcm samples to wav format samples */ | ||
219 | static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; | ||
220 | static void chunk_to_wav_format(uint32_t *src, uint32_t *dst) | ||
221 | { | ||
222 | if (num_channels == 1) | ||
223 | { | ||
224 | /* On big endian: | ||
225 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| | ||
226 | * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => | ||
227 | * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM| | ||
228 | * | ||
229 | * On little endian: | ||
230 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| | ||
231 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => | ||
232 | * |mmmmmmmmMMMMMMMM|mmmmmmmmMMMMMMMM| | ||
233 | */ | ||
234 | uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; | ||
235 | |||
236 | inline void to_mono(uint32_t **src, uint32_t **dst) | ||
51 | { | 237 | { |
52 | /* update wave header size entries: special to WAV format */ | 238 | int32_t lr1, lr2; |
53 | *(long*)(head_buffer+ 4) = htole32(num_file_bytes + 36); | 239 | |
54 | *(long*)(head_buffer+40) = htole32(num_file_bytes); | 240 | lr1 = *(*src)++; |
241 | lr1 = ((int16_t)lr1 + (lr1 >> 16)) >> 1; | ||
242 | |||
243 | lr2 = *(*src)++; | ||
244 | lr2 = ((int16_t)lr2 + (lr2 >> 16)) >> 1; | ||
245 | *(*dst)++ = swap_odd_even_be32((lr1 << 16) | (uint16_t)lr2); | ||
246 | } /* to_mono */ | ||
247 | |||
248 | do | ||
249 | { | ||
250 | to_mono(&src, &dst); | ||
251 | to_mono(&src, &dst); | ||
252 | to_mono(&src, &dst); | ||
253 | to_mono(&src, &dst); | ||
254 | to_mono(&src, &dst); | ||
255 | to_mono(&src, &dst); | ||
256 | to_mono(&src, &dst); | ||
257 | to_mono(&src, &dst); | ||
55 | } | 258 | } |
259 | while (src < src_end); | ||
56 | } | 260 | } |
57 | } | 261 | else |
262 | { | ||
263 | #ifdef ROCKBOX_BIG_ENDIAN | ||
264 | /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => | ||
265 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| | ||
266 | */ | ||
267 | uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; | ||
58 | 268 | ||
59 | /* main codec entry point */ | 269 | do |
60 | enum codec_status codec_start(struct codec_api* api) | 270 | { |
271 | *dst++ = swap_odd_even32(*src++); | ||
272 | *dst++ = swap_odd_even32(*src++); | ||
273 | *dst++ = swap_odd_even32(*src++); | ||
274 | *dst++ = swap_odd_even32(*src++); | ||
275 | *dst++ = swap_odd_even32(*src++); | ||
276 | *dst++ = swap_odd_even32(*src++); | ||
277 | *dst++ = swap_odd_even32(*src++); | ||
278 | *dst++ = swap_odd_even32(*src++); | ||
279 | } | ||
280 | while (src < src_end); | ||
281 | #else | ||
282 | /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => | ||
283 | * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| | ||
284 | */ | ||
285 | ci->memcpy(dst, src, PCM_CHUNK_SIZE); | ||
286 | #endif | ||
287 | } | ||
288 | } /* chunk_to_wav_format */ | ||
289 | |||
290 | static bool init_encoder(void) | ||
61 | { | 291 | { |
62 | int i; | 292 | struct enc_inputs inputs; |
63 | long lr; | 293 | struct enc_parameters params; |
64 | unsigned long t; | ||
65 | unsigned long *src; | ||
66 | unsigned long *dst; | ||
67 | int chunk_size, num_chunks, samp_per_chunk; | ||
68 | int enc_buffer_size; | ||
69 | int enc_quality; | ||
70 | bool cpu_boosted = true; /* start boosted */ | ||
71 | 294 | ||
72 | ci = api; // copy to global api pointer | 295 | if (ci->enc_get_inputs == NULL || |
296 | ci->enc_set_parameters == NULL || | ||
297 | ci->enc_get_chunk == NULL || | ||
298 | ci->enc_finish_chunk == NULL || | ||
299 | ci->enc_pcm_buf_near_empty == NULL || | ||
300 | ci->enc_get_pcm_data == NULL ) | ||
301 | return false; | ||
73 | 302 | ||
74 | if(ci->enc_get_inputs == NULL || | 303 | ci->enc_get_inputs(&inputs); |
75 | ci->enc_set_parameters == NULL || | ||
76 | ci->enc_alloc_chunk == NULL || | ||
77 | ci->enc_free_chunk == NULL || | ||
78 | ci->enc_wavbuf_near_empty == NULL || | ||
79 | ci->enc_get_wav_data == NULL || | ||
80 | ci->enc_set_header_callback == NULL ) | ||
81 | return CODEC_ERROR; | ||
82 | 304 | ||
83 | ci->cpu_boost(true); | 305 | if (inputs.config->afmt != AFMT_PCM_WAV) |
306 | return false; | ||
84 | 307 | ||
85 | *ci->enc_set_header_callback = enc_set_header; | 308 | sample_rate = inputs.sample_rate; |
86 | ci->enc_get_inputs(&enc_buffer_size, &enc_channels, &enc_quality); | 309 | num_channels = inputs.num_channels; |
87 | 310 | ||
88 | /* configure the buffer system */ | 311 | /* configure the buffer system */ |
89 | chunk_size = sizeof(long) + CHUNK_SIZE * enc_channels / 2; | 312 | params.afmt = AFMT_PCM_WAV; |
90 | num_chunks = enc_buffer_size / chunk_size; | 313 | enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2; |
91 | samp_per_chunk = CHUNK_SIZE / 4; | 314 | params.chunk_size = enc_size; |
315 | params.enc_sample_rate = sample_rate; | ||
316 | params.reserve_bytes = 0; | ||
317 | params.events_callback = enc_events_callback; | ||
318 | ci->enc_set_parameters(¶ms); | ||
319 | |||
320 | return true; | ||
321 | } /* init_encoder */ | ||
322 | |||
323 | /* main codec entry point */ | ||
324 | enum codec_status codec_start(struct codec_api* api) | ||
325 | { | ||
326 | bool cpu_boosted; | ||
92 | 327 | ||
93 | /* inform the main program about buffer dimensions and other params */ | 328 | ci = api; // copy to global api pointer |
94 | ci->enc_set_parameters(chunk_size, num_chunks, samp_per_chunk, | 329 | |
95 | (enc_channels == 2) ? wav_header : wav_header_mono, | 330 | #ifdef USE_IRAM |
96 | sizeof(wav_header), AFMT_PCM_WAV); | 331 | ci->memcpy(iramstart, iramcopy, iramend - iramstart); |
332 | ci->memset(iedata, 0, iend - iedata); | ||
333 | #endif | ||
334 | |||
335 | if (!init_encoder()) | ||
336 | { | ||
337 | ci->enc_codec_loaded = -1; | ||
338 | return CODEC_ERROR; | ||
339 | } | ||
97 | 340 | ||
98 | /* main application waits for this flag during encoder loading */ | 341 | /* main application waits for this flag during encoder loading */ |
99 | ci->enc_codec_loaded = true; | 342 | ci->enc_codec_loaded = 1; |
343 | |||
344 | ci->cpu_boost(true); | ||
345 | cpu_boosted = true; | ||
100 | 346 | ||
101 | /* main encoding loop */ | 347 | /* main encoding loop */ |
102 | while(!ci->stop_codec) | 348 | while(!ci->stop_codec) |
103 | { | 349 | { |
104 | while((src = (unsigned long*)ci->enc_get_wav_data(CHUNK_SIZE)) != NULL) | 350 | uint32_t *src; |
351 | |||
352 | while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL) | ||
105 | { | 353 | { |
106 | if(ci->stop_codec) | 354 | struct enc_chunk_hdr *chunk; |
355 | |||
356 | if (ci->stop_codec) | ||
107 | break; | 357 | break; |
108 | 358 | ||
109 | if(ci->enc_wavbuf_near_empty() == 0) | 359 | if (!cpu_boosted && ci->enc_pcm_buf_near_empty() == 0) |
110 | { | 360 | { |
111 | if(!cpu_boosted) | 361 | ci->cpu_boost(true); |
112 | { | 362 | cpu_boosted = true; |
113 | ci->cpu_boost(true); | ||
114 | cpu_boosted = true; | ||
115 | } | ||
116 | } | 363 | } |
117 | 364 | ||
118 | dst = (unsigned long*)ci->enc_alloc_chunk(); | 365 | chunk = ci->enc_get_chunk(); |
119 | *dst++ = CHUNK_SIZE * enc_channels / 2; /* set size info */ | 366 | chunk->enc_size = enc_size; |
367 | chunk->num_pcm = PCM_SAMP_PER_CHUNK; | ||
368 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); | ||
120 | 369 | ||
121 | if(enc_channels == 2) | 370 | chunk_to_wav_format(src, (uint32_t *)chunk->enc_data); |
122 | { | ||
123 | /* swap byte order & copy to destination */ | ||
124 | for (i=0; i<CHUNK_SIZE/4; i++) | ||
125 | { | ||
126 | t = *src++; | ||
127 | *dst++ = ((t >> 8) & 0xff00ff) | ((t << 8) & 0xff00ff00); | ||
128 | } | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | /* mix left/right, swap byte order & copy to destination */ | ||
133 | for (i=0; i<CHUNK_SIZE/8; i++) | ||
134 | { | ||
135 | lr = (long)*src++; | ||
136 | lr = (((lr<<16)>>16) + (lr>>16)) >> 1; /* left+right */ | ||
137 | t = (lr << 16); | ||
138 | lr = (long)*src++; | ||
139 | lr = (((lr<<16)>>16) + (lr>>16)) >> 1; /* left+right */ | ||
140 | t |= lr & 0xffff; | ||
141 | *dst++ = ((t >> 8) & 0xff00ff) | ((t << 8) & 0xff00ff00); | ||
142 | } | ||
143 | } | ||
144 | 371 | ||
145 | ci->enc_free_chunk(); | 372 | ci->enc_finish_chunk(); |
146 | ci->yield(); | 373 | ci->yield(); |
147 | } | 374 | } |
148 | 375 | ||
149 | if(ci->enc_wavbuf_near_empty()) | 376 | if (cpu_boosted && ci->enc_pcm_buf_near_empty() != 0) |
150 | { | 377 | { |
151 | if(cpu_boosted) | 378 | ci->cpu_boost(false); |
152 | { | 379 | cpu_boosted = false; |
153 | ci->cpu_boost(false); | ||
154 | cpu_boosted = false; | ||
155 | } | ||
156 | } | 380 | } |
157 | 381 | ||
158 | ci->yield(); | 382 | ci->yield(); |
@@ -162,11 +386,12 @@ enum codec_status codec_start(struct codec_api* api) | |||
162 | ci->cpu_boost(false); | 386 | ci->cpu_boost(false); |
163 | 387 | ||
164 | /* reset parameters to initial state */ | 388 | /* reset parameters to initial state */ |
165 | ci->enc_set_parameters(0, 0, 0, 0, 0, 0); | 389 | ci->enc_set_parameters(NULL); |
166 | 390 | ||
167 | /* main application waits for this flag during encoder removing */ | 391 | /* main application waits for this flag during encoder removing */ |
168 | ci->enc_codec_loaded = false; | 392 | ci->enc_codec_loaded = 0; |
169 | 393 | ||
170 | return CODEC_OK; | 394 | return CODEC_OK; |
171 | } | 395 | } /* codec_start */ |
172 | #endif | 396 | |
397 | #endif /* ndef SIMULATOR */ | ||