mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-25 16:48:44 +02:00
322 lines
9.1 KiB
C++
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;
|
|
|
|
}
|
|
|