Simplify tab completion code by removing nr_tails variable
[cmus.git] / nomad.c
blob57fea61477b1de48321102d21f2611eb9734650a
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
21 * Xing code copied from xmms-mad plugin.
25 #include "nomad.h"
26 #include "id3.h"
27 #include "xmalloc.h"
28 #include "debug.h"
30 #include <mad.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
35 struct seek_idx_entry {
36 off_t offset;
37 mad_timer_t timer;
40 struct nomad {
41 struct mad_stream stream;
42 struct mad_frame frame;
43 struct mad_synth synth;
44 struct mad_header header;
45 mad_timer_t timer;
46 unsigned long cur_frame;
47 off_t input_offset;
48 unsigned char input_buffer[INPUT_BUFFER_SIZE];
49 int i;
50 unsigned int fast : 1;
51 unsigned int has_xing : 1;
53 struct {
54 unsigned int flags;
55 unsigned int nr_frames;
56 unsigned int bytes;
57 unsigned int scale;
58 unsigned char toc[100];
59 } xing;
61 struct {
62 int size;
63 struct seek_idx_entry *table;
64 } seek_idx;
66 struct nomad_info info;
67 void *datasource;
68 int datasource_fd;
69 struct nomad_callbacks cbs;
72 /* ------------------------------------------------------------------------- */
74 static ssize_t default_read(void *datasource, void *buffer, size_t count)
76 int fd = *(int *)datasource;
78 return read(fd, buffer, count);
81 static off_t default_lseek(void *datasource, off_t offset, int whence)
83 int fd = *(int *)datasource;
85 return lseek(fd, offset, whence);
88 static int default_close(void *datasource)
90 int fd = *(int *)datasource;
92 return close(fd);
95 /* ------------------------------------------------------------------------- */
97 static inline int scale(mad_fixed_t sample)
99 sample += 1L << (MAD_F_FRACBITS - 16);
100 if (sample >= MAD_F_ONE) {
101 sample = MAD_F_ONE - 1;
102 } else if (sample < -MAD_F_ONE) {
103 sample = -MAD_F_ONE;
105 return sample >> (MAD_F_FRACBITS - 15);
108 static inline double timer_to_seconds(mad_timer_t timer)
110 signed long ms;
112 ms = mad_timer_count(timer, MAD_UNITS_MILLISECONDS);
113 return (double)ms / 1000.0;
116 enum {
117 XING_FRAMES = 0x00000001L,
118 XING_BYTES = 0x00000002L,
119 XING_TOC = 0x00000004L,
120 XING_SCALE = 0x00000008L
124 * format:
126 * 4 "Xing"
127 * 4 flags
128 * 4 frames (optional)
129 * 4 bytes (optional)
130 * 100 TOC (optional)
131 * 4 scale (optional)
133 static int xing_parse(struct nomad *nomad)
135 struct mad_bitptr ptr = nomad->stream.anc_ptr;
136 int bitlen = nomad->stream.anc_bitlen;
138 nomad->has_xing = 0;
139 if (bitlen < 64)
140 return -1;
141 if (mad_bit_read(&ptr, 32) != (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g'))
142 return -1;
143 nomad->xing.flags = mad_bit_read(&ptr, 32);
144 bitlen -= 64;
145 if (nomad->xing.flags & XING_FRAMES) {
146 if (bitlen < 32)
147 return -1;
148 nomad->xing.nr_frames = mad_bit_read(&ptr, 32);
149 bitlen -= 32;
151 if (nomad->xing.flags & XING_BYTES) {
152 if (bitlen < 32)
153 return -1;
154 nomad->xing.bytes = mad_bit_read(&ptr, 32);
155 bitlen -= 32;
157 if (nomad->xing.flags & XING_TOC) {
158 int i;
160 if (bitlen < 800)
161 return -1;
162 for (i = 0; i < 100; i++)
163 nomad->xing.toc[i] = mad_bit_read(&ptr, 8);
164 bitlen -= 800;
166 if (nomad->xing.flags & XING_SCALE) {
167 if (bitlen < 32)
168 return -1;
169 nomad->xing.scale = mad_bit_read(&ptr, 32);
170 bitlen -= 32;
172 nomad->has_xing = 1;
173 return 0;
177 * returns:
178 * 0: eof
179 * -1: error
180 * >0: ok
182 static int fill_buffer(struct nomad *nomad)
184 if (nomad->stream.buffer == NULL || nomad->stream.error == MAD_ERROR_BUFLEN) {
185 ssize_t read_size, remaining, len;
186 unsigned char *read_start;
188 if (nomad->stream.next_frame != NULL) {
189 remaining = nomad->stream.bufend - nomad->stream.next_frame;
190 memmove(nomad->input_buffer, nomad->stream.next_frame, remaining);
191 read_start = nomad->input_buffer + remaining;
192 read_size = INPUT_BUFFER_SIZE - remaining;
193 } else {
194 read_size = INPUT_BUFFER_SIZE;
195 read_start = nomad->input_buffer;
196 remaining = 0;
198 read_size = nomad->cbs.read(nomad->datasource, read_start, read_size);
199 if (read_size == -1) {
200 if (errno != EAGAIN)
201 d_print("read error on bitstream (%d:%s)\n", errno, strerror(errno));
202 return -1;
204 if (read_size == 0)
205 return 0;
207 len = read_size + remaining;
209 nomad->input_offset += read_size;
210 #if 0
211 if (len < MAD_BUFFER_GUARD) {
212 memset(nomad->input_buffer + len, 0, MAD_BUFFER_GUARD - len);
213 len = MAD_BUFFER_GUARD;
215 #endif
216 mad_stream_buffer(&nomad->stream, nomad->input_buffer, len);
217 nomad->stream.error = 0;
219 return 1;
222 static void handle_lost_sync(struct nomad *nomad)
224 unsigned long frame;
225 int size;
227 frame = nomad->cur_frame;
228 if (frame == 0) {
229 /* cur_frame is not set when scanning file */
230 frame = nomad->info.nr_frames;
233 size = id3_tag_size((const char *)nomad->stream.this_frame,
234 nomad->stream.bufend - nomad->stream.this_frame);
235 if (size > 0) {
236 d_print("frame %ld, skipping ID3 tag (%d bytes)\n", frame, size);
237 mad_stream_skip(&nomad->stream, size);
238 } else {
239 d_print("frame %ld\n", frame);
244 /* builds a seek index as the file is decoded
245 * should be called after every call to mad_decode_header,
246 * and before increasing nomad->timer
248 static void build_seek_index(struct nomad *nomad)
250 mad_timer_t timer;
251 off_t offset;
252 int idx;
254 if (nomad->has_xing)
255 return;
257 timer = nomad->timer;
258 mad_timer_add(&timer, nomad->header.duration);
260 if (timer.seconds < (nomad->seek_idx.size + 1) * SEEK_IDX_INTERVAL)
261 return;
263 /* offset = ftell() */
264 offset = nomad->input_offset;
265 /* subtract by buffer length to get offset to start of buffer */
266 offset -= (nomad->stream.bufend - nomad->input_buffer);
267 /* then add offset to the current frame */
268 offset += (nomad->stream.this_frame - nomad->input_buffer);
270 idx = nomad->seek_idx.size;
272 nomad->seek_idx.table = xrenew(struct seek_idx_entry, nomad->seek_idx.table, idx + 1);
273 nomad->seek_idx.table[idx].offset = offset;
274 nomad->seek_idx.table[idx].timer = nomad->timer;
276 nomad->seek_idx.size++;
281 * fields
282 * nomad->info.avg_bitrate and
283 * nomad->info.vbr
284 * are filled only if fast = 0
286 static int scan(struct nomad *nomad)
288 int frame_decoded = 0;
289 int old_bitrate = 0;
290 unsigned long long int bitrate_sum = 0;
292 nomad->info.nr_frames = 0;
293 nomad->info.vbr = 0;
294 while (1) {
295 int rc;
297 rc = fill_buffer(nomad);
298 if (rc == -1)
299 return -1;
300 if (rc == 0)
301 break;
303 if (mad_header_decode(&nomad->header, &nomad->stream) == 0) {
304 bitrate_sum += nomad->header.bitrate;
305 build_seek_index(nomad);
306 mad_timer_add(&nomad->timer, nomad->header.duration);
307 nomad->info.nr_frames++;
308 if (!frame_decoded) {
309 nomad->info.sample_rate = nomad->header.samplerate;
310 nomad->info.channels = MAD_NCHANNELS(&nomad->header);
311 nomad->info.layer = nomad->header.layer;
312 nomad->info.dual_channel = nomad->header.mode == MAD_MODE_DUAL_CHANNEL;
313 nomad->info.joint_stereo = nomad->header.mode == MAD_MODE_JOINT_STEREO;
315 nomad->frame.header = nomad->header;
316 if (mad_frame_decode(&nomad->frame, &nomad->stream) == -1) {
317 if (nomad->stream.error == MAD_ERROR_BUFLEN)
318 continue;
319 if (!MAD_RECOVERABLE(nomad->stream.error)) {
320 d_print("unrecoverable frame level error.\n");
321 return -1;
323 if (nomad->stream.error == MAD_ERROR_LOSTSYNC)
324 handle_lost_sync(nomad);
325 continue;
327 frame_decoded = 1;
328 xing_parse(nomad);
330 #if defined(DEBUG_XING)
331 if (nomad->has_xing && (nomad->xing.flags & XING_FRAMES))
332 d_print("xing: frames: %d (xing)\n", nomad->xing.nr_frames);
333 #endif
335 if (nomad->fast) {
336 nomad->info.avg_bitrate = -1;
337 nomad->info.vbr = -1;
338 if (nomad->has_xing && (nomad->xing.flags & XING_FRAMES)) {
339 nomad->info.nr_frames = nomad->xing.nr_frames;
340 mad_timer_multiply(&nomad->timer, nomad->info.nr_frames);
341 } else {
342 nomad->info.nr_frames = nomad->info.filesize /
343 (nomad->stream.next_frame - nomad->stream.this_frame);
344 mad_timer_multiply(&nomad->timer, nomad->info.nr_frames);
346 break;
348 } else {
349 if (old_bitrate != nomad->header.bitrate)
350 nomad->info.vbr = 1;
352 old_bitrate = nomad->header.bitrate;
353 } else {
354 if (!MAD_RECOVERABLE(nomad->stream.error) && nomad->stream.error != MAD_ERROR_BUFLEN) {
355 d_print("unrecoverable frame level error.\n");
356 return -1;
358 if (nomad->stream.error == MAD_ERROR_LOSTSYNC)
359 handle_lost_sync(nomad);
362 if (nomad->info.nr_frames == 0) {
363 d_print("error: not an mp3 file!\n");
364 return -NOMAD_ERROR_FILE_FORMAT;
366 nomad->info.duration = timer_to_seconds(nomad->timer);
367 if (!nomad->fast)
368 nomad->info.avg_bitrate = bitrate_sum / nomad->info.nr_frames;
369 nomad->cur_frame = 0;
370 nomad->cbs.lseek(nomad->datasource, 0, SEEK_SET);
371 nomad->input_offset = 0;
372 return 0;
375 static int decode(struct nomad *nomad)
377 int rc;
379 start:
380 rc = fill_buffer(nomad);
381 if (rc == -1)
382 return -1;
383 if (rc == 0)
384 return 1;
386 if (mad_frame_decode(&nomad->frame, &nomad->stream)) {
387 if (nomad->stream.error == MAD_ERROR_BUFLEN)
388 goto start;
389 if (!MAD_RECOVERABLE(nomad->stream.error)) {
390 d_print("unrecoverable frame level error.\n");
391 return -1;
393 if (nomad->stream.error == MAD_ERROR_LOSTSYNC)
394 handle_lost_sync(nomad);
395 goto start;
397 nomad->cur_frame++;
398 if (nomad->info.filesize > 0)
399 build_seek_index(nomad);
400 mad_timer_add(&nomad->timer, nomad->frame.header.duration);
401 mad_synth_frame(&nomad->synth, &nomad->frame);
402 return 0;
405 static void init_mad(struct nomad *nomad)
407 mad_stream_init(&nomad->stream);
408 mad_frame_init(&nomad->frame);
409 mad_synth_init(&nomad->synth);
410 mad_header_init(&nomad->header);
411 mad_timer_reset(&nomad->timer);
412 nomad->cur_frame = 0;
413 nomad->i = -1;
414 nomad->input_offset = 0;
417 static void free_mad(struct nomad *nomad)
419 mad_stream_finish(&nomad->stream);
420 mad_frame_finish(&nomad->frame);
421 mad_synth_finish(nomad->synth);
422 mad_header_finish(&nomad->header);
425 static int do_open(struct nomad *nomad, int fast)
427 int rc;
429 init_mad(nomad);
430 nomad->info.filesize = nomad->cbs.lseek(nomad->datasource, 0, SEEK_END);
431 if (nomad->info.filesize == -1) {
432 nomad->fast = 1;
433 } else {
434 nomad->fast = fast != 0;
435 nomad->cbs.lseek(nomad->datasource, 0, SEEK_SET);
437 if (nomad->info.filesize == -1) {
438 rc = decode(nomad);
439 if (rc < 0)
440 goto error;
441 if (rc == 1)
442 goto eof;
443 nomad->info.sample_rate = nomad->frame.header.samplerate;
444 nomad->info.channels = MAD_NCHANNELS(&nomad->frame.header);
445 nomad->info.layer = nomad->frame.header.layer;
446 nomad->info.dual_channel = nomad->header.mode == MAD_MODE_DUAL_CHANNEL;
447 nomad->info.joint_stereo = nomad->header.mode == MAD_MODE_JOINT_STEREO;
449 /* unknown */
450 nomad->info.duration = -1.0;
451 nomad->info.nr_frames = -1;
452 nomad->info.vbr = -1;
453 nomad->info.avg_bitrate = -1;
454 } else {
455 rc = scan(nomad);
456 if (rc < 0)
457 goto error;
458 if (rc == 1)
459 goto eof;
460 free_mad(nomad);
461 init_mad(nomad);
463 d_print("\n frames: %d, br: %d b/s, sr: %d Hz, ch: %d, layer: %d, joint stereo: %d\n"
464 " dual channel: %d, vbr: %d, duration: %g s, xing: %d\n",
465 nomad->info.nr_frames, nomad->info.avg_bitrate,
466 nomad->info.sample_rate, nomad->info.channels,
467 nomad->info.layer, nomad->info.joint_stereo,
468 nomad->info.dual_channel, nomad->info.vbr,
469 nomad->info.duration,
470 nomad->has_xing);
471 #if defined(DEBUG_XING)
472 if (nomad->has_xing)
473 d_print("xing: flags: 0x%x, frames: %d, bytes: %d, scale: %d\n",
474 nomad->xing.flags,
475 nomad->xing.nr_frames,
476 nomad->xing.bytes,
477 nomad->xing.scale);
478 #endif
479 return 0;
480 error:
481 nomad_close(nomad);
482 return rc;
483 eof:
484 nomad_close(nomad);
485 return -NOMAD_ERROR_FILE_FORMAT;
488 int nomad_open(struct nomad **nomadp, int fd, int fast)
490 struct nomad *nomad;
492 nomad = xnew0(struct nomad, 1);
493 nomad->datasource = &nomad->datasource_fd;
494 nomad->datasource_fd = fd;
495 nomad->cbs.read = default_read;
496 nomad->cbs.lseek = default_lseek;
497 nomad->cbs.close = default_close;
498 *nomadp = nomad;
499 /* on error do_open calls nomad_close */
500 return do_open(nomad, fast);
503 int nomad_open_callbacks(struct nomad **nomadp, void *datasource, int fast, struct nomad_callbacks *cbs)
505 struct nomad *nomad;
507 nomad = xnew0(struct nomad, 1);
508 nomad->datasource = datasource;
509 nomad->cbs = *cbs;
510 *nomadp = nomad;
511 /* on error do_open calls nomad_close */
512 return do_open(nomad, fast);
515 void nomad_close(struct nomad *nomad)
517 free_mad(nomad);
518 nomad->cbs.close(nomad->datasource);
519 free(nomad->seek_idx.table);
520 free(nomad);
523 void nomad_info(struct nomad *nomad, struct nomad_info *info)
525 *info = nomad->info;
528 int nomad_read(struct nomad *nomad, char *buffer, int count)
530 int i, j, size, psize, to;
532 if (nomad->i == -1) {
533 int rc;
535 rc = decode(nomad);
536 if (rc < 0)
537 return rc;
538 if (rc == 1)
539 return 0;
540 nomad->i = 0;
542 psize = nomad->info.channels * 16 / 8;
543 size = (nomad->synth.pcm.length - nomad->i) * psize;
544 if (size > count) {
545 to = nomad->i + count / psize;
546 } else {
547 to = nomad->synth.pcm.length;
549 j = 0;
550 for (i = nomad->i; i < to; i++) {
551 short sample;
553 sample = scale(nomad->synth.pcm.samples[0][i]);
554 buffer[j++] = (sample >> 0) & 0xff;
555 buffer[j++] = (sample >> 8) & 0xff;
557 if (nomad->info.channels == 2) {
558 sample = scale(nomad->synth.pcm.samples[1][i]);
559 buffer[j++] = (sample >> 0) & 0xff;
560 buffer[j++] = (sample >> 8) & 0xff;
563 if (to != nomad->synth.pcm.length) {
564 nomad->i = i;
565 } else {
566 nomad->i = -1;
568 return j;
571 int nomad_time_seek(struct nomad *nomad, double pos)
573 off_t offset = 0;
575 if (pos < 0.0 || pos > nomad->info.duration) {
576 errno = EINVAL;
577 return -1;
579 if (nomad->info.filesize == -1) {
580 errno = ESPIPE;
581 return -1;
583 free_mad(nomad);
584 init_mad(nomad);
586 /* calculate seek offset */
587 if (nomad->has_xing) {
588 /* seek to truncate(pos / duration * 100) / 100 * duration */
589 double k, tmp_pos;
590 int ki;
592 k = pos / nomad->info.duration * 100.0;
593 ki = k;
594 tmp_pos = ((double)ki) / 100.0 * nomad->info.duration;
595 nomad->timer.seconds = (signed int)tmp_pos;
596 nomad->timer.fraction = (tmp_pos - (double)nomad->timer.seconds) * MAD_TIMER_RESOLUTION;
597 #if defined(DEBUG_XING)
598 d_print("seeking to %g = %g %d%%\n",
599 pos,
600 timer_to_seconds(nomad->timer),
601 ki);
602 #endif
603 offset = ((unsigned long long)nomad->xing.toc[ki] * nomad->xing.bytes) / 256;
604 } else if (nomad->seek_idx.size > 0) {
605 int idx = (int)(pos / SEEK_IDX_INTERVAL) - 1;
607 if (idx > nomad->seek_idx.size - 1)
608 idx = nomad->seek_idx.size - 1;
610 if (idx >= 0) {
611 offset = nomad->seek_idx.table[idx].offset;
612 nomad->timer = nomad->seek_idx.table[idx].timer;
615 if (nomad->cbs.lseek(nomad->datasource, offset, SEEK_SET) < 0)
616 return -1;
618 nomad->input_offset = offset;
619 while (timer_to_seconds(nomad->timer) < pos) {
620 int rc;
622 rc = fill_buffer(nomad);
623 if (rc == -1)
624 return -1;
625 if (rc == 0)
626 return 0;
628 if (mad_header_decode(&nomad->header, &nomad->stream) == 0) {
629 build_seek_index(nomad);
630 mad_timer_add(&nomad->timer, nomad->header.duration);
631 } else {
632 if (!MAD_RECOVERABLE(nomad->stream.error) && nomad->stream.error != MAD_ERROR_BUFLEN) {
633 d_print("unrecoverable frame level error.\n");
634 return -1;
636 if (nomad->stream.error == MAD_ERROR_LOSTSYNC)
637 handle_lost_sync(nomad);
640 #if defined(DEBUG_XING)
641 if (nomad->has_xing)
642 d_print("seeked to %g = %g\n", pos, timer_to_seconds(nomad->timer));
643 #endif
644 return 0;
647 double nomad_time_tell(struct nomad *nomad)
649 return timer_to_seconds(nomad->timer);
652 double nomad_time_total(struct nomad *nomad)
654 return nomad->info.duration;