/* findsep.c * library routines for find silent points in mp3 data * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Portions are adapted from minimad.c, included with the * libmad library, distributed under the GNU General Public License. * Copyright (C) 2000-2004 Underbit Technologies, Inc. */ #include #include #include #include #include #include #include #include "mad.h" #include "findsep.h" #include "util.h" #include "srtypes.h" #include "debug.h" #include "list.h" #define MIN_RMS_SILENCE 100 #define MAX_RMS_SILENCE 32767 //max short #define NUM_SILTRACKERS 30 #define READSIZE 2000 // #define READSIZE 1000 /* Uncomment to dump an mp3 of the search window. */ // #define MAKE_DUMP_MP3 1 typedef struct FRAME_LIST_struct FRAME_LIST; struct FRAME_LIST_struct { const unsigned char* m_framepos; long m_samples; long m_pcmpos; LIST m_list; }; typedef struct SILENCETRACKERst { long insilencecount; double silencevol; // long silencestart; unsigned long silstart_samp; BOOL foundsil; } SILENCETRACKER; typedef struct DECODE_STRUCTst { unsigned char* mpgbuf; long mpgsize; long mpgpos; long len_to_sw_ms; long searchwindow_ms; long silence_ms; long silence_samples; unsigned long len_to_sw_start_samp; unsigned long len_to_sw_end_samp; unsigned long pcmpos; long samplerate; SILENCETRACKER siltrackers[NUM_SILTRACKERS]; LIST frame_list; } DECODE_STRUCT; typedef struct GET_BITRATE_STRUCTst { unsigned long bitrate; unsigned char* mpgbuf; long mpgsize; } GET_BITRATE_STRUCT; /***************************************************************************** * Public functions *****************************************************************************/ /***************************************************************************** * Private functions *****************************************************************************/ static void init_siltrackers(SILENCETRACKER* siltrackers); static void apply_padding (DECODE_STRUCT* ds, unsigned long silstart, long padding1, long padding2, u_long* pos1, u_long* pos2); static void free_frame_list (DECODE_STRUCT* ds); static enum mad_flow input(void *data, struct mad_stream *ms); static void search_for_silence(DECODE_STRUCT *ds, double vol); static signed int scale(mad_fixed_t sample); static enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm); static enum mad_flow filter (void *data, struct mad_stream const *ms, struct mad_frame *frame); static enum mad_flow error(void *data, struct mad_stream *ms, struct mad_frame *frame); static enum mad_flow header(void *data, struct mad_header const *pheader); static enum mad_flow input_get_bitrate (void *data, struct mad_stream *stream); static enum mad_flow header_get_bitrate (void *data, struct mad_header const *pheader); /***************************************************************************** * Private Vars *****************************************************************************/ /***************************************************************************** * Functions *****************************************************************************/ error_code findsep_silence (const u_char* mpgbuf, long mpgsize, long len_to_sw, long searchwindow, long silence_length, long padding1, long padding2, u_long* pos1, u_long* pos2 ) { DECODE_STRUCT ds; struct mad_decoder decoder; int result; unsigned long silstart; int i; ds.mpgbuf = (unsigned char*)mpgbuf; ds.mpgsize = mpgsize; ds.pcmpos = 0; ds.mpgpos= 0; ds.samplerate = 0; ds.len_to_sw_ms = len_to_sw; ds.searchwindow_ms = searchwindow; ds.silence_ms = silence_length; INIT_LIST_HEAD (&ds.frame_list); debug_printf ("FINDSEP: %p -> %p (%d)\n", mpgbuf, mpgbuf+mpgsize, mpgsize); init_siltrackers(ds.siltrackers); #if defined (MAKE_DUMP_MP3) { FILE* fp = fopen("dump.mp3", "wb"); fwrite(mpgbuf, mpgsize, 1, fp); fclose(fp); } #endif /* Run decoder */ mad_decoder_init(&decoder, &ds, input /* input */, header/* header */, filter /* filter */, output /* output */, error /* error */, NULL /* message */); result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); mad_decoder_finish(&decoder); debug_printf ("total length: %d\n", ds.pcmpos); debug_printf ("silence_length: %d ms\n", ds.silence_ms); debug_printf ("silence_samples: %d\n", ds.silence_samples); /* Search through siltrackers to find minimum volume point */ assert(ds.mpgsize != 0); silstart = ds.pcmpos/2; for(i = 0; i < NUM_SILTRACKERS; i++) { debug_printf("i=%d, start=%d\n", i, ds.siltrackers[i].silstart_samp); if (ds.siltrackers[i].foundsil) { debug_printf("found!\n"); silstart = ds.siltrackers[i].silstart_samp; break; } } if (i == NUM_SILTRACKERS) { debug_printf("warning: no silence found between tracks\n"); } /* Now that we have the start of the silence, let's add the padding */ apply_padding (&ds, silstart, padding1, padding2, pos1, pos2); /* Free the list of frame info */ free_frame_list (&ds); return SR_SUCCESS; } void init_siltrackers(SILENCETRACKER* siltrackers) { int i; long stepsize = (MAX_RMS_SILENCE - MIN_RMS_SILENCE) / (NUM_SILTRACKERS-1); long rms = MIN_RMS_SILENCE; for (i = 0; i < NUM_SILTRACKERS; i++, rms += stepsize) { siltrackers[i].foundsil = 0; siltrackers[i].silstart_samp = 0; siltrackers[i].insilencecount = 0; siltrackers[i].silencevol = rms; } } static void apply_padding (DECODE_STRUCT* ds, unsigned long silstart, long padding1, long padding2, u_long* pos1, u_long* pos2 ) { /* Compute positions in samples */ FRAME_LIST *pos; long pos1s, pos2s; pos1s = silstart + (ds->silence_samples/2) + padding1 * (ds->samplerate/1000); pos2s = silstart + (ds->silence_samples/2) - padding2 * (ds->samplerate/1000); debug_printf ("Applying padding\n"); /* GCS FIX: Need to check for pos == null */ /* GCS FIX: Watch out for -1, might have mem error! */ pos = list_entry (ds->frame_list.next, FRAME_LIST, m_list); if (pos1s < pos->m_pcmpos) { *pos1 = pos->m_framepos - ds->mpgbuf - 1; } if (pos2s < pos->m_pcmpos) { *pos2 = pos->m_framepos - ds->mpgbuf; } list_for_each_entry (pos, FRAME_LIST, &(ds->frame_list), m_list) { if (pos1s >= pos->m_pcmpos) { *pos1 = pos->m_framepos - ds->mpgbuf - 1; } if (pos2s >= pos->m_pcmpos) { *pos2 = pos->m_framepos - ds->mpgbuf; } } debug_printf ("pos1, pos2 = %d,%d (%02x%02x)\n", *pos1, *pos2, ds->mpgbuf[*pos2], ds->mpgbuf[*pos2+1]); } static void free_frame_list (DECODE_STRUCT* ds) { FRAME_LIST *pos, *n; /* GCS: This seems to be the best way to go through a list. Note no compiler warnings. */ list_for_each_entry_safe (pos, FRAME_LIST, n, &(ds->frame_list), m_list) { list_del (&(pos->m_list)); free (pos); } } enum mad_flow input(void *data, struct mad_stream *ms) { DECODE_STRUCT *ds = (DECODE_STRUCT *)data; long frameoffset = 0; long espnextpos = ds->mpgpos+READSIZE; /* GCS FIX: This trims the last READSIZE from consideration */ if (espnextpos > ds->mpgsize) { return MAD_FLOW_STOP; } if (ms->next_frame) { frameoffset = &(ds->mpgbuf[ds->mpgpos]) - ms->next_frame; /* GCS July 8, 2004 This is the famous frameoffset != READSIZE bug. What appears to be happening is libmad is not syncing properly on the broken initial frame. Therefore, if there is no header yet (hence no ds->samplerate), we'll nudge along the buffer to try to resync. */ if (frameoffset == READSIZE) { if (!ds->samplerate) { frameoffset--; } else { FILE* fp; debug_printf ("%p | %p | %p | %p | %d\n", ds->mpgbuf, ds->mpgpos, &(ds->mpgbuf[ds->mpgpos]), ms->next_frame, frameoffset); fprintf (stderr, "ERROR: frameoffset != READSIZE\n"); debug_printf ("ERROR: frameoffset != READSIZE\n"); fp = fopen ("gcs1.txt","w"); fwrite(ds->mpgbuf,1,ds->mpgsize,fp); fclose(fp); exit (-1); } } } debug_printf ("%p | %p | %p |< %p | %p >| %d\n", ds->mpgbuf, ds->mpgpos, &(ds->mpgbuf[ds->mpgpos]), ms->this_frame, ms->next_frame, frameoffset); mad_stream_buffer (ms, (const unsigned char*) (ds->mpgbuf+ds->mpgpos)-frameoffset, READSIZE); ds->mpgpos += READSIZE - frameoffset; return MAD_FLOW_CONTINUE; } void search_for_silence (DECODE_STRUCT *ds, double vol) { int i; for(i = 0; i < NUM_SILTRACKERS; i++) { SILENCETRACKER *pstracker = &ds->siltrackers[i]; if (pstracker->foundsil) continue; if (vol < pstracker->silencevol) { if (pstracker->insilencecount == 0) { pstracker->silstart_samp = ds->pcmpos; } pstracker->insilencecount++; } else { pstracker->insilencecount = 0; } if (pstracker->insilencecount > ds->silence_samples) { pstracker->foundsil = TRUE; } } } signed int scale(mad_fixed_t sample) { /* round */ sample += (1L << (MAD_F_FRACBITS - 16)); /* clip */ if (sample >= MAD_F_ONE) sample = MAD_F_ONE - 1; else if (sample < -MAD_F_ONE) sample = -MAD_F_ONE; /* quantize */ return sample >> (MAD_F_FRACBITS + 1 - 16); } static enum mad_flow filter (void *data, struct mad_stream const *ms, struct mad_frame *frame) { DECODE_STRUCT *ds = (DECODE_STRUCT *)data; FRAME_LIST* fl; fl = (FRAME_LIST*) malloc (sizeof(FRAME_LIST)); fl->m_framepos = ms->this_frame; fl->m_samples = 0; fl->m_pcmpos = 0; list_add_tail (&(fl->m_list), &(ds->frame_list)); #if defined (commentout) debug_printf ("FILTER: %p (%02x%02x) | %p\n", ms->this_frame, ms->this_frame[0], ms->this_frame[1], ms->next_frame); #endif return MAD_FLOW_CONTINUE; } enum mad_flow output (void *data, struct mad_header const *header, struct mad_pcm *pcm) { DECODE_STRUCT *ds = (DECODE_STRUCT *)data; FRAME_LIST *fl; unsigned int nchannels, nsamples; mad_fixed_t const *left_ch, *right_ch; static short lastSample = 0; static signed int sample; double v; nchannels = pcm->channels; nsamples = pcm->length; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; /* Get frame entry */ fl = list_entry (ds->frame_list.prev, FRAME_LIST, m_list); fl->m_samples = nsamples; fl->m_pcmpos = ds->pcmpos; #if defined (commentout) if (ds->pcmpos > ds->len_to_sw_start_samp && ds->pcmpos < ds->len_to_sw_end_samp) { debug_printf ("*\n"); } else { debug_printf ("-\n"); } #endif while(nsamples--) { /* output sample(s) in 16-bit signed little-endian PCM */ /* GCS FIX: Does this work on big endian machines??? */ lastSample = sample; sample = (short) scale (*left_ch++); // fwrite(&sample, sizeof(short), 1, fp); if (nchannels == 2) { // make mono sample = (sample+scale(*right_ch++))/2; } // get the instantanous volume v = (lastSample*lastSample)+(sample*sample); v = sqrt(v / 2); if (ds->pcmpos > ds->len_to_sw_start_samp && ds->pcmpos < ds->len_to_sw_end_samp) { search_for_silence(ds, v); } ds->pcmpos++; } return MAD_FLOW_CONTINUE; } static enum mad_flow header(void *data, struct mad_header const *pheader) { DECODE_STRUCT *ds = (DECODE_STRUCT *)data; if (!ds->samplerate) { ds->samplerate = pheader->samplerate; ds->silence_samples = ds->silence_ms * (ds->samplerate/1000); ds->len_to_sw_start_samp = ds->len_to_sw_ms * (ds->samplerate/1000); ds->len_to_sw_end_samp = (ds->len_to_sw_ms + ds->searchwindow_ms) * (ds->samplerate/1000); debug_printf ("Setting samplerate: %ld\n",ds->samplerate); } return MAD_FLOW_CONTINUE; } enum mad_flow error(void *data, struct mad_stream *ms, struct mad_frame *frame) { if (MAD_RECOVERABLE(ms->error)) { debug_printf("mad error 0x%04x\n", ms->error); return MAD_FLOW_CONTINUE; } debug_printf("unrecoverable mad error 0x%04x\n", ms->error); return MAD_FLOW_BREAK; } /* The following routines have nothing to do with finding a separation * point. Instead, they have to do with finding the bitrate. However, * they are included here because they are "mad" related. */ error_code find_bitrate(unsigned long* bitrate, const u_char* mpgbuf, long mpgsize) { struct mad_decoder decoder; GET_BITRATE_STRUCT gbs; int result; /* initialize and start decoder */ gbs.mpgbuf = (unsigned char*) mpgbuf; gbs.mpgsize = mpgsize; gbs.bitrate = 0; mad_decoder_init(&decoder, &gbs, input_get_bitrate /* input */, header_get_bitrate /* header */, NULL /* filter */, NULL /* output */, NULL /* error */, NULL /* message */); result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); mad_decoder_finish(&decoder); *bitrate = gbs.bitrate; return SR_SUCCESS; } static enum mad_flow input_get_bitrate (void *data, struct mad_stream *stream) { GET_BITRATE_STRUCT* gbs = (GET_BITRATE_STRUCT*) data; if (!gbs->mpgsize) return MAD_FLOW_STOP; mad_stream_buffer(stream, gbs->mpgbuf, gbs->mpgsize); gbs->mpgsize = 0; return MAD_FLOW_CONTINUE; } static enum mad_flow header_get_bitrate (void *data, struct mad_header const *pheader) { GET_BITRATE_STRUCT* gbs = (GET_BITRATE_STRUCT*) data; gbs->bitrate = pheader->bitrate; /* stream bitrate (bps) */ debug_printf ("Decoded bitrate from stream: %ld\n", gbs->bitrate); return MAD_FLOW_STOP; }