Hopefully fix red now and reduce binsize for HWCODEC targets. This change implements...
[kugel-rb.git] / apps / mp3data.c
blob31ed492679738ef99994daf6bff90c184ec8aedd
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Daniel Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 * Parts of this code has been stolen from the Ample project and was written
24 * by David Härdeman. It has since been extended and enhanced pretty much by
25 * all sorts of friendly Rockbox people.
27 * A nice reference for MPEG header info:
28 * http://rockbox.haxx.se/docs/mpeghdr.html
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdbool.h>
36 #include <limits.h>
37 #include "debug.h"
38 #include "logf.h"
39 #include "mp3data.h"
40 #include "file.h"
41 #include "buffer.h"
42 #include "metadata/metadata_common.h"
44 // #define DEBUG_VERBOSE
46 #ifdef DEBUG_VERBOSE
47 #define VDEBUGF DEBUGF
48 #else
49 #define VDEBUGF(...) do { } while(0)
50 #endif
52 #define SYNC_MASK (0x7ffL << 21)
53 #define VERSION_MASK (3L << 19)
54 #define LAYER_MASK (3L << 17)
55 #define PROTECTION_MASK (1L << 16)
56 #define BITRATE_MASK (0xfL << 12)
57 #define SAMPLERATE_MASK (3L << 10)
58 #define PADDING_MASK (1L << 9)
59 #define PRIVATE_MASK (1L << 8)
60 #define CHANNELMODE_MASK (3L << 6)
61 #define MODE_EXT_MASK (3L << 4)
62 #define COPYRIGHT_MASK (1L << 3)
63 #define ORIGINAL_MASK (1L << 2)
64 #define EMPHASIS_MASK 3L
66 /* MPEG Version table, sorted by version index */
67 static const signed char version_table[4] = {
68 MPEG_VERSION2_5, -1, MPEG_VERSION2, MPEG_VERSION1
71 /* Bitrate table for mpeg audio, indexed by row index and birate index */
72 static const short bitrates[5][16] = {
73 {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, /* V1 L1 */
74 {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0}, /* V1 L2 */
75 {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0}, /* V1 L3 */
76 {0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,0}, /* V2 L1 */
77 {0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0} /* V2 L2+L3 */
80 /* Bitrate pointer table, indexed by version and layer */
81 static const short *bitrate_table[3][3] =
83 {bitrates[0], bitrates[1], bitrates[2]},
84 {bitrates[3], bitrates[4], bitrates[4]},
85 {bitrates[3], bitrates[4], bitrates[4]}
88 /* Sampling frequency table, indexed by version and frequency index */
89 static const unsigned short freq_table[3][3] =
91 {44100, 48000, 32000}, /* MPEG Version 1 */
92 {22050, 24000, 16000}, /* MPEG version 2 */
93 {11025, 12000, 8000}, /* MPEG version 2.5 */
96 unsigned long bytes2int(unsigned long b0, unsigned long b1,
97 unsigned long b2, unsigned long b3)
99 return (b0 & 0xFF) << (3*8) |
100 (b1 & 0xFF) << (2*8) |
101 (b2 & 0xFF) << (1*8) |
102 (b3 & 0xFF) << (0*8);
105 /* check if 'head' is a valid mp3 frame header */
106 static bool is_mp3frameheader(unsigned long head)
108 if ((head & SYNC_MASK) != (unsigned long)SYNC_MASK) /* bad sync? */
109 return false;
110 if ((head & VERSION_MASK) == (1L << 19)) /* bad version? */
111 return false;
112 if (!(head & LAYER_MASK)) /* no layer? */
113 return false;
114 #if CONFIG_CODEC != SWCODEC
115 /* The MAS can't decode layer 1, so treat layer 1 data as invalid */
116 if ((head & LAYER_MASK) == LAYER_MASK)
117 return false;
118 #endif
119 if ((head & BITRATE_MASK) == BITRATE_MASK) /* bad bitrate? */
120 return false;
121 if (!(head & BITRATE_MASK)) /* no bitrate? */
122 return false;
123 if ((head & SAMPLERATE_MASK) == SAMPLERATE_MASK) /* bad sample rate? */
124 return false;
126 return true;
129 static bool mp3headerinfo(struct mp3info *info, unsigned long header)
131 int bitindex, freqindex;
133 /* MPEG Audio Version */
134 if ((header & VERSION_MASK) >> 19 >= sizeof(version_table))
135 return false;
137 info->version = version_table[(header & VERSION_MASK) >> 19];
138 if (info->version < 0)
139 return false;
141 /* Layer */
142 info->layer = 3 - ((header & LAYER_MASK) >> 17);
143 if (info->layer == 3)
144 return false;
146 /* Rockbox: not used
147 info->protection = (header & PROTECTION_MASK) ? true : false;
150 /* Bitrate */
151 bitindex = (header & BITRATE_MASK) >> 12;
152 info->bitrate = bitrate_table[info->version][info->layer][bitindex];
153 if(info->bitrate == 0)
154 return false;
156 /* Sampling frequency */
157 freqindex = (header & SAMPLERATE_MASK) >> 10;
158 if (freqindex == 3)
159 return false;
160 info->frequency = freq_table[info->version][freqindex];
162 info->padding = (header & PADDING_MASK) ? 1 : 0;
164 /* Calculate number of bytes, calculation depends on layer */
165 if (info->layer == 0) {
166 info->frame_samples = 384;
167 info->frame_size = (12000 * info->bitrate / info->frequency
168 + info->padding) * 4;
170 else {
171 if ((info->version > MPEG_VERSION1) && (info->layer == 2))
172 info->frame_samples = 576;
173 else
174 info->frame_samples = 1152;
175 info->frame_size = (1000/8) * info->frame_samples * info->bitrate
176 / info->frequency + info->padding;
179 /* Frametime fraction denominator */
180 if (freqindex != 0) { /* 48/32/24/16/12/8 kHz */
181 info->ft_den = 1; /* integer number of milliseconds */
183 else { /* 44.1/22.05/11.025 kHz */
184 if (info->layer == 0) /* layer 1 */
185 info->ft_den = 147;
186 else /* layer 2+3 */
187 info->ft_den = 49;
189 /* Frametime fraction numerator */
190 info->ft_num = 1000 * info->ft_den * info->frame_samples / info->frequency;
192 info->channel_mode = (header & CHANNELMODE_MASK) >> 6;
193 /* Rockbox: not used
194 info->mode_extension = (header & MODE_EXT_MASK) >> 4;
195 info->emphasis = header & EMPHASIS_MASK;
197 VDEBUGF( "Header: %08lx, Ver %d, lay %d, bitr %d, freq %ld, "
198 "chmode %d, bytes: %d time: %d/%d\n",
199 header, info->version, info->layer+1, info->bitrate,
200 info->frequency, info->channel_mode,
201 info->frame_size, info->ft_num, info->ft_den);
202 return true;
205 static bool headers_have_same_type(unsigned long header1,
206 unsigned long header2)
208 /* Compare MPEG version, layer and sampling frequency. If header1 is zero
209 * it is assumed the headers are of same type. */
210 unsigned int mask = 0xfffe0c00;
211 header1 &= mask;
212 header2 &= mask;
213 return header1 ? (header1 == header2) : true;
216 /* Helper function to read 4-byte in big endian format. */
217 static void read_uint32be_mp3data(int fd, unsigned long *data, long *pos)
219 #ifdef ROCKBOX_BIG_ENDIAN
220 (void)read(fd, (char*)data, 4);
221 #else
222 char tmp[4];
223 (void)read(fd, tmp, 4);
224 *data = (tmp[0]<<24) | (tmp[1]<<16) | (tmp[2]<<8) | tmp[3];
225 #endif
226 *pos += 4;
229 static unsigned long __find_next_frame(int fd, long *offset, long max_offset,
230 unsigned long reference_header,
231 int(*getfunc)(int fd, unsigned char *c),
232 bool single_header)
234 unsigned long header=0;
235 unsigned char tmp;
236 long pos = 0;
238 /* We will search until we find two consecutive MPEG frame headers with
239 * the same MPEG version, layer and sampling frequency. The first header
240 * of this pair is assumed to be the first valid MPEG frame header of the
241 * whole stream. */
242 do {
243 /* Read 1 new byte. */
244 header <<= 8;
245 if (!getfunc(fd, &tmp))
246 return 0;
247 header |= tmp;
248 pos++;
250 /* Abort if max_offset is reached. Stop parsing. */
251 if (max_offset > 0 && pos > max_offset)
252 return 0;
254 if (is_mp3frameheader(header)) {
255 if (single_header) {
256 /* We search for one _single_ valid header that has the same
257 * type as the reference_header (if reference_header != 0).
258 * In this case we are finished. */
259 if (headers_have_same_type(reference_header, header))
260 break;
261 } else {
262 /* The current header is valid. Now gather the frame size,
263 * seek to this byte position and check if there is another
264 * valid MPEG frame header of the same type. */
265 struct mp3info info;
267 /* Gather frame size from given header and seek to next
268 * frame header. */
269 mp3headerinfo(&info, header);
270 lseek(fd, info.frame_size-4, SEEK_CUR);
272 /* Read possible next frame header and seek back to last frame
273 * headers byte position. */
274 reference_header = 0;
275 read_uint32be_mp3data(fd, &reference_header, &pos);
277 lseek(fd, -info.frame_size, SEEK_CUR);
279 /* If the current header is of the same type as the previous
280 * header we are finished. */
281 if (headers_have_same_type(header, reference_header))
282 break;
286 } while (true);
288 *offset = pos - 4;
290 if(*offset)
291 VDEBUGF("Warning: skipping %ld bytes of garbage\n", *offset);
293 return header;
296 static int fileread(int fd, unsigned char *c)
298 return read(fd, c, 1);
301 unsigned long find_next_frame(int fd,
302 long *offset,
303 long max_offset,
304 unsigned long reference_header)
306 return __find_next_frame(fd, offset, max_offset, reference_header,
307 fileread, true);
310 #ifndef __PCTOOL__
311 static int fnf_read_index;
312 static int fnf_buf_len;
314 static int buf_getbyte(int fd, unsigned char *c)
316 if(fnf_read_index < fnf_buf_len)
318 *c = audiobuf[fnf_read_index++];
319 return 1;
321 else
323 fnf_buf_len = read(fd, audiobuf, audiobufend - audiobuf);
324 if(fnf_buf_len < 0)
325 return -1;
327 fnf_read_index = 0;
329 if(fnf_buf_len > 0)
331 *c = audiobuf[fnf_read_index++];
332 return 1;
334 else
335 return 0;
337 return 0;
340 static int buf_seek(int fd, int len)
342 fnf_read_index += len;
343 if(fnf_read_index > fnf_buf_len)
345 len = fnf_read_index - fnf_buf_len;
347 fnf_buf_len = read(fd, audiobuf, audiobufend - audiobuf);
348 if(fnf_buf_len < 0)
349 return -1;
351 fnf_read_index = 0;
352 fnf_read_index += len;
355 if(fnf_read_index > fnf_buf_len)
357 return -1;
359 else
360 return 0;
363 static void buf_init(void)
365 fnf_buf_len = 0;
366 fnf_read_index = 0;
369 static unsigned long buf_find_next_frame(int fd, long *offset, long max_offset)
371 return __find_next_frame(fd, offset, max_offset, 0, buf_getbyte, true);
374 static int audiobuflen;
375 static int mem_pos;
376 static int mem_cnt;
377 static int mem_maxlen;
379 static int mem_getbyte(int dummy, unsigned char *c)
381 dummy = dummy;
383 *c = audiobuf[mem_pos++];
384 if(mem_pos >= audiobuflen)
385 mem_pos = 0;
387 if(mem_cnt++ >= mem_maxlen)
388 return 0;
389 else
390 return 1;
393 unsigned long mem_find_next_frame(int startpos,
394 long *offset,
395 long max_offset,
396 unsigned long reference_header)
398 audiobuflen = audiobufend - audiobuf;
399 mem_pos = startpos;
400 mem_cnt = 0;
401 mem_maxlen = max_offset;
403 return __find_next_frame(0, offset, max_offset, reference_header,
404 mem_getbyte, true);
406 #endif
408 /* Extract information from a 'Xing' or 'Info' header. */
409 static void get_xing_info(struct mp3info *info, unsigned char *buf)
411 int i = 8;
413 /* Is it a VBR file? */
414 info->is_vbr = !memcmp(buf, "Xing", 4);
416 if (buf[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */
418 info->frame_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]);
419 if (info->frame_count <= ULONG_MAX / info->ft_num)
420 info->file_time = info->frame_count * info->ft_num / info->ft_den;
421 else
422 info->file_time = info->frame_count / info->ft_den * info->ft_num;
423 i += 4;
426 if (buf[7] & VBR_BYTES_FLAG) /* Is byte count there? */
428 info->byte_count = bytes2int(buf[i], buf[i+1], buf[i+2], buf[i+3]);
429 i += 4;
432 if (info->file_time && info->byte_count)
434 if (info->byte_count <= (ULONG_MAX/8))
435 info->bitrate = info->byte_count * 8 / info->file_time;
436 else
437 info->bitrate = info->byte_count / (info->file_time >> 3);
440 if (buf[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */
442 info->has_toc = true;
443 memcpy( info->toc, buf+i, 100 );
444 i += 100;
446 if (buf[7] & VBR_QUALITY_FLAG)
448 /* We don't care about this, but need to skip it */
449 i += 4;
451 #if CONFIG_CODEC==SWCODEC
452 i += 21;
453 info->enc_delay = ((int)buf[i ] << 4) | (buf[i+1] >> 4);
454 info->enc_padding = ((int)buf[i+1] << 8) | buf[i+2];
455 /* TODO: This sanity checking is rather silly, seeing as how the LAME
456 header contains a CRC field that can be used to verify integrity. */
457 if (!(info->enc_delay >= 0 && info->enc_delay <= 2880 &&
458 info->enc_padding >= 0 && info->enc_padding <= 2*1152))
460 /* Invalid data */
461 info->enc_delay = -1;
462 info->enc_padding = -1;
464 #endif
467 /* Extract information from a 'VBRI' header. */
468 static void get_vbri_info(struct mp3info *info, unsigned char *buf)
470 int i, num_offsets, offset = 0;
472 info->is_vbr = true; /* Yes, it is a FhG VBR file */
473 info->has_toc = false; /* We don't parse the TOC (yet) */
475 info->byte_count = bytes2int(buf[10], buf[11], buf[12], buf[13]);
476 info->frame_count = bytes2int(buf[14], buf[15], buf[16], buf[17]);
477 if (info->frame_count <= ULONG_MAX / info->ft_num)
478 info->file_time = info->frame_count * info->ft_num / info->ft_den;
479 else
480 info->file_time = info->frame_count / info->ft_den * info->ft_num;
482 if (info->byte_count <= (ULONG_MAX/8))
483 info->bitrate = info->byte_count * 8 / info->file_time;
484 else
485 info->bitrate = info->byte_count / (info->file_time >> 3);
487 /* We don't parse the TOC, since we don't yet know how to (FIXME) */
488 num_offsets = bytes2int(0, 0, buf[18], buf[19]);
489 VDEBUGF("Frame size (%dkpbs): %d bytes (0x%x)\n",
490 info->bitrate, info->frame_size, info->frame_size);
491 VDEBUGF("Frame count: %lx\n", info->frame_count);
492 VDEBUGF("Byte count: %lx\n", info->byte_count);
493 VDEBUGF("Offsets: %d\n", num_offsets);
494 VDEBUGF("Frames/entry: %ld\n",
495 bytes2int(0, 0, buf[24], buf[25]));
497 for(i = 0; i < num_offsets; i++)
499 offset += bytes2int(0, 0, buf[26+i*2], buf[27+i*2]);;
500 VDEBUGF("%03d: %lx\n", i, offset - bytecount,);
504 /* Seek to next mpeg header and extract relevant information. */
505 static int get_next_header_info(int fd, long *bytecount, struct mp3info *info,
506 bool single_header)
508 long tmp;
509 unsigned long header = 0;
511 header = __find_next_frame(fd, &tmp, 0x20000, 0, fileread, single_header);
512 if(header == 0)
513 return -1;
515 if(!mp3headerinfo(info, header))
516 return -2;
518 /* Next frame header is tmp bytes away. */
519 *bytecount += tmp;
521 return 0;
524 int get_mp3file_info(int fd, struct mp3info *info)
526 unsigned char frame[1800], *vbrheader;
527 long bytecount = 0;
528 int result;
530 /* Initialize info and frame */
531 memset(info, 0, sizeof(struct mp3info));
532 memset(frame, 0, sizeof(frame));
534 #if CONFIG_CODEC==SWCODEC
535 /* These two are needed for proper LAME gapless MP3 playback */
536 info->enc_delay = -1;
537 info->enc_padding = -1;
538 #endif
540 /* Get the very first single MPEG frame. */
541 result = get_next_header_info(fd, &bytecount, info, true);
542 if(result)
543 return result;
545 /* OK, we have found a frame. Let's see if it has a Xing header */
546 if (info->frame_size-4 >= (int)sizeof(frame))
548 DEBUGF("Error: Invalid id3 header, frame_size: %d\n", info->frame_size);
549 return -8;
552 if(read(fd, frame, info->frame_size-4) < 0)
553 return -3;
555 /* Calculate position of a possible VBR header */
556 if (info->version == MPEG_VERSION1) {
557 if (info->channel_mode == 3) /* mono */
558 vbrheader = frame + 17;
559 else
560 vbrheader = frame + 32;
561 } else {
562 if (info->channel_mode == 3) /* mono */
563 vbrheader = frame + 9;
564 else
565 vbrheader = frame + 17;
568 if (!memcmp(vbrheader, "Xing", 4) || !memcmp(vbrheader, "Info", 4))
570 VDEBUGF("-- XING header --\n");
572 /* We want to skip the Xing frame when playing the stream */
573 bytecount += info->frame_size;
575 /* Now get the next frame to read the real info about the mp3 stream */
576 result = get_next_header_info(fd, &bytecount, info, false);
577 if(result)
578 return result;
580 get_xing_info(info, vbrheader);
582 else if (!memcmp(vbrheader, "VBRI", 4))
584 VDEBUGF("-- VBRI header --\n");
586 /* We want to skip the VBRI frame when playing the stream */
587 bytecount += info->frame_size;
589 /* Now get the next frame to read the real info about the mp3 stream */
590 result = get_next_header_info(fd, &bytecount, info, false);
591 if(result)
592 return result;
594 get_vbri_info(info, vbrheader);
596 else
598 VDEBUGF("-- No VBR header --\n");
600 /* There was no VBR header found. So, we seek back to beginning and
601 * search for the first MPEG frame header of the mp3 stream. */
602 lseek(fd, -info->frame_size, SEEK_CUR);
603 result = get_next_header_info(fd, &bytecount, info, false);
604 if(result)
605 return result;
608 return bytecount;
611 #ifndef __PCTOOL__
612 static void long2bytes(unsigned char *buf, long val)
614 buf[0] = (val >> 24) & 0xff;
615 buf[1] = (val >> 16) & 0xff;
616 buf[2] = (val >> 8) & 0xff;
617 buf[3] = val & 0xff;
620 int count_mp3_frames(int fd, int startpos, int filesize,
621 void (*progressfunc)(int))
623 unsigned long header = 0;
624 struct mp3info info;
625 int num_frames;
626 long bytes;
627 int cnt;
628 long progress_chunk = filesize / 50; /* Max is 50%, in 1% increments */
629 int progress_cnt = 0;
630 bool is_vbr = false;
631 int last_bitrate = 0;
632 int header_template = 0;
634 if(lseek(fd, startpos, SEEK_SET) < 0)
635 return -1;
637 buf_init();
639 /* Find out the total number of frames */
640 num_frames = 0;
641 cnt = 0;
643 while((header = buf_find_next_frame(fd, &bytes, header_template))) {
644 mp3headerinfo(&info, header);
646 if(!header_template)
647 header_template = header;
649 /* See if this really is a VBR file */
650 if(last_bitrate && info.bitrate != last_bitrate)
652 is_vbr = true;
654 last_bitrate = info.bitrate;
656 buf_seek(fd, info.frame_size-4);
657 num_frames++;
658 if(progressfunc)
660 cnt += bytes + info.frame_size;
661 if(cnt > progress_chunk)
663 progress_cnt++;
664 progressfunc(progress_cnt);
665 cnt = 0;
669 VDEBUGF("Total number of frames: %d\n", num_frames);
671 if(is_vbr)
672 return num_frames;
673 else
675 DEBUGF("Not a VBR file\n");
676 return 0;
680 static const char cooltext[] = "Rockbox - rocks your box";
682 /* buf needs to be the audio buffer with TOC generation enabled,
683 and at least MAX_XING_HEADER_SIZE bytes otherwise */
684 int create_xing_header(int fd, long startpos, long filesize,
685 unsigned char *buf, unsigned long num_frames,
686 unsigned long rec_time, unsigned long header_template,
687 void (*progressfunc)(int), bool generate_toc)
689 struct mp3info info;
690 unsigned char toc[100];
691 unsigned long header = 0;
692 unsigned long xing_header_template = header_template;
693 unsigned long filepos;
694 long pos, last_pos;
695 long j;
696 long bytes;
697 int i;
698 int index;
700 DEBUGF("create_xing_header()\n");
702 if(generate_toc)
704 lseek(fd, startpos, SEEK_SET);
705 buf_init();
707 /* Generate filepos table */
708 last_pos = 0;
709 filepos = 0;
710 header = 0;
711 for(i = 0;i < 100;i++) {
712 /* Calculate the absolute frame number for this seek point */
713 pos = i * num_frames / 100;
715 /* Advance from the last seek point to this one */
716 for(j = 0;j < pos - last_pos;j++)
718 header = buf_find_next_frame(fd, &bytes, header_template);
719 filepos += bytes;
720 mp3headerinfo(&info, header);
721 buf_seek(fd, info.frame_size-4);
722 filepos += info.frame_size;
724 if(!header_template)
725 header_template = header;
728 /* Save a header for later use if header_template is empty.
729 We only save one header, and we want to save one in the
730 middle of the stream, just in case the first and the last
731 headers are corrupt. */
732 if(!xing_header_template && i == 1)
733 xing_header_template = header;
735 if(progressfunc)
737 progressfunc(50 + i/2);
740 /* Fill in the TOC entry */
741 /* each toc is a single byte indicating how many 256ths of the
742 * way through the file, is that percent of the way through the
743 * song. the easy method, filepos*256/filesize, chokes when
744 * the upper 8 bits of the file position are nonzero
745 * (i.e. files over 16mb in size).
747 if (filepos > (ULONG_MAX/256))
749 /* instead of multiplying filepos by 256, we divide
750 * filesize by 256.
752 toc[i] = filepos / (filesize >> 8);
754 else
756 toc[i] = filepos * 256 / filesize;
759 VDEBUGF("Pos %d: %ld relpos: %ld filepos: %lx tocentry: %x\n",
760 i, pos, pos-last_pos, filepos, toc[i]);
762 last_pos = pos;
766 /* Use the template header and create a new one.
767 We ignore the Protection bit even if the rest of the stream is
768 protected. */
769 header = xing_header_template & ~(BITRATE_MASK|PROTECTION_MASK|PADDING_MASK);
770 header |= 8 << 12; /* This gives us plenty of space, 192..576 bytes */
772 if (!mp3headerinfo(&info, header))
773 return 0; /* invalid header */
775 if (num_frames == 0 && rec_time) {
776 /* estimate the number of frames based on the recording time */
777 if (rec_time <= ULONG_MAX / info.ft_den)
778 num_frames = rec_time * info.ft_den / info.ft_num;
779 else
780 num_frames = rec_time / info.ft_num * info.ft_den;
783 /* Clear the frame */
784 memset(buf, 0, MAX_XING_HEADER_SIZE);
786 /* Write the header to the buffer */
787 long2bytes(buf, header);
789 /* Calculate position of VBR header */
790 if (info.version == MPEG_VERSION1) {
791 if (info.channel_mode == 3) /* mono */
792 index = 21;
793 else
794 index = 36;
796 else {
797 if (info.channel_mode == 3) /* mono */
798 index = 13;
799 else
800 index = 21;
803 /* Create the Xing data */
804 memcpy(&buf[index], "Xing", 4);
805 long2bytes(&buf[index+4], (num_frames ? VBR_FRAMES_FLAG : 0)
806 | (filesize ? VBR_BYTES_FLAG : 0)
807 | (generate_toc ? VBR_TOC_FLAG : 0));
808 index += 8;
809 if(num_frames)
811 long2bytes(&buf[index], num_frames);
812 index += 4;
815 if(filesize)
817 long2bytes(&buf[index], filesize - startpos);
818 index += 4;
821 /* Copy the TOC */
822 memcpy(buf + index, toc, 100);
824 /* And some extra cool info */
825 memcpy(buf + index + 100, cooltext, sizeof(cooltext));
827 #ifdef DEBUG
828 for(i = 0;i < info.frame_size;i++)
830 if(i && !(i % 16))
831 DEBUGF("\n");
833 DEBUGF("%02x ", buf[i]);
835 #endif
837 return info.frame_size;
840 #endif