Audio-Scan

 view release on metacpan or  search on metacpan

src/mp3.c  view on Meta::CPAN

        my_hv_store( info, "lame_preset", newSVpv( presets_v[mp3->xing_frame->lame_preset], 0 ) );
      }
    }
    else if (mp3->xing_frame->lame_preset >= 1000 && mp3->xing_frame->lame_preset <= 1007) {
      mp3->xing_frame->lame_preset -= 1000;
      if ( presets_old[mp3->xing_frame->lame_preset] ) {
        my_hv_store( info, "lame_preset", newSVpv( presets_old[mp3->xing_frame->lame_preset], 0 ) );
      }
    }
  }

  if (mp3->vbr == ABR || mp3->vbr == VBR) {
    my_hv_store( info, "vbr", newSViv(1) );
  }

  // DLNA profile detection
  if (_is_mp3x_profile(mp3))
    my_hv_store( info, "dlna_profile", newSVpvn( "MP3X", 4 ) );
  else if (_is_mp3_profile(mp3))
    my_hv_store( info, "dlna_profile", newSVpvn( "MP3", 3 ) );

out:

  return mp3;
}

int
mp3_find_frame(PerlIO *infile, char *file, int offset)
{
  Buffer mp3_buf;
  unsigned char *bptr;
  unsigned int buf_size;
  struct mp3frame frame;
  int frame_offset = -1;
  HV *info = newHV();

  mp3info *mp3 = _mp3_parse(infile, file, info);

  buffer_init(&mp3_buf, MP3_BLOCK_SIZE);

  if (!mp3->song_length_ms)
    goto out;

  // (undocumented) If offset is negative, treat it as an absolute file offset in bytes
  // This is a bit ugly but avoids the need to write an entirely new method
  if (offset < 0) {
    frame_offset = abs(offset);
    if (frame_offset < mp3->audio_offset) {
      // Force offset to be at least audio_offset, so we don't end up in an ID3 tag
      frame_offset = mp3->audio_offset;
    }
    DEBUG_TRACE("find_frame: using absolute offset value %d\n", frame_offset);
  }
  else {
    if (offset >= mp3->song_length_ms) {
      goto out;
    }

    // Use Xing TOC if available
    if ( mp3->xing_frame->has_toc ) {
      float percent;
      uint8_t ipercent;
      uint16_t tva;
      uint16_t tvb;
      float tvx;

      percent = (offset * 1.0 / mp3->song_length_ms) * 100;
      ipercent = (int)percent;

      if (ipercent > 99)
        ipercent = 99;

      // Interpolate between 2 TOC points
      tva = mp3->xing_frame->xing_toc[ipercent];
      if (ipercent < 99) {
        tvb = mp3->xing_frame->xing_toc[ipercent + 1];
      }
      else {
        tvb = 256;
      }

      tvx = tva + (tvb - tva) * (percent - ipercent);

      frame_offset = (int)((1.0/256.0) * tvx * mp3->xing_frame->xing_bytes);

      frame_offset += mp3->audio_offset;

      // Don't return offset == audio_offset, because that would be the Xing frame
      if (frame_offset == mp3->audio_offset) {
        DEBUG_TRACE("find_frame: frame_offset == audio_offset, skipping to next frame\n");
        frame_offset += 1;
      }

      DEBUG_TRACE("find_frame: using Xing TOC, song_length_ms: %d, percent: %f, tva: %d, tvb: %d, tvx: %f, frame offset: %d\n",
        mp3->song_length_ms, percent, tva, tvb, tvx, frame_offset
      );
    }
    else {
      // calculate offset using bitrate
      float bytes_per_ms = mp3->bitrate / 8.0;

      frame_offset = (int)(bytes_per_ms * offset);

      frame_offset += mp3->audio_offset;

      DEBUG_TRACE("find_frame: using bitrate %d, bytes_per_ms: %f, frame offset: %d\n", mp3->bitrate, bytes_per_ms, frame_offset);
    }
  }

  // If frame_offset is too near the end of the file we won't find a valid frame
  // so require offset to be at least 1000 bytes from the end of the file
  // XXX this would be more accurate if we determined max_frame_len
  if ((mp3->file_size - frame_offset) < 1000) {
    frame_offset -= 1000 - (mp3->file_size - frame_offset);
    if (frame_offset < 0)
      frame_offset = 0;
    DEBUG_TRACE("find_frame: offset too close to end of file, adjusted to %d\n", frame_offset);
  }

  PerlIO_seek(infile, frame_offset, SEEK_SET);

  if ( !_check_buf(infile, &mp3_buf, 4, MP3_BLOCK_SIZE) ) {
    frame_offset = -1;
    goto out;
  }

  bptr = (unsigned char *)buffer_ptr(&mp3_buf);
  buf_size = buffer_len(&mp3_buf);

  // Find 0xFF sync and verify it's a valid mp3 frame header
  while (1) {
    if (
      buf_size < 4
      ||
      ( bptr[0] == 0xFF && !_decode_mp3_frame( bptr, &frame ) )
    ) {
      break;
    }

    bptr++;
    buf_size--;
  }

  if (buf_size >= 4) {
    frame_offset += buffer_len(&mp3_buf) - buf_size;
    DEBUG_TRACE("find_frame: frame_offset: %d\n", frame_offset);
  }
  else {
    // Didn't find a valid frame, probably too near the end of the file
    DEBUG_TRACE("find_frame: did not find a valid frame\n");
    frame_offset = -1;
  }

out:
  buffer_free(&mp3_buf);



( run in 0.581 second using v1.01-cache-2.11-cpan-39bf76dae61 )