Beispiel #1
0
static gint
xmms_mpc_read (xmms_xform_t *xform, xmms_sample_t *buffer,
               gint len, xmms_error_t *err)
{
    MPC_SAMPLE_FORMAT internal[MPC_DECODER_BUFFER_LENGTH];
    xmms_mpc_data_t *data;
    mpc_uint32_t ret;
    guint size;

    data = xmms_xform_private_data_get (xform);

    size = MIN (data->buffer->len, len);

#ifdef HAVE_MPCDEC_OLD
    if (size <= 0) {
        ret = mpc_decoder_decode (&data->decoder, internal, NULL, NULL);
        if (ret == -1) {
            xmms_error_set (err, XMMS_ERROR_GENERIC, "Musepack decoder failed");
            return -1;
        }

        ret *= xmms_sample_size_get (XMMS_SAMPLE_FORMAT_FLOAT);
        ret *= data->info.channels;

        g_string_append_len (data->buffer, (gchar *) internal, ret);
    }
#else
    if (size <= 0) {
        mpc_frame_info frame;

        frame.buffer = internal;
        do {
            ret = mpc_demux_decode (data->demux, &frame);
        } while (frame.bits != -1 && frame.samples == 0);

        if (frame.bits == -1 && ret != MPC_STATUS_OK) {
            xmms_error_set (err, XMMS_ERROR_GENERIC, "Musepack decoder failed");
            return -1;
        }
        ret = frame.samples;

        ret *= xmms_sample_size_get (XMMS_SAMPLE_FORMAT_FLOAT);
        ret *= data->info.channels;

        g_string_append_len (data->buffer, (gchar *) internal, ret);
    }
#endif

    /* Update the current size of available data */
    size = MIN (data->buffer->len, len);

    memcpy (buffer, data->buffer->str, size);
    g_string_erase (data->buffer, 0, size);

    return size;
}
 int64 Read_(int16 *buffer, int64 frames)
 {
  mpc_status err;
  int16 *cowbuf = (int16 *)buffer;
  int32 toread = frames * 2;

  while(toread > 0)
  {
   int32 tmplen;

   if(!MPCBufferIn)
   {
    mpc_frame_info fi;
    memset(&fi, 0, sizeof(fi));

    fi.buffer = MPCBuffer;
    if((err = mpc_demux_decode(demux, &fi)) < 0 || fi.bits == -1)
     return(frames - toread / 2);

    MPCBufferIn = fi.samples * 2;
    MPCBufferOffs = 0;
   }

   tmplen = MPCBufferIn;

   if(tmplen >= toread)
    tmplen = toread;

   for(int x = 0; x < tmplen; x++)
   {
#ifdef MPC_FIXED_POINT
    int32 samp = MPCBuffer[MPCBufferOffs + x] >> MPC_FIXED_POINT_FRACTPART;
#else
    #warning Floating-point MPC decoding path not tested.
    int32 samp = (int32)(MPCBuffer[MPCBufferOffs + x] * 32767);
#endif
    if(samp < -32768)
     samp = -32768;

    if(samp > 32767)
     samp = 32767;

    *cowbuf = (int16)samp;
    cowbuf++;
   }
      
   MPCBufferOffs += tmplen;
   toread -= tmplen;
   MPCBufferIn -= tmplen;
  }

  return(frames - toread / 2);
 }
Beispiel #3
0
void AudioStreamMPC::update() {

	if (!active || paused)
		return;

	int todo=get_todo();

	while(todo>MPC_DECODER_BUFFER_LENGTH/si.channels) {

		mpc_frame_info frame;

		frame.buffer=sample_buffer;

		mpc_status err = mpc_demux_decode(demux, &frame);
		if (frame.bits!=-1) {

			int16_t *dst_buff = get_write_buffer();

#ifdef MPC_FIXED_POINT

			for( int i = 0; i < frame.samples * si.channels; i++) {
				int tmp = sample_buffer[i] >> MPC_FIXED_POINT_FRACTPART;
				if (tmp > ((1 << 15) - 1)) tmp = ((1 << 15) - 1);
				if (tmp < -(1 << 15)) tmp = -(1 << 15);
				dst_buff[i] = tmp;
			}
#else
			for( int i = 0; i < frame.samples * si.channels; i++) {

				int tmp = Math::fast_ftoi(sample_buffer[i]*32767.0);
				if (tmp > ((1 << 15) - 1)) tmp = ((1 << 15) - 1);
				if (tmp < -(1 << 15)) tmp = -(1 << 15);
				dst_buff[i] = tmp;

			}

#endif

			int frames = frame.samples;
			write(frames);
			todo-=frames;
		} else {

			if (err != MPC_STATUS_OK) {
static void
gst_musepackdec_loop (GstPad * sinkpad)
{
  GstMusepackDec *musepackdec;
  GstFlowReturn flow;
  GstBuffer *out;
  GstMapInfo info;
  mpc_frame_info frame;
  mpc_status err;
  gint num_samples, samplerate, bitspersample;

  musepackdec = GST_MUSEPACK_DEC (GST_PAD_PARENT (sinkpad));

  samplerate = g_atomic_int_get (&musepackdec->rate);

  if (samplerate == 0) {
    if (!gst_musepack_stream_init (musepackdec))
      goto pause_task;

    gst_musepackdec_send_newsegment (musepackdec);
    samplerate = g_atomic_int_get (&musepackdec->rate);
  }

  bitspersample = g_atomic_int_get (&musepackdec->bps);

  out = gst_buffer_new_allocate (NULL, MPC_DECODER_BUFFER_LENGTH * 4, NULL);

  gst_buffer_map (out, &info, GST_MAP_READWRITE);
  frame.buffer = (MPC_SAMPLE_FORMAT *) info.data;
  err = mpc_demux_decode (musepackdec->d, &frame);
  gst_buffer_unmap (out, &info);

  if (err != MPC_STATUS_OK) {
    GST_ERROR_OBJECT (musepackdec, "Failed to decode sample");
    GST_ELEMENT_ERROR (musepackdec, STREAM, DECODE, (NULL), (NULL));
    goto pause_task;
  } else if (frame.bits == -1) {
    goto eos_and_pause;
  }

  num_samples = frame.samples;

  gst_buffer_set_size (out, num_samples * bitspersample);

  GST_BUFFER_OFFSET (out) = musepackdec->segment.position;
  GST_BUFFER_PTS (out) =
      gst_util_uint64_scale_int (musepackdec->segment.position,
      GST_SECOND, samplerate);
  GST_BUFFER_DURATION (out) =
      gst_util_uint64_scale_int (num_samples, GST_SECOND, samplerate);

  musepackdec->segment.position += num_samples;

  GST_LOG_OBJECT (musepackdec, "Pushing buffer, timestamp %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out)));

  flow = gst_pad_push (musepackdec->srcpad, out);
  if (flow != GST_FLOW_OK) {
    GST_DEBUG_OBJECT (musepackdec, "Flow: %s", gst_flow_get_name (flow));
    goto pause_task;
  }

  /* check if we're at the end of a configured segment */
  if (musepackdec->segment.stop != -1 &&
      musepackdec->segment.position >= musepackdec->segment.stop) {
    gint64 stop_time;

    GST_DEBUG_OBJECT (musepackdec, "Reached end of configured segment");

    if ((musepackdec->segment.flags & GST_SEEK_FLAG_SEGMENT) == 0)
      goto eos_and_pause;

    GST_DEBUG_OBJECT (musepackdec, "Posting SEGMENT_DONE message");

    stop_time = gst_util_uint64_scale_int (musepackdec->segment.stop,
        GST_SECOND, samplerate);

    gst_element_post_message (GST_ELEMENT (musepackdec),
        gst_message_new_segment_done (GST_OBJECT (musepackdec),
            GST_FORMAT_TIME, stop_time));
    gst_pad_push_event (musepackdec->srcpad,
        gst_event_new_segment_done (GST_FORMAT_TIME, stop_time));

    goto pause_task;
  }

  return;

eos_and_pause:
  {
    GST_DEBUG_OBJECT (musepackdec, "sending EOS event");
    gst_pad_push_event (musepackdec->srcpad, gst_event_new_eos ());
    /* fall through to pause */
  }

pause_task:
  {
    GST_DEBUG_OBJECT (musepackdec, "Pausing task");
    gst_pad_pause_task (sinkpad);
    return;
  }
}
UInt32 SFB::Audio::MusepackDecoder::_ReadAudio(AudioBufferList *bufferList, UInt32 frameCount)
{
	if(bufferList->mNumberBuffers != mFormat.mChannelsPerFrame) {
		LOGGER_WARNING("org.sbooth.AudioEngine.Decoder.Musepack", "_ReadAudio() called with invalid parameters");
		return 0;
	}

	MPC_SAMPLE_FORMAT	buffer			[MPC_DECODER_BUFFER_LENGTH];
	UInt32				framesRead		= 0;
	
	// Reset output buffer data size
	for(UInt32 i = 0; i < bufferList->mNumberBuffers; ++i)
		bufferList->mBuffers[i].mDataByteSize = 0;
	
	for(;;) {
		UInt32	framesRemaining	= frameCount - framesRead;
		UInt32	framesToSkip	= (UInt32)(bufferList->mBuffers[0].mDataByteSize / sizeof(float));
		UInt32	framesInBuffer	= (UInt32)(mBufferList->mBuffers[0].mDataByteSize / sizeof(float));
		UInt32	framesToCopy	= std::min(framesInBuffer, framesRemaining);
		
		// Copy data from the buffer to output
		for(UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i) {
			float *floatBuffer = (float *)bufferList->mBuffers[i].mData;
			memcpy(floatBuffer + framesToSkip, mBufferList->mBuffers[i].mData, framesToCopy * sizeof(float));
			bufferList->mBuffers[i].mDataByteSize += framesToCopy * sizeof(float);
			
			// Move remaining data in buffer to beginning
			if(framesToCopy != framesInBuffer) {
				floatBuffer = (float *)mBufferList->mBuffers[i].mData;
				memmove(floatBuffer, floatBuffer + framesToCopy, (framesInBuffer - framesToCopy) * sizeof(float));
			}
			
			mBufferList->mBuffers[i].mDataByteSize -= framesToCopy * sizeof(float);
		}
		
		framesRead += framesToCopy;
		
		// All requested frames were read
		if(framesRead == frameCount)
			break;
		
		// Decode one frame of MPC data
		mpc_frame_info frame;
		frame.buffer = buffer;

		mpc_status result = mpc_demux_decode(mDemux, &frame);
		if(MPC_STATUS_OK != result) {
			LOGGER_ERR("org.sbooth.AudioEngine.Decoder.Musepack", "Musepack decoding error");
			break;
		}

		// End of input
		if(-1 == frame.bits)
			break;
		
#ifdef MPC_FIXED_POINT
#error "Fixed point not yet supported"
#else
		float *inputBuffer = (float *)buffer;

		// Clip the samples to [-1, 1)
		float minValue = -1.f;
		float maxValue = 8388607.f / 8388608.f;

		vDSP_vclip(inputBuffer, 1, &minValue, &maxValue, inputBuffer, 1, frame.samples * mFormat.mChannelsPerFrame);

		// Deinterleave the normalized samples
		for(UInt32 channel = 0; channel < mFormat.mChannelsPerFrame; ++channel) {
			float *floatBuffer = (float *)mBufferList->mBuffers[channel].mData;

			for(UInt32 sample = channel; sample < frame.samples * mFormat.mChannelsPerFrame; sample += mFormat.mChannelsPerFrame)
				*floatBuffer++ = inputBuffer[sample];
			
			mBufferList->mBuffers[channel].mNumberChannels	= 1;
			mBufferList->mBuffers[channel].mDataByteSize	= frame.samples * sizeof(float);
		}
#endif /* MPC_FIXED_POINT */		
	}
	
	mCurrentFrame += framesRead;
	
	return framesRead;
}
Beispiel #6
0
/* This is the codec entry point. */
enum codec_status codec_main(void)
{
    mpc_int64_t samplesdone;
    uint32_t frequency;     /* 0.1 kHz accuracy */
    uint32_t elapsed_time;  /* milliseconds */
    uint32_t byterate;      /* bytes per second */
    mpc_status status;
    mpc_reader reader;
    mpc_streaminfo info;
    mpc_frame_info frame;
    mpc_demux *demux = NULL;
    int retval = CODEC_OK;
    
    frame.buffer = sample_buffer;
    
    /* musepack's sample representation is 18.14
     * DSP_SET_SAMPLE_DEPTH = 14 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 29 */
    ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
    
    /* Create a decoder instance */
    reader.read     = read_impl;
    reader.seek     = seek_impl;
    reader.tell     = tell_impl;
    reader.get_size = get_size_impl;

next_track:    
    if (codec_init()) 
    {
        retval = CODEC_ERROR;
        goto exit;
    }

    while (!*ci->taginfo_ready && !ci->stop_codec)
        ci->sleep(1);

    /* Initialize demux/decoder. */
    demux = mpc_demux_init(&reader);
    if (NULL == demux)
    {
        retval = CODEC_ERROR;
        goto done;
    }
    /* Read file's streaminfo data. */
    mpc_demux_get_info(demux, &info);
    
    byterate  = (mpc_uint32_t)(info.average_bitrate) / 8;
    frequency = info.sample_freq / 100; /* 0.1 kHz accuracy */
    ci->configure(DSP_SWITCH_FREQUENCY, info.sample_freq);

    /* Remark: rockbox offset is the file offset in bytes. So, estimate the 
     * sample seek position from the file offset, the sampling frequency and
     * the bitrate. As the saved position is exactly calculated the reverse way 
     * there is no loss of information except rounding. */
    samplesdone = 100 * ((mpc_uint64_t)(ci->id3->offset * frequency) / byterate);
        
    /* Set up digital signal processing for correct number of channels */
    /* NOTE: current musepack format only allows for stereo files
       but code is here to handle other configurations anyway */
    if      (info.channels == 2)
        ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
    else if (info.channels == 1)
        ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
    else 
    {
       retval = CODEC_ERROR;
       goto done;
    }
    
    codec_set_replaygain(ci->id3);

    /* Resume to saved sample offset. */
    if (samplesdone > 0) 
    {
        if (mpc_demux_seek_sample(demux, samplesdone) == MPC_STATUS_OK) 
        {
            elapsed_time = (samplesdone*10)/frequency;
            ci->set_elapsed(elapsed_time);
        } 
        else 
        {
            samplesdone = 0;
        }
    }

    /* This is the decoding loop. */
    do 
    {
        /* Complete seek handler. */
        if (ci->seek_time) 
        {
            mpc_int64_t new_offset = ((ci->seek_time - 1)/10)*frequency;
            if (mpc_demux_seek_sample(demux, new_offset) == MPC_STATUS_OK) 
            {
                samplesdone = new_offset;
                ci->set_elapsed(ci->seek_time);
            }
            ci->seek_complete();
        }
        
        /* Stop or skip occured, exit decoding loop. */
        if (ci->stop_codec || ci->new_track)
            break;

        /* Decode one frame. */
        status = mpc_demux_decode(demux, &frame);
        ci->yield();
        if (frame.bits == -1)
        {
            /* Decoding error, exit decoding loop. */
            retval = (status == MPC_STATUS_OK) ? CODEC_OK : CODEC_ERROR;
            goto done;
        } 
        else 
        {
            /* Decoding passed, insert samples to PCM buffer. */
            ci->pcmbuf_insert(frame.buffer,
                              frame.buffer + MPC_FRAME_LENGTH,
                              frame.samples);
            samplesdone += frame.samples;
            elapsed_time = (samplesdone*10)/frequency;
            ci->set_elapsed(elapsed_time);
            /* Remark: rockbox offset is the file offset in bytes. So estimate 
             * this offset from the samples, sampling frequency and bitrate */
            ci->set_offset( (samplesdone * byterate)/(frequency*100) );
        }
    } while (true);

done:
    if (ci->request_next_track())
        goto next_track;

exit:
    return retval;
}
static void
gst_musepackdec_loop (GstPad * sinkpad)
{
    GstMusepackDec *musepackdec;
    GstFlowReturn flow;
    GstBuffer *out;

#ifdef MPC_IS_OLD_API
    guint32 update_acc, update_bits;
#else
    mpc_frame_info frame;
    mpc_status err;
#endif
    gint num_samples, samplerate, bitspersample;

    musepackdec = GST_MUSEPACK_DEC (GST_PAD_PARENT (sinkpad));

    samplerate = g_atomic_int_get (&musepackdec->rate);

    if (samplerate == 0) {
        if (!gst_musepack_stream_init (musepackdec))
            goto pause_task;

        gst_musepackdec_send_newsegment (musepackdec);
        samplerate = g_atomic_int_get (&musepackdec->rate);
    }

    bitspersample = g_atomic_int_get (&musepackdec->bps);

    flow = gst_pad_alloc_buffer_and_set_caps (musepackdec->srcpad, -1,
            MPC_DECODER_BUFFER_LENGTH * 4, GST_PAD_CAPS (musepackdec->srcpad), &out);

    if (flow != GST_FLOW_OK) {
        GST_DEBUG_OBJECT (musepackdec, "Flow: %s", gst_flow_get_name (flow));
        goto pause_task;
    }
#ifdef MPC_IS_OLD_API
    num_samples = mpc_decoder_decode (musepackdec->d,
                                      (MPC_SAMPLE_FORMAT *) GST_BUFFER_DATA (out), &update_acc, &update_bits);

    if (num_samples < 0) {
        GST_ERROR_OBJECT (musepackdec, "Failed to decode sample");
        GST_ELEMENT_ERROR (musepackdec, STREAM, DECODE, (NULL), (NULL));
        goto pause_task;
    } else if (num_samples == 0) {
        goto eos_and_pause;
    }
#else
    frame.buffer = (MPC_SAMPLE_FORMAT *) GST_BUFFER_DATA (out);
    err = mpc_demux_decode (musepackdec->d, &frame);

    if (err != MPC_STATUS_OK) {
        GST_ERROR_OBJECT (musepackdec, "Failed to decode sample");
        GST_ELEMENT_ERROR (musepackdec, STREAM, DECODE, (NULL), (NULL));
        goto pause_task;
    } else if (frame.bits == -1) {
        goto eos_and_pause;
    }

    num_samples = frame.samples;
#endif

    GST_BUFFER_SIZE (out) = num_samples * bitspersample;

    GST_BUFFER_OFFSET (out) = musepackdec->segment.last_stop;
    GST_BUFFER_TIMESTAMP (out) =
        gst_util_uint64_scale_int (musepackdec->segment.last_stop,
                                   GST_SECOND, samplerate);
    GST_BUFFER_DURATION (out) =
        gst_util_uint64_scale_int (num_samples, GST_SECOND, samplerate);

    musepackdec->segment.last_stop += num_samples;

    GST_LOG_OBJECT (musepackdec, "Pushing buffer, timestamp %" GST_TIME_FORMAT,
                    GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (out)));

    flow = gst_pad_push (musepackdec->srcpad, out);
    if (flow != GST_FLOW_OK) {
        GST_DEBUG_OBJECT (musepackdec, "Flow: %s", gst_flow_get_name (flow));
        goto pause_task;
    }

    /* check if we're at the end of a configured segment */
    if (musepackdec->segment.stop != -1 &&
            musepackdec->segment.last_stop >= musepackdec->segment.stop) {
        gint64 stop_time;

        GST_DEBUG_OBJECT (musepackdec, "Reached end of configured segment");

        if ((musepackdec->segment.flags & GST_SEEK_FLAG_SEGMENT) == 0)
            goto eos_and_pause;

        GST_DEBUG_OBJECT (musepackdec, "Posting SEGMENT_DONE message");

        stop_time = gst_util_uint64_scale_int (musepackdec->segment.stop,
                                               GST_SECOND, samplerate);

        gst_element_post_message (GST_ELEMENT (musepackdec),
                                  gst_message_new_segment_done (GST_OBJECT (musepackdec),
                                          GST_FORMAT_TIME, stop_time));

        goto pause_task;
    }

    return;

eos_and_pause:
    {
        GST_DEBUG_OBJECT (musepackdec, "sending EOS event");
        gst_pad_push_event (musepackdec->srcpad, gst_event_new_eos ());
        /* fall through to pause */
    }

pause_task:
    {
        GST_DEBUG_OBJECT (musepackdec, "Pausing task");
        gst_pad_pause_task (sinkpad);
        return;
    }
}
Beispiel #8
0
int main(int argc, char **argv)
{
	MPC_SAMPLE_FORMAT album_max = 0;
	mpc_uint16_t album_gain;
	mpc_uint16_t album_peak;
	mpc_uint16_t * title_gain;
	mpc_uint16_t * title_peak;
	mpc_uint32_t * header_pos;
	int j;

	printf(About);

	if (argc < 2) {
		usage(argv[0]);
		return 0;
	}

	title_gain = malloc((sizeof(mpc_uint16_t) * 2 + sizeof(mpc_uint32_t)) * (argc - 1));
	title_peak = title_gain + (argc - 1);
	header_pos = (mpc_uint32_t *) (title_peak + (argc - 1));

	for (j = 1; j < argc; j++) {
		MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
		MPC_SAMPLE_FORMAT title_max = 0, chap_max;
		mpc_uint16_t * chap_gain, * chap_peak;
		mpc_reader reader;
		mpc_demux* demux;
		mpc_streaminfo si;
		mpc_status err;
		int chap_nb, chap = 0;
		mpc_uint64_t cur_sample = 1, next_chap_sample = mpc_int64_max;

		err = mpc_reader_init_stdio(&reader, argv[j]);
		if (err < 0) return !MPC_STATUS_OK;

		demux = mpc_demux_init(&reader);
		if (!demux) return !MPC_STATUS_OK;
		mpc_demux_get_info(demux,  &si);

		chap_nb = mpc_demux_chap_nb(demux);
		mpc_demux_seek_sample(demux, 0);
		if (chap_nb > 0) {
			mpc_chap_info * chap_info = mpc_demux_chap(demux, chap);
			next_chap_sample = chap_info->sample;
			chap_gain = malloc(sizeof(mpc_uint16_t) * 2 * chap_nb);
			chap_peak = chap_gain + chap_nb;
		}

		if (j == 1) gain_init_analysis ( si.sample_freq );

		while (1) {
			mpc_frame_info frame;
			int i = 0;

			frame.buffer = sample_buffer;
			mpc_demux_decode(demux, &frame);
			if (frame.bits == -1) break;

			while (next_chap_sample < cur_sample + frame.samples) {
				int sample_nb = (int)(next_chap_sample - cur_sample);

				chap_max = _max(chap_max, analyze_get_max(sample_buffer + 2 * i, sample_nb));

				if (chap == 0) // first samples are not in a chapter
					gain_get_chapter();
				else {
					chap_gain[chap - 1] = (mpc_uint16_t) (gain_get_chapter() * 256);
					chap_peak[chap - 1] = (mpc_uint16_t) (log10(chap_max * (1 << 15)) * 20 * 256);
				}
				chap++;
				title_max = _max(title_max, chap_max);
				chap_max = 0;
				i += sample_nb;
				cur_sample = next_chap_sample;
				if (chap < chap_nb) {
					mpc_chap_info * chap_info = mpc_demux_chap(demux, chap);
					next_chap_sample = chap_info->sample;
				} else
					next_chap_sample = mpc_int64_max;
			}

			chap_max = _max(chap_max, analyze_get_max(sample_buffer + 2 * i, frame.samples - i));
			cur_sample += frame.samples - i;
		}

		if (chap_nb > 0) {
			chap_gain[chap - 1] = (mpc_uint16_t) (gain_get_chapter() * 256);
			chap_peak[chap - 1] = (mpc_uint16_t) (log10(chap_max * (1 << 15)) * 20 * 256);
			write_chaps_gain(demux, argv[j], chap_gain, chap_peak);
		}

		title_max = _max(title_max, chap_max);
		album_max = _max(album_max, title_max);

		title_gain[j-1] = (mpc_uint16_t) (gain_get_title() * 256);
		title_peak[j-1] = (mpc_uint16_t) (log10(title_max * (1 << 15)) * 20 * 256);
		header_pos[j-1] = si.header_position + 4;

		mpc_demux_exit(demux);
		mpc_reader_exit_stdio(&reader);
		if (chap_nb > 0)
			free(chap_gain);
	}

	album_gain = (mpc_uint16_t) (gain_get_album() * 256);
	album_peak = (mpc_uint16_t) (log10(album_max * (1 << 15)) * 20 * 256);

	for (j = 0; j < argc - 1; j++) {
		unsigned char buffer[64];
		mpc_bits_reader r;
		mpc_block b;
		mpc_uint64_t size;
		FILE * file;

		file = fopen( argv[j + 1], "r+b");
		if (file == 0) {
			fprintf(stderr, "Can't open file \"%s\" for writing\n", argv[j + 1]);
			continue;
		}
		fseek(file, header_pos[j] - 4, SEEK_SET);
		fread(buffer, 1, 16, file);
		if (memcmp(buffer, "MPCK", 4) != 0) {
			fprintf(stderr, "Unsupported file format, not a sv8 file : %s\n", argv[j + 1]);
			fclose(file);
			continue;
		}
		r.buff = buffer + 4;
		r.count = 8;

		for(;;) {
			size = mpc_bits_get_block(&r, &b);
			if (mpc_check_key(b.key) != MPC_STATUS_OK) break;

			if (memcmp(b.key, "RG", 2) == 0) break;
			header_pos[j] += b.size + size;
			fseek(file, header_pos[j], SEEK_SET);
			fread(buffer, 1, 16, file);
			r.buff = buffer;
			r.count = 8;
		}

		if (memcmp(b.key, "RG", 2) != 0 || b.size < 9) { //check for the loop above having aborted without finding the packet we want to update
			fprintf(stderr, "Unsupported file format or corrupted file : %s\n", argv[j + 1]);
			fclose(file);
			continue;
		}
		header_pos[j] += size;

		buffer[size] = 1; // replaygain version
		buffer[size + 1] = title_gain[j] >> 8;
		buffer[size + 2] = title_gain[j] & 0xFF;
		buffer[size + 3] = title_peak[j] >> 8;
		buffer[size + 4] = title_peak[j] & 0xFF;
		buffer[size + 5] = album_gain >> 8;
		buffer[size + 6] = album_gain & 0xFF;
		buffer[size + 7] = album_peak >> 8;
		buffer[size + 8] = album_peak & 0xFF;

		fseek(file, header_pos[j], SEEK_SET);
		fwrite(buffer + size, 1, b.size, file);
		fclose(file);
	}

	free(title_gain);

    return 0;
}