1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-25 16:48:44 +02:00
audacity/src/ondemand/ODDecodeFlacTask.cpp

322 lines
9.1 KiB
C++

/*
* ODDecodeFlacTask.cpp
* Audacity
*
* Created by apple on 8/10/08.
* Copyright 2008 __MyCompanyName__. All rights reserved.
*
*/
#include "ODDecodeFlacTask.h"
#include "../Prefs.h"
#include <wx/string.h>
#include <wx/utils.h>
#include <wx/file.h>
#include <wx/ffile.h>
#ifdef USE_LIBID3TAG
extern "C" {
#include <id3tag.h>
}
#endif
#include "../Tags.h"
#define FLAC_HEADER "fLaC"
#define DESC _("FLAC files")
ODDecodeFlacTask::~ODDecodeFlacTask()
{
}
ODTask* ODDecodeFlacTask::Clone()
{
ODDecodeFlacTask* clone = new ODDecodeFlacTask;
clone->mDemandSample=GetDemandSample();
//the decoders and blockfiles should not be copied. They are created as the task runs.
return clone;
}
void ODFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata)
{
switch (metadata->type)
{
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) {
mComments.Add(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry));
}
break;
case FLAC__METADATA_TYPE_STREAMINFO:
mDecoder->mSampleRate=metadata->data.stream_info.sample_rate;
mDecoder->mNumChannels=metadata->data.stream_info.channels;
mDecoder->mBitsPerSample=metadata->data.stream_info.bits_per_sample;
mDecoder->mNumSamples=metadata->data.stream_info.total_samples;
if (mDecoder->mBitsPerSample<=16) {
if (mDecoder->mFormat<int16Sample) {
mDecoder->mFormat=int16Sample;
}
} else if (mDecoder->mBitsPerSample<=24) {
if (mDecoder->mFormat<int24Sample) {
mDecoder->mFormat=int24Sample;
}
} else {
mDecoder->mFormat=floatSample;
}
mDecoder->mStreamInfoDone=true;
break;
// handle the other types we do nothing with to avoid a warning
case FLAC__METADATA_TYPE_PADDING: // do nothing with padding
case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this
case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here
case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels?
case FLAC__METADATA_TYPE_PICTURE: // ignore pictures
case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either
break;
}
}
void ODFLACFile::error_callback(FLAC__StreamDecoderErrorStatus status)
{
mWasError = true;
switch (status)
{
case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
printf("Flac Error: Lost sync\n");
break;
case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
printf("Flac Error: Crc mismatch\n");
break;
case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
printf("Flac Error: Bad Header\n");
break;
default:
printf("Flac Error: Unknown error code\n");
break;
}
}
//the inside of the read loop.
FLAC__StreamDecoderWriteStatus ODFLACFile::write_callback(const FLAC__Frame *frame,
const FLAC__int32 * const buffer[])
{
unsigned int bytesToCopy = frame->header.blocksize;
if(bytesToCopy>mDecoder->mDecodeBufferLen-mDecoder->mDecodeBufferWritePosition)
bytesToCopy=mDecoder->mDecodeBufferLen-mDecoder->mDecodeBufferWritePosition;
//the decodeBuffer was allocated to be the same format as the flac buffer, so we can do a straight up memcpy.
memcpy(mDecoder->mDecodeBuffer+SAMPLE_SIZE(mDecoder->mFormat)*mDecoder->mDecodeBufferWritePosition,buffer[mDecoder->mTargetChannel],SAMPLE_SIZE(mDecoder->mFormat) * bytesToCopy);
mDecoder->mDecodeBufferWritePosition+=bytesToCopy;
/*
short *tmp=new short[frame->header.blocksize];
for (unsigned int chn=0; chn<mDecoder->mNumChannels; chn++) {
if (frame->header.bits_per_sample == 16) {
for (unsigned int s=0; s<frame->header.blocksize; s++) {
tmp[s]=buffer[chn][s];
}
mDecoder->mChannels[chn]->Append((samplePtr)tmp,
int16Sample,
frame->header.blocksize);
}
else {
mDecoder->mChannels[chn]->Append((samplePtr)buffer[chn],
int24Sample,
frame->header.blocksize);
}
}
delete [] tmp;
*/
mDecoder->mSamplesDone += frame->header.blocksize;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
// return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
// mDecoder->mUpdateResult = mDecoder->mProgress->Update((wxULongLong_t) mDecoder->mSamplesDone, mDecoder->mNumSamples != 0 ? (wxULongLong_t)mDecoder->mNumSamples : 1);
/*
if (mDecoder->mUpdateResult != eProgressSuccess)
{
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
*/
}
//--Decoder stuff:
///Decodes the samples for this blockfile from the real file into a float buffer.
///This is file specific, so subclasses must implement this only.
///the buffer was defined like
///samplePtr sampleData = NewSamples(mLen, floatSample);
///this->ReadData(sampleData, floatSample, 0, mLen);
///This class should call ReadHeader() first, so it knows the length, and can prepare
///the file object if it needs to.
int ODFlacDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)
{
//we need to lock this so the target stays fixed over the seek/write callback.
mFlacFileLock.Lock();
bool usingCache=mLastDecodeStartSample==start;
if(usingCache)
{
//we've just decoded this, so lets use a cache. (often so for
}
mDecodeBufferWritePosition=0;
mDecodeBufferLen = len;
data.Allocate(len, mFormat);
mDecodeBuffer = data.ptr();
format = mFormat;
mTargetChannel=channel;
if(!mFile->seek_absolute(start))
{
mFlacFileLock.Unlock();
return -1;
}
while(mDecodeBufferWritePosition<mDecodeBufferLen)
mFile->process_single();
mFlacFileLock.Unlock();
if(!usingCache)
{
mLastDecodeStartSample=start;
}
//insert into blockfile and
//calculate summary happen in ODDecodeBlockFile::WriteODDecodeBlockFile, where this method is also called.
return 1;
}
///Read header. Subclasses must override. Probably should save the info somewhere.
///Ideally called once per decoding of a file. This complicates the task because
///returns true if the file exists and the header was read alright.
//Note:we are not using LEGACY_FLAC defs (see ImportFlac.cpp FlacImportFileHandle::Init()
//this code is based on that function.
bool ODFlacDecoder::ReadHeader()
{
mFormat = int16Sample;//start with the smallest and move up in the metadata_callback.
//we want to use the native flac type for quick conversion.
/* (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);*/
if(mFile)
delete mFile;
mFile = new ODFLACFile(this);
if (!mHandle.Open(mFName, wxT("rb"))) {
return false;
}
// Even though there is an init() method that takes a filename, use the one that
// takes a file handle because wxWidgets can open a file with a Unicode name and
// libflac can't (under Windows).
//
// Responsibility for closing the file is passed to libflac.
// (it happens when mFile->finish() is called)
bool result = mFile->init(mHandle.fp())?true:false;
mHandle.Detach();
if (result != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
return false;
}
//this will call the metadata_callback when it is done
mFile->process_until_end_of_metadata();
// not necessary to check state, error callback will catch errors, but here's how:
if (mFile->get_state() > FLAC__STREAM_DECODER_READ_FRAME) {
return false;
}
if (!mFile->is_valid() || mFile->get_was_error()) {
// This probably is not a FLAC file at all
return false;
}
MarkInitialized();
return true;
}
ODFLACFile* ODFlacDecoder::GetFlacFile()
{
return mFile;
}
ODFlacDecoder::~ODFlacDecoder(){
if(mFile)
{
mFile->finish();
delete mFile;
}
}
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
//
//compare to FLACImportPlugin::Open(wxString filename)
ODFileDecoder* ODDecodeFlacTask::CreateFileDecoder(const wxString & fileName)
{
// First check if it really is a FLAC file
/*
int cnt;
wxFile binaryFile;
if (!binaryFile.Open(fileName)) {
return NULL; // File not found
}
#ifdef USE_LIBID3TAG
// Skip any ID3 tags that might be present
id3_byte_t query[ID3_TAG_QUERYSIZE];
cnt = binaryFile.Read(query, sizeof(query));
cnt = id3_tag_query(query, cnt);
binaryFile.Seek(cnt);
#endif
char buf[5];
cnt = binaryFile.Read(buf, 4);
binaryFile.Close();
if (cnt == wxInvalidOffset || strncmp(buf, FLAC_HEADER, 4) != 0) {
// File is not a FLAC file
return NULL;
}
// Open the file for import
ODFlacDecoder *decoder = new ODFlacDecoder(fileName);
*/
/*
bool success = decoder->Init();
if (!success) {
delete decoder;
return NULL;
}
*/
// Open the file for import
ODFlacDecoder *decoder = new ODFlacDecoder(fileName);
mDecoders.push_back(decoder);
return decoder;
}