int FLAC_plugin__seek(FLAC__StreamDecoder *decoder, stream_data_struct *stream_data) { int pos; FLAC__uint64 target_sample = stream_data->total_samples * stream_data->seek_to / stream_data->length_in_msec; if (stream_data->total_samples > 0 && target_sample >= stream_data->total_samples && target_sample > 0) target_sample = stream_data->total_samples - 1; /* even if the seek fails we have to reset these so that we don't repeat the seek */ stream_data->seek_to = -1; stream_data->eof = false; wide_samples_in_reservoir_ = 0; pos = (int)(target_sample*1000 / stream_data->sample_rate); if (!FLAC__stream_decoder_seek_absolute(decoder, target_sample)) { if(FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_SEEK_ERROR) FLAC__stream_decoder_flush(decoder); pos = -1; } bh_index_last_o = bh_index_last_w = (pos/BITRATE_HIST_SEGMENT_MSEC) % BITRATE_HIST_SIZE; if (!FLAC__stream_decoder_get_decode_position(decoder, &decode_position)) decode_position = 0; return pos; }
FLAC__bool populate_seekpoint_values(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write) { FLAC__StreamDecoder *decoder; ClientData client_data; FLAC__bool ok = true; FLAC__ASSERT(0 != block); FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_SEEKTABLE); client_data.seektable_template = &block->data.seek_table; client_data.samples_written = 0; /* client_data.audio_offset must be determined later */ client_data.first_seekpoint_to_check = 0; client_data.error_occurred = false; decoder = FLAC__stream_decoder_new(); if(0 == decoder) { fprintf(stderr, "%s: ERROR (--add-seekpoint) creating the decoder instance\n", filename); return false; } FLAC__stream_decoder_set_md5_checking(decoder, false); FLAC__stream_decoder_set_metadata_ignore_all(decoder); if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, /*metadata_callback=*/0, error_callback_, &client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { fprintf(stderr, "%s: ERROR (--add-seekpoint) initializing the decoder instance (%s)\n", filename, FLAC__stream_decoder_get_resolved_state_string(decoder)); ok = false; } if(ok && !FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file (%s)\n", filename, FLAC__stream_decoder_get_resolved_state_string(decoder)); ok = false; } if(ok && !FLAC__stream_decoder_get_decode_position(decoder, &client_data.audio_offset)) { fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file\n", filename); ok = false; } client_data.last_offset = client_data.audio_offset; if(ok && !FLAC__stream_decoder_process_until_end_of_stream(decoder)) { fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file (%s)\n", filename, FLAC__stream_decoder_get_resolved_state_string(decoder)); ok = false; } if(ok && client_data.error_occurred) { fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file (%u:%s)\n", filename, (unsigned)client_data.error_status, FLAC__StreamDecoderErrorStatusString[client_data.error_status]); ok = false; } *needs_write = true; FLAC__stream_decoder_delete(decoder); return ok; }
static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { ClientData *cd = (ClientData*)client_data; (void)buffer; FLAC__ASSERT(0 != cd); if(!cd->error_occurred) { const unsigned blocksize = frame->header.blocksize; const FLAC__uint64 frame_first_sample = cd->samples_written; const FLAC__uint64 frame_last_sample = frame_first_sample + (FLAC__uint64)blocksize - 1; FLAC__uint64 test_sample; unsigned i; for(i = cd->first_seekpoint_to_check; i < cd->seektable_template->num_points; i++) { test_sample = cd->seektable_template->points[i].sample_number; if(test_sample > frame_last_sample) { break; } else if(test_sample >= frame_first_sample) { cd->seektable_template->points[i].sample_number = frame_first_sample; cd->seektable_template->points[i].stream_offset = cd->last_offset - cd->audio_offset; cd->seektable_template->points[i].frame_samples = blocksize; cd->first_seekpoint_to_check++; /* DO NOT: "break;" and here's why: * The seektable template may contain more than one target * sample for any given frame; we will keep looping, generating * duplicate seekpoints for them, and we'll clean it up later, * just before writing the seektable back to the metadata. */ } else { cd->first_seekpoint_to_check++; } } cd->samples_written += blocksize; if(!FLAC__stream_decoder_get_decode_position(decoder, &cd->last_offset)) return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } else return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; }
static FLAC__StreamDecoderWriteStatus flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame, const FLAC__int32 *const buf[], void *vdata) { struct flac_data *data = (struct flac_data *) vdata; FLAC__uint64 nbytes = 0; if (FLAC__stream_decoder_get_decode_position(dec, &nbytes)) { if (data->position > 0 && nbytes > data->position) { nbytes -= data->position; data->position += nbytes; } else { data->position = nbytes; nbytes = 0; } } else nbytes = 0; return flac_common_write(data, frame, buf, nbytes); }
static size_t audiosourceflac_Position(struct audiosource* source) { struct audiosourceflac_internaldata* idata = source->internaldata; if (idata->eof && idata->returnerroroneof) { return 0; } if (!idata->flacopened) { return 0; } if (source->seekable) { uint64_t pos; if (FLAC__stream_decoder_get_decode_position( idata->decoder, &pos) == true) { return pos; } } return 0; }
CBaseDec::RetCode CFlacDec::Decoder(FILE *in, const int /*OutputFd*/, State* const state, CAudioMetaData* meta_data, time_t* const time_played, unsigned int* const secondsToSkip) { int rval; // FLAC__uint64 jumptime=0; Status = OK; mState = state; mTimePlayed = time_played; mFrameCount = 0; mSamplesProcessed = 0; struct stat s; FLAC__uint64 filesize = 0; mFlacDec = FLAC__stream_decoder_new(); if (!Open(in, mFlacDec)) { Status=DATA_ERR; return Status; } if (fstat(fileno(in), &s)) { perror("CFlacDec::Decoder fstat"); *time_played = 0; } else filesize = (FLAC__uint64)s.st_size; /* up and away ... */ mSlotSize = MAX_OUTPUT_SAMPLES * 2 * FLAC__stream_decoder_get_channels(mFlacDec); // State oldstate=*state; int jumppos=0; int actSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip : MSECS_TO_SKIP / 1000; int bytes_to_skip = actSecsToSkip * meta_data->bitrate / 8; int bytes_to_play = MSECS_TO_PLAY * meta_data->bitrate / 8000; unsigned int oldSecsToSkip = *secondsToSkip; FLAC__uint64 position; do { while(*state==PAUSE) usleep(10000); if(*state==FF || *state==REV) { if (oldSecsToSkip != *secondsToSkip) { actSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip : MSECS_TO_SKIP / 1000; bytes_to_skip = actSecsToSkip * meta_data->bitrate / 8; oldSecsToSkip = *secondsToSkip; } printf("skipping %d secs and %d bytes\n",actSecsToSkip,bytes_to_skip); if(std::abs(ftell(in)-jumppos) > bytes_to_play) { if(*state==FF) { fseek(in, bytes_to_skip, SEEK_CUR); jumppos=ftell(in); } else { if(ftell(in) < bytes_to_skip) { fseek(in, 0, SEEK_SET); *state=PLAY; } else { fseek(in, -bytes_to_skip, SEEK_CUR); jumppos=ftell(in); } } } // if a custom value was set we only jump once if (*secondsToSkip != 0) { *state=PLAY; } } if (FLAC__stream_decoder_get_state(mFlacDec) == FLAC__STREAM_DECODER_END_OF_STREAM) { rval = false; break; } rval = FLAC__stream_decoder_process_single(mFlacDec); /* update playback position */ if (filesize > 0 && FLAC__stream_decoder_get_decode_position(mFlacDec, &position)) *time_played = position / 1000ULL * mLengthInMsec / filesize; } while (rval && *state != STOP_REQ && Status == OK); // let buffer run dry printf("let buffer run dry\n"); while (rval == true && *state != STOP_REQ && Status == OK /* && mReadSlot != mWriteSlot*/) { printf("...drying - *state=%x, Status=%x\n", *state, Status); usleep(100000); } /* clean up the junk from the party */ if (mMetadata) FLAC__metadata_object_delete(mMetadata); mMetadata = NULL; FLAC__stream_decoder_finish(mFlacDec); FLAC__stream_decoder_delete(mFlacDec); mFlacDec = NULL; audioDecoder->StopClip(); /* and drive home ;) */ return Status; }
bool flac_reader_c::parse_file() { FLAC__StreamDecoderState state; flac_block_t block; uint64_t u, old_pos; int result, progress, old_progress; bool ok; m_in->setFilePointer(0); metadata_parsed = false; mxinfo(Y("+-> Parsing the FLAC file. This can take a LONG time.\n")); init_flac_decoder(); result = FLAC__stream_decoder_process_until_end_of_metadata(m_flac_decoder.get()); mxverb(2, boost::format("flac_reader: extract->metadata, result: %1%, mdp: %2%, num blocks: %3%\n") % result % metadata_parsed % blocks.size()); if (!metadata_parsed) mxerror_fn(m_ti.m_fname, Y("No metadata block found. This file is broken.\n")); FLAC__stream_decoder_get_decode_position(m_flac_decoder.get(), &u); block.type = FLAC_BLOCK_TYPE_HEADERS; block.filepos = 0; block.len = u; old_pos = u; blocks.push_back(block); mxverb(2, boost::format("flac_reader: headers: block at %1% with size %2%\n") % block.filepos % block.len); old_progress = -5; ok = FLAC__stream_decoder_skip_single_frame(m_flac_decoder.get()); while (ok) { state = FLAC__stream_decoder_get_state(m_flac_decoder.get()); progress = m_in->getFilePointer() * 100 / m_size; if ((progress - old_progress) >= 5) { mxinfo(boost::format(Y("+-> Pre-parsing FLAC file: %1%%%%2%")) % progress % "\r"); old_progress = progress; } if (FLAC__stream_decoder_get_decode_position(m_flac_decoder.get(), &u) && (u != old_pos)) { block.type = FLAC_BLOCK_TYPE_DATA; block.filepos = old_pos; block.len = u - old_pos; old_pos = u; blocks.push_back(block); mxverb(2, boost::format("flac_reader: skip/decode frame, block at %1% with size %2%\n") % block.filepos % block.len); } if (state > FLAC__STREAM_DECODER_READ_FRAME) break; ok = FLAC__stream_decoder_skip_single_frame(m_flac_decoder.get()); } if (100 != old_progress) mxinfo(Y("+-> Pre-parsing FLAC file: 100%\n")); else mxinfo("\n"); if ((blocks.size() == 0) || (blocks[0].type != FLAC_BLOCK_TYPE_HEADERS)) mxerror(Y("flac_reader: Could not read all header packets.\n")); m_in->setFilePointer(0); blocks[0].len -= 4; blocks[0].filepos = 4; return metadata_parsed; }
unsigned FLAC_plugin__decode(FLAC__StreamDecoder *decoder, stream_data_struct *stream_data, char *sample_buffer) { /* fill reservoir */ while (wide_samples_in_reservoir_ < SAMPLES_PER_WRITE) { if (FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_END_OF_STREAM) { stream_data->eof = true; break; } else if (!FLAC__stream_decoder_process_single(decoder)) { FLAC_plugin__show_error("Error while processing frame (%s).", FLAC__stream_decoder_get_resolved_state_string(decoder)); stream_data->eof = true; break; } if (!FLAC__stream_decoder_get_decode_position(decoder, &decode_position)) decode_position = 0; } /* output samples */ if (wide_samples_in_reservoir_ > 0) { const unsigned n = min(wide_samples_in_reservoir_, SAMPLES_PER_WRITE); const unsigned channels = stream_data->channels; unsigned i; int bytes; if (cfg.replaygain.enable && stream_data->has_replaygain) { bytes = FLAC__replaygain_synthesis__apply_gain( sample_buffer, true, /* little_endian_data_out */ stream_data->output_bits_per_sample == 8, /* unsigned_data_out */ reservoir__, n, channels, stream_data->bits_per_sample, stream_data->output_bits_per_sample, stream_data->replay_scale, cfg.replaygain.hard_limit, cfg.resolution.replaygain.dither, &stream_data->dither_context ); } else { bytes = FLAC__plugin_common__pack_pcm_signed_little_endian( sample_buffer, reservoir__, n, channels, stream_data->bits_per_sample, stream_data->output_bits_per_sample ); } wide_samples_in_reservoir_ -= n; for (i = 0; i < channels; i++) memmove(&reservoir_[i][0], &reservoir_[i][n], sizeof(reservoir_[0][0]) * wide_samples_in_reservoir_); return bytes; } else { stream_data->eof = true; return 0; } }
void *play_loop_(void *arg) { unsigned written_time_last = 0, bh_index_last_w = 0, bh_index_last_o = BITRATE_HIST_SIZE, blocksize = 1; FLAC__uint64 decode_position_last = 0, decode_position_frame_last = 0, decode_position_frame = 0; (void)arg; while(stream_data_.is_playing) { if(!stream_data_.eof) { while(sample_buffer_last_ - sample_buffer_first_ < SAMPLES_PER_WRITE) { unsigned s; s = sample_buffer_last_ - sample_buffer_first_; if(FLAC__stream_decoder_get_state(decoder_) == FLAC__STREAM_DECODER_END_OF_STREAM) { stream_data_.eof = true; break; } else if(!FLAC__stream_decoder_process_single(decoder_)) { /*@@@ this should probably be a dialog */ fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n"); stream_data_.eof = true; break; } blocksize = sample_buffer_last_ - sample_buffer_first_ - s; decode_position_frame_last = decode_position_frame; if(stream_data_.is_http_source || !FLAC__stream_decoder_get_decode_position(decoder_, &decode_position_frame)) decode_position_frame = 0; } if(sample_buffer_last_ - sample_buffer_first_ > 0) { const unsigned n = min(sample_buffer_last_ - sample_buffer_first_, SAMPLES_PER_WRITE); int bytes = n * stream_data_.channels * stream_data_.sample_format_bytes_per_sample; FLAC__byte *sample_buffer_start = sample_buffer_ + sample_buffer_first_ * stream_data_.channels * stream_data_.sample_format_bytes_per_sample; unsigned written_time, bh_index_w; FLAC__uint64 decode_position; sample_buffer_first_ += n; flac_ip.add_vis_pcm(flac_ip.output->written_time(), stream_data_.sample_format, stream_data_.channels, bytes, sample_buffer_start); while(flac_ip.output->buffer_free() < (int)bytes && stream_data_.is_playing && stream_data_.seek_to_in_sec == -1) xmms_usleep(10000); if(stream_data_.is_playing && stream_data_.seek_to_in_sec == -1) flac_ip.output->write_audio(sample_buffer_start, bytes); /* compute current bitrate */ written_time = flac_ip.output->written_time(); bh_index_w = written_time / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; if(bh_index_w != bh_index_last_w) { bh_index_last_w = bh_index_w; decode_position = decode_position_frame - (double)(sample_buffer_last_ - sample_buffer_first_) * (double)(decode_position_frame - decode_position_frame_last) / (double)blocksize; bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE - 1) % BITRATE_HIST_SIZE] = decode_position > decode_position_last && written_time > written_time_last ? 8000 * (decode_position - decode_position_last) / (written_time - written_time_last) : stream_data_.sample_rate * stream_data_.channels * stream_data_.bits_per_sample; decode_position_last = decode_position; written_time_last = written_time; } } else { stream_data_.eof = true; xmms_usleep(10000); } } else xmms_usleep(10000); if(!stream_data_.is_http_source && stream_data_.seek_to_in_sec != -1) { const double distance = (double)stream_data_.seek_to_in_sec * 1000.0 / (double)stream_data_.length_in_msec; FLAC__uint64 target_sample = (FLAC__uint64)(distance * (double)stream_data_.total_samples); if(stream_data_.total_samples > 0 && target_sample >= stream_data_.total_samples) target_sample = stream_data_.total_samples - 1; if(FLAC__stream_decoder_seek_absolute(decoder_, target_sample)) { flac_ip.output->flush(stream_data_.seek_to_in_sec * 1000); bh_index_last_w = bh_index_last_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; if(!FLAC__stream_decoder_get_decode_position(decoder_, &decode_position_frame)) decode_position_frame = 0; stream_data_.eof = false; sample_buffer_first_ = sample_buffer_last_ = 0; } else if(FLAC__stream_decoder_get_state(decoder_) == FLAC__STREAM_DECODER_SEEK_ERROR) { /*@@@ this should probably be a dialog */ fprintf(stderr, "libxmms-flac: SEEK ERROR\n"); FLAC__stream_decoder_flush(decoder_); stream_data_.eof = false; sample_buffer_first_ = sample_buffer_last_ = 0; } stream_data_.seek_to_in_sec = -1; } else { /* display the right bitrate from history */ unsigned bh_index_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; if(bh_index_o != bh_index_last_o && bh_index_o != bh_index_last_w && bh_index_o != (bh_index_last_w + 1) % BITRATE_HIST_SIZE) { bh_index_last_o = bh_index_o; flac_ip.set_info(stream_data_.title, stream_data_.length_in_msec, bitrate_history_[bh_index_o], stream_data_.sample_rate, stream_data_.channels); } } } safe_decoder_finish_(decoder_); /* are these two calls necessary? */ flac_ip.output->buffer_free(); flac_ip.output->buffer_free(); g_free(stream_data_.title); pthread_exit(NULL); return 0; /* to silence the compiler warning about not returning a value */ }