Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / libzmpeg3 / audio / audio.C
diff --git a/cinelerra-5.1/libzmpeg3/audio/audio.C b/cinelerra-5.1/libzmpeg3/audio/audio.C
new file mode 100644 (file)
index 0000000..efcd901
--- /dev/null
@@ -0,0 +1,783 @@
+#include "../libzmpeg3.h"
+
+static class audio_decode_t {
+  pthread_mutex_t zlock;
+public:
+  audio_decode_t() {
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutex_init(&zlock, &attr);
+  }
+  ~audio_decode_t() {
+    pthread_mutex_destroy(&zlock);
+  }
+  void lock() { pthread_mutex_lock(&zlock); }
+  void unlock() { pthread_mutex_unlock(&zlock); }
+} audio_decode;
+
+static void toc_error()
+{
+  zerr( "mpeg3audio: sample accurate seeking without a table of contents \n"
+    "is no longer supported.  Use mpeg3toc <mpeg file> <table of contents>\n"
+    "to generate a table of contents and load the table of contents instead.\n");
+}
+
+
+int zaudio_t::
+rewind_audio()
+{
+  if( track->sample_offsets )
+    track->demuxer->seek_byte(track->sample_offsets[0]);
+  else
+    track->demuxer->seek_byte(0);
+  track->reset_pts();
+  packet_position = 0;
+  return 0;
+}
+
+/* Advance to next header and read it. */
+int zaudio_t::
+read_header()
+{
+  int count = 0x10000;
+  int got_it = 0;
+  int result = 1;
+  int pos = 0;
+
+  switch(track->format) {
+  case afmt_AC3:
+    while( count > 0 && !track->demuxer->eof() ) {
+      if( pos >= (int)sizeof(packet_buffer)-8 ) {
+        memmove(packet_buffer, &packet_buffer[pos], packet_position-=pos);
+        pos = 0;
+      }
+      if( packet_position-pos < 8 ) {
+        packet_buffer[packet_position++] = track->demuxer->read_char();
+        continue;
+      }
+      got_it = ac3_decoder->ac3_header(&packet_buffer[pos]);
+      if( got_it ) break;
+      ++pos;  --count;
+    }
+
+    if( got_it ) {
+      channels = ac3_decoder->channels;
+      samplerate = ac3_decoder->samplerate;
+      framesize = ac3_decoder->framesize;
+      result = 0;
+    }
+    break;
+
+  case afmt_MPEG:
+    /* Layer 1 not supported */
+    if( layer_decoder->layer == 1 ) break;
+
+    /* Load starting bytes and check format when not seekable */
+    if( !src->seekable && !packet_position && !track->total_sample_offsets ) {
+      if( src->demuxer->payload_unit_start_indicator ) {
+        if( track->demuxer->read_data(packet_buffer,packet_position=4) ) break;
+        /* reject mp4 data */
+        if( !zaudio_decoder_layer_t::id3_check(packet_buffer) &&
+              zaudio_decoder_layer_t::layer_check(packet_buffer) ) {
+          zerrs("bad mp3 header, pid=%04x, track ignored\n", track->pid);
+          track->format = afmt_IGNORE;
+          result = -1;
+        }
+      }
+    }
+
+    while( count > 0 && !track->demuxer->eof() ) {
+      if( pos >= (int)sizeof(packet_buffer)-4 ) {
+        memmove(packet_buffer, &packet_buffer[pos], packet_position-=pos);
+        pos = 0;
+      }
+      if( packet_position-pos < 4 ) {
+        packet_buffer[packet_position++] = track->demuxer->read_char();
+        continue;
+      }
+      got_it = layer_decoder->layer3_header(&packet_buffer[pos]);
+      if( got_it ) break;
+//zmsgs("%d got_it=%d packet=%02x%02x%02x%02x\n", __LINE__,
+//  got_it, (uint8_t)packet_buffer[pos+0], (uint8_t)packet_buffer[pos+1],
+//  (uint8_t)packet_buffer[pos+2], (uint8_t)packet_buffer[pos+3]);
+      ++pos;  --count;
+      /* ID3 tags need to reset the count to skip the tags */
+      if( layer_decoder->id3_state != id3_IDLE ) count = 0x10000;
+    }
+
+    if( got_it ) {
+      channels = layer_decoder->channels;
+      samplerate = layer_decoder->samplerate;
+      framesize = layer_decoder->framesize;
+      result = 0;
+    }
+    break;
+
+  case afmt_PCM:
+    while( count > 0 && !track->demuxer->eof() ) {
+      if( pos >= (int)sizeof(packet_buffer)-PCM_HEADERSIZE ) {
+        memmove(packet_buffer, &packet_buffer[pos], packet_position-=pos);
+        pos = 0;
+      }
+      if( packet_position-pos < PCM_HEADERSIZE ) {
+        packet_buffer[packet_position++] = track->demuxer->read_char();
+        continue;
+      }
+      got_it = pcm_decoder->pcm_header(&packet_buffer[pos]);
+      if( got_it ) break;
+      ++pos;  --count;
+    }
+
+    if( got_it ) {
+      channels = pcm_decoder->channels;
+      samplerate = pcm_decoder->samplerate;
+      framesize = pcm_decoder->framesize;
+      result = 0;
+    }
+    break;
+
+  default:
+    result = -1;
+    break;
+  }
+
+  if( pos > 0 )
+    memmove(packet_buffer, &packet_buffer[pos], packet_position-=pos);
+
+  if( track->demuxer->error() )
+    result = 1;
+  if( !result ) {
+    if( channels > track->channels )
+      track->channels = channels;
+    track->sample_rate = samplerate;
+  }
+//zmsgs("%d %d %d\n", track->channels, track->sample_rate, framesize);
+
+  return result;
+}
+
+zaudio_t::
+~audio_t()
+{
+  if( output ) {
+    for( int i=0; i<output_channels; ++i )
+      delete [] output[i];
+    delete [] output;
+  }
+  if( ac3_decoder ) delete ac3_decoder;
+  if( layer_decoder ) delete layer_decoder;
+  if( pcm_decoder ) delete pcm_decoder;
+}
+
+void zaudio_t::
+update_channels()
+{
+  int i;
+  float **new_output = new float *[channels];
+  if( output_channels < channels ) {
+    for( i=0; i<output_channels; ++i )
+      new_output[i] = output[i];
+    while( i < channels ) // more channels
+      new_output[i++] = new float[output_allocated];
+  }
+  else {
+    for( i=0; i<channels; ++i )
+      new_output[i] = output[i];
+    while( i < output_channels ) // fewer channels
+      delete [] output[i++];
+  }
+  delete [] output;
+  output = new_output;
+  output_channels = channels;
+}
+
+int zaudio_t::
+read_frame(int render)
+{
+  zdemuxer_t *demux = track->demuxer;
+  int samples = -1;
+
+  /* Find and read next header */
+  int result = read_header();
+//if( src->seekable ) {
+//  int64_t app_pos = track->apparent_position();
+//  int64_t aud_pos = audio_position();
+//  double atime = track->get_audio_time();
+//  int64_t pts_pos = atime * track->sample_rate + 0.5;
+//  zmsgs(" apr_pos " _LD "/" _LD " + " _LD "  " _LD " + " _LD "\n",
+//    app_pos, aud_pos, app_pos-aud_pos, pts_pos, pts_pos-aud_pos);
+//}
+  if( !result ) {
+    /* Handle changes in channel count, for ATSC */
+    if( output_channels != channels )
+      update_channels();
+    /* try to read rest of frame */
+    int len = framesize - packet_position;
+    uint8_t *bfr = packet_buffer + packet_position;
+    if( !src->seekable ) {
+      int data_length = demux->zdata.length();
+      if( data_length < len ) len = data_length;
+    }
+    if( !(result = demux->read_data(bfr, len)) )
+      packet_position += len;
+  }
+
+  if( !result && packet_position >= framesize ) {
+    float *out[output_channels];
+    for( int i=0; i<output_channels; ++i )
+      out[i] = output[i] + output_size;
+
+    switch(track->format) {
+    case afmt_AC3:
+      audio_decode.lock(); /* Liba52 is not reentrant */
+      samples = ac3_decoder->do_ac3(packet_buffer,framesize,out,render);
+      audio_decode.unlock();
+//zmsgs("ac3 %d samples\n", samples);
+      break;
+    case afmt_MPEG:
+      switch( layer_decoder->layer ) {
+      case 2:
+        samples = layer_decoder->do_layer2(packet_buffer,framesize,out,render);
+        break;
+      case 3:
+        samples = layer_decoder->do_layer3(packet_buffer,framesize,out,render);
+        break;
+      }
+      break;
+    case afmt_PCM:
+      samples = pcm_decoder->do_pcm(packet_buffer,framesize,out,render);
+      break;
+    }
+    if( samples > 0 )
+      output_size += samples;
+    packet_position = 0;
+  }
+
+  return samples;
+}
+
+/* Get the length. */
+/* Use chunksize if demuxer has a table of contents */
+/* For elementary streams use sample count over a certain number of */
+/*  bytes to guess total samples */
+/* For program streams use timecode */
+int zaudio_t::
+get_length()
+{
+  int result = 0;
+  int samples = 0;
+  int64_t stream_end = track->demuxer->stream_end;
+  int64_t total_bytes = track->demuxer->movie_size();
+  int64_t stream_max = total_bytes;
+  if( stream_max > 0x1000000 ) stream_max = 0x1000000;
+  track->demuxer->stream_end = stream_max;
+  rewind_audio();
+  /* Table of contents */
+  if( track->sample_offsets || !src->is_audio_stream() ) {
+    /* Estimate using multiplexed stream size in seconds */
+    /* Get stream parameters for header validation */
+    /* Need a table of contents */
+    for( int retry=0; retry<100 && samples<=0; ++retry ) {
+      samples = read_frame(0);
+    }
+    result = track->sample_offsets ? track->total_samples : 0;
+    /* : (long)(track->demuxer->length() * track->sample_rate); */
+  }
+  else { /* Estimate using average bitrate */
+    long test_bytes = 0;
+    long max_bytes = 0x40000;
+    long test_samples = 0;
+    while( test_bytes < max_bytes ) {
+      int samples = read_frame(0);
+      if( samples <= 0 ) break;
+      test_samples += samples;
+      test_bytes += framesize;
+    }
+    result = (long)(((double)total_bytes / test_bytes) * test_samples + 0.5);
+  }
+
+  track->demuxer->stream_end = stream_end;
+  output_size = 0;
+  rewind_audio();
+  return result;
+}
+
+int zatrack_t::
+calculate_format(zmpeg3_t *src)
+{
+  uint8_t header[8];
+  int result = 0;
+  /* Determine the format of the stream.  */
+  /* If it isn't in the first 8 bytes give up and go to a movie. */
+  switch( format ) {
+  case afmt_UNKNOWN:
+    /* Need these 8 bytes later on for header parsing */
+    result = demuxer->read_data(header, sizeof(header));
+//zmsgs(_LD"\n", demuxer->tell_byte());
+    if( !result )
+      format = !zaudio_decoder_ac3_t::ac3_check(header) ? afmt_AC3 : afmt_MPEG;
+    if( audio ) {
+      memcpy(audio->packet_buffer+1, header, sizeof(header));
+      audio->packet_position = 9;
+    }
+    break;
+  case afmt_IGNORE:
+    result = 1;
+    break;
+  default:
+    break;
+  }
+
+  return result;
+}
+
+int zaudio_t::
+init_audio(zmpeg3_t *zsrc, zatrack_t *ztrack, int zformat)
+{
+  int result = 0;
+  src = zsrc;
+  track = ztrack;
+  demuxer_t *demux = track->demuxer;
+  byte_seek = -1;
+  sample_seek = -1;
+  track->format = zformat;
+
+  if( zsrc->seekable )
+    result = track->calculate_format(src);
+//zmsgs(_LD"\n", demux->tell_byte());
+  /* get stream parameters */
+  if( !result && zsrc->seekable ) {
+    switch( track->format ) {
+    case afmt_AC3:
+      ac3_decoder = new audio_decoder_ac3_t();
+      break;
+    case afmt_MPEG:
+      layer_decoder = new audio_decoder_layer_t();
+      break;
+    case afmt_PCM:
+      pcm_decoder = new audio_decoder_pcm_t();
+      break;
+    default:
+      result = 1;
+    }
+    if( !result ) {
+      int64_t stream_end = demux->stream_end;
+      //if( !track->sample_offsets )
+      {
+        if( zsrc->is_transport_stream() )
+          demux->stream_end = START_BYTE + MAX_TS_PROBE;
+        else if( src->is_program_stream() )
+          demux->stream_end = START_BYTE + MAX_PGM_PROBE;
+      }
+      rewind_audio();
+      result = read_header();
+      if( !result && src->is_audio_stream() )
+        start_byte = demux->tell_byte() - zsrc->packet_size;
+      demux->stream_end = stream_end;
+    }
+//zmsgs("1 %d %d %d start_byte=0x"_LX"\n", track->format, 
+// layer_decoder->layer, result, start_byte);
+  }
+  return result;
+}
+
+zaudio_t *zmpeg3_t::
+new_audio_t(zatrack_t *ztrack, int zformat)
+{
+  audio_t *new_audio = new audio_t();
+  int result = new_audio->init_audio(this,ztrack,zformat);
+  /* Calculate Length */
+  if( !result && seekable ) {
+    new_audio->rewind_audio();
+    ztrack->total_samples = new_audio->get_length();
+  }
+  else if( seekable ) {
+    delete new_audio;
+    new_audio = 0;
+  }
+  return new_audio;
+}
+
+
+
+int zaudio_t::
+seek()
+{
+  demuxer_t *demux = track->demuxer;
+  int seeked = 0;
+/* Stream is unseekable */
+  if( !src->seekable ) return 0;
+/* Sample seek was requested */
+  if( sample_seek >= 0 ) {
+    sample_seek += track->nudge;
+    if( sample_seek < 0 ) sample_seek = 0;
+/* Doesn't work with VBR streams + ID3 tags */
+//zmsgs("%d "_LD" "_LD" "_LD" "_LD"\n", __LINE__, sample_seek,
+//  track->current_position, output_position, audio_position());
+/* Don't do anything if the destination is inside the sample buffer */
+    if( sample_seek < output_position ||
+        sample_seek >= audio_position() ) {
+      if( sample_seek != output_position ) {
+        /* Use table of contents */
+        if( track->sample_offsets ) {
+          int index = sample_seek / AUDIO_CHUNKSIZE;
+          if( index >= track->total_sample_offsets )
+            index = track->total_sample_offsets - 1;
+          /* incase of padding */
+          int64_t byte = track->sample_offsets[index];
+          while( index > 0 && byte == track->sample_offsets[index-1] ) --index;
+          output_position = (int64_t)index * AUDIO_CHUNKSIZE;
+          output_size = 0;
+          demux->seek_byte(byte);
+        }
+        else if( sample_seek > 0 && !src->is_audio_stream() ) { /* Use demuxer */
+          toc_error();
+/*        double time_position = (double)sample_seek / track->sample_rate; */
+/*        result |= mpeg3demux_seek_time(demuxer, time_position); */
+          output_position = sample_seek;
+        }
+        else { /* Use bitrate for elementary stream */
+          int64_t total_bytes = demux->movie_size() - start_byte;
+          int64_t byte = (int64_t)(total_bytes * 
+            ((double)sample_seek)/track->total_samples) + start_byte;
+//zmsgs("%d byte="_LD"\n", __LINE__, byte);
+          output_position = sample_seek;
+          output_size = 0;
+          demux->seek_byte(byte);
+        }
+        seeked = 1;
+      }
+    }
+    sample_seek = -1;
+  }
+  else if( byte_seek >= 0 ) {
+    output_position = 0;
+    output_size = 0;
+    /* Percentage seek was requested */
+    demux->seek_byte(byte_seek);
+    /* Scan for pts if we're the first to seek. */
+    /* if( src->percentage_pts < 0 ) */
+    /*   file->percentage_pts = demux->scan_pts(); */
+    /* else */
+    /*   demux->goto_pts(src->percentage_pts); */
+    seeked = 1;
+    byte_seek = -1;
+  }
+
+  if( seeked ) {
+    packet_position = 0;
+    track->reset_pts();
+    switch( track->format ) {
+    case afmt_MPEG:
+      layer_decoder->layer_reset();
+      break;
+    }
+  }
+
+  return 0;
+}
+
+/* ================================*/
+/*          ENTRY POINTS           */
+/* ================================*/
+
+int zaudio_t::
+seek_byte(int64_t byte)
+{
+  byte_seek = byte;
+  return 0;
+}
+
+int zaudio_t::
+seek_sample(long sample)
+{
+/* Doesn't work for rereading audio during percentage seeking */
+/*  if(sample > track->total_samples) sample = track->total_samples; */
+  sample_seek = (int64_t)sample;
+  return 0;
+}
+
+/* Read raw frames for the mpeg3cat utility */
+int zaudio_t::
+read_raw(uint8_t *output, long *size, long max_size)
+{
+  *size = 0;
+  switch( track->format ) {
+  case afmt_AC3: /* Just write the AC3 stream */
+    if( track->demuxer->read_data(output, 0x800) ) return 1;
+    *size = 0x800;
+    break;
+
+  case afmt_MPEG: /* Fix the mpeg stream */
+    if( track->demuxer->read_data(output, 0x800) ) return 1;
+    *size = 0x800;
+    break;
+    
+  case afmt_PCM: /* This is required to strip the headers */
+    if( track->demuxer->read_data(output, framesize) ) return 1;
+    *size = framesize;
+    break;
+  }
+
+  return 0;
+#if 0
+/* This probably doesn't work. */
+  result = read_header(audio);
+  switch( track->format ) {
+  case afmt_AC3: /* Just write the AC3 stream */
+    result = track->demuxer->read_data(output, framesize);
+    *size = framesize;
+//zmsgs("1 %d\n", framesize);
+    break;
+  case afmt_MPEG: /* Fix the mpeg stream */
+    if( !result ) {
+      if(track->demuxer->read_data(output, framesize)) return 1;
+      *size += framesize;
+    }
+    break;
+  case afmt_PCM:
+    if(track->demuxer->read_data(output, framesize)) return 1;
+    *size = framesize;
+    break;
+  }
+  return result;
+#endif
+}
+
+void zaudio_t::
+shift_audio(int64_t diff)
+{
+  int len = diff > 0 ? output_size - diff : 0;
+  if( len > 0 ) {
+    for( int i=0; i<output_channels; ++i )
+      memmove(output[i], output[i]+diff, len*sizeof(*output[i]));
+    output_position += diff;
+    output_size -= diff;
+  }
+  else {
+    output_position += output_size;
+    output_size = 0;
+  }
+}
+
+/* zero pad data to match pts */
+int zaudio_t::
+audio_pts_padding()
+{
+  int result = 0, silence = 0;
+  if( track->audio_time >= 0 && src->pts_padding > 0 ) {
+    int64_t pts_pos = track->audio_time * track->sample_rate + 0.5;
+    int padding = pts_pos - track->pts_position;
+    if( padding > 0 ) {
+      if( track->askip <= 0 ) {
+        int limit = track->sample_rate/8;
+        if( padding > limit ) {
+          int64_t aud_pos = audio_position();
+          zmsgs("audio start padding  pid %02x @ " _LDv(12)" (%12.6f) %d samples\n",
+                track->pid, aud_pos, track->get_audio_time(), padding);
+          track->askip = 1;
+          result = 1;
+        }
+      }
+      else if( padding > track->sample_rate )
+        result = silence = 1;
+      else if( !(++track->askip & 1) )
+        result = 1;
+      if( result ) {
+        if( padding > AUDIO_MAX_DECODE ) padding = AUDIO_MAX_DECODE;
+        int avail = output_allocated - output_size;
+        if( padding > avail ) padding = avail;
+        if( padding > 0 ) {
+          int n = track->sample_rate/20;
+          if( output_size < n ) silence = 1;
+          if( silence ) {
+            for( int chan=0; chan<output_channels; ++chan ) {
+              float *out = output[chan] + output_size;
+              for( int i=0; i<padding; ++i ) *out++ = 0;
+            }
+          }
+          else {
+            int k = output_size - n;
+            for( int chan=0; chan<output_channels; ++chan ) {
+              float *out = output[chan] + output_size;
+              float *pad = output[chan] + k;
+              for( int i=0; i<padding; ++i ) *out++ = *pad++;
+            }
+          }
+          output_size += padding;
+          track->pts_position += padding;
+        }
+      }
+    }
+    else if( track->askip > 0 ) {
+      int64_t aud_pos = audio_position();
+      zmsgs("audio end   padding  pid %02x @ " _LDv(12) " (%12.6f) %d\n",
+            track->pid, aud_pos, track->get_audio_time(), (1+track->askip)/2);
+      track->askip = 0;
+    }
+  }
+  return result;
+}
+
+int zaudio_t::
+audio_pts_skipping(int samples)
+{
+  int result = 0;
+  if( track->audio_time >= 0 && src->pts_padding > 0 ) {
+    int64_t pts_pos = track->audio_time * track->sample_rate + 0.5;
+    int skipping = track->pts_position - pts_pos;
+    if( skipping > 0 ) {
+      if( track->askip >= 0 ) {
+        int limit = track->sample_rate/8;
+        if( skipping > 3*limit ) {
+          int64_t aud_pos = audio_position();
+          zmsgs("audio start skipping pid %02x @ " _LDv(12)" (%12.6f) %d samples\n",
+                track->pid, aud_pos, track->get_audio_time(), skipping);
+          track->askip = -1;
+          result = 1;
+        }
+      }
+      else if( !(--track->askip & 1) || skipping > track->sample_rate )
+        result = 1;
+      if( result ) {
+        if( samples < skipping ) skipping = samples;
+        if( output_size < skipping ) skipping = output_size;
+        output_size -= skipping;
+        track->pts_position -= skipping;
+      }
+    }
+    else if( track->askip < 0 ) {
+      int64_t aud_pos = audio_position();
+      zmsgs("audio end   skipping pid %02x @ " _LDv(12) " (%12.6f) %d\n",
+            track->pid, aud_pos, track->get_audio_time(), (1-track->askip)/2);
+      track->askip = 0;
+    }
+  }
+  return result;
+}
+
+void zaudio_t::
+update_audio_history()
+{
+  int64_t diff = track->track_position() - output_position;
+  if( diff ) shift_audio(diff);
+}
+
+/* Channel is 0 to channels - 1 */
+/* Always render since now the TOC contains index files. */
+int zaudio_t::
+decode_audio(void *output_v, int atyp, int channel, int len)
+{
+  int i, j, k;
+  zdemuxer_t *demux = track->demuxer;
+//zmsgs("decode_audio(out_pos="_LD"-"_LD", aud_pos "_LD", seek "_LD"  ch=%d, len=%d)\n",
+//  output_position, audio_position(), track->track_position(),
+//  (sample_seek<0 ? sample_seek : sample_seek+track->nudge), channel, len);
+
+  /* Get header for streaming mode */
+  if( track->format == afmt_IGNORE ) return 1;
+  if( track->format == afmt_UNKNOWN ) {
+    if( track->calculate_format(src) ) return 1;
+  }
+  if( track->format == afmt_AC3 && !ac3_decoder )
+    ac3_decoder = new audio_decoder_ac3_t();
+  else if( track->format == afmt_MPEG && !layer_decoder )
+    layer_decoder = new audio_decoder_layer_t();
+  else if( track->format == afmt_PCM && !pcm_decoder )
+    pcm_decoder = new audio_decoder_pcm_t();
+  /* Handle seeking requests */
+  seek();
+  int64_t end_pos = track->track_position() + len;
+  long new_size = end_pos - output_position + MAXFRAMESAMPLEZ;
+  /* Expand output until enough room exists for new data */
+  if( new_size > output_allocated ) {
+    for( i=0; i<output_channels; ++i ) {
+      float *old_output = output[i];
+      float *new_output = new float[new_size];
+      if( output_size ) {
+        int len = output_size * sizeof(new_output[0]);
+        memmove(new_output, old_output, len);
+        len = (new_size-output_size) * sizeof(new_output[0]);
+        memset(new_output+output_size, 0, len);
+      }
+      delete [] output[i];
+      output[i] = new_output;
+    }
+    output_allocated = new_size;
+  }
+
+
+  /* Decode frames until the output is ready */
+  int retry = 0;
+
+  while( retry < 256 ) {
+    int64_t aud_pos = audio_position();
+    int64_t demand = end_pos - aud_pos;
+    if( demand <= 0 ) break;
+    if( audio_pts_padding() ) continue;
+    if( output_allocated-output_size < AUDIO_MAX_DECODE ) break;
+//zmsgs(" out_pos/sz "_LD"/%d=aud_pos "_LD", end_pos "_LD", demand "_LD",
+//  tell/eof "_LD"/%d\n", output_position, output_size, aud_pos, end_pos,
+//  demand, demux->tell_byte(), demux->eof());
+    if( demux->eof() ) break;
+    /* if overflowing, shift audio back */
+    if( src->seekable && output_size > AUDIO_HISTORY ) {
+      int diff = demand + output_size - AUDIO_HISTORY;
+      shift_audio(diff);
+    }
+    int samples = read_frame(1);
+    if( samples < 0 ) break;
+    if( !samples ) { ++retry; continue; }
+    retry = 0;
+    if( audio_pts_skipping(samples) ) continue;
+    track->update_frame_pts();
+    track->update_audio_time();
+  }
+
+  /* Copy the buffer to the output */
+  if( !track->channels ) return 1;
+  if( channel >= track->channels )
+    channel = track->channels - 1;
+  i = 0;
+  j = track->track_position() - output_position;
+  k = output_size - j;
+  if( k > len ) k = len;
+  float *out = channel < output_channels ?
+    output[channel] + j : 0;
+
+  /* transmit data in specified format */
+  switch( atyp ) {
+  case atyp_NONE:
+    break;
+  case atyp_DOUBLE: {
+    double *output_d = (double *)output_v;
+    if( out ) while( i < k )
+      output_d[i++] = *out++;
+    while( i < len ) output_d[i++] = 0.;
+    break; }
+  case atyp_FLOAT: {
+    float *output_f = (float *)output_v;
+    if( out )
+      memmove(output_f,out,(i=k)*sizeof(output_f[0]));
+    while( i < len ) output_f[i++] = 0.;
+    break; }
+  case atyp_INT: {
+    int *output_i = (int *)output_v;
+    if( out ) while( i < k )
+      output_i[i++] = clip((int)(*out++ * 0x7fffff),-0x800000,0x7fffff);
+    while( i < len ) output_i[i++] = 0;
+    break; }
+  case atyp_SHORT: {
+    short *output_s = (short *)output_v;
+    if( out ) while( i < k )
+      output_s[i++] = clip((int)(*out++ * 0x7fff),-0x8000,0x7fff);
+    while( i < len ) output_s[i++] = 0;
+    break; }
+  default:
+    return 1;
+  }
+
+//zmsgs("output_size=%d chan=%d len=%d\n",output_size,channel,len);
+  return output_size > 0 ? 0 : 1;
+}
+