/* Audio File Library Copyright (C) 2000, Silicon Graphics, Inc. Copyright (C) 2010-2013, Michael Pruett This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "ModuleState.h" #include "File.h" #include "FileHandle.h" #include "FileModule.h" #include "RebufferModule.h" #include "SimpleModule.h" #include "Track.h" #include "byteorder.h" #include "compression.h" #include "units.h" #include "util.h" #include "../pcm.h" #include #include #include #include #include ModuleState::ModuleState() : m_isDirty(true) { } ModuleState::~ModuleState() { } status ModuleState::initFileModule(AFfilehandle file, Track *track) { const CompressionUnit *unit = _af_compression_unit_from_id(track->f.compressionType); if (!unit) return AF_FAIL; // Validate compression format and parameters. if (!unit->fmtok(&track->f)) return AF_FAIL; if (file->m_seekok && file->m_fh->seek(track->fpos_first_frame, File::SeekFromBeginning) != track->fpos_first_frame) { _af_error(AF_BAD_LSEEK, "unable to position file handle at beginning of sound data"); return AF_FAIL; } AFframecount chunkFrames; if (file->m_access == _AF_READ_ACCESS) m_fileModule = unit->initdecompress(track, file->m_fh, file->m_seekok, file->m_fileFormat == AF_FILE_RAWDATA, &chunkFrames); else m_fileModule = unit->initcompress(track, file->m_fh, file->m_seekok, file->m_fileFormat == AF_FILE_RAWDATA, &chunkFrames); if (unit->needsRebuffer) { assert(unit->nativeSampleFormat == AF_SAMPFMT_TWOSCOMP); RebufferModule::Direction direction = file->m_access == _AF_WRITE_ACCESS ? RebufferModule::VariableToFixed : RebufferModule::FixedToVariable; m_fileRebufferModule = new RebufferModule(direction, track->f.bytesPerFrame(false), chunkFrames, unit->multiple_of); } track->filemodhappy = true; return AF_SUCCEED; } status ModuleState::init(AFfilehandle file, Track *track) { if (initFileModule(file, track) == AF_FAIL) return AF_FAIL; return AF_SUCCEED; } bool ModuleState::fileModuleHandlesSeeking() const { return m_fileModule->handlesSeeking(); } status ModuleState::setup(AFfilehandle file, Track *track) { AFframecount fframepos = llrint(track->nextvframe * track->f.sampleRate / track->v.sampleRate); bool isReading = file->m_access == _AF_READ_ACCESS; if (!track->v.isUncompressed()) { _af_error(AF_BAD_NOT_IMPLEMENTED, "library does not support compression in virtual format yet"); return AF_FAIL; } if (arrange(file, track) == AF_FAIL) return AF_FAIL; track->filemodhappy = true; int maxbufsize = 0; if (isReading) { m_chunks.back()->frameCount = _AF_ATOMIC_NVFRAMES; for (int i=m_modules.size() - 1; i >= 0; i--) { SharedPtr inChunk = m_chunks[i]; SharedPtr outChunk = m_chunks[i+1]; int bufsize = outChunk->frameCount * outChunk->f.bytesPerFrame(true); if (bufsize > maxbufsize) maxbufsize = bufsize; if (i != 0) m_modules[i]->setSource(m_modules[i-1].get()); m_modules[i]->maxPull(); } if (!track->filemodhappy) return AF_FAIL; int bufsize = m_fileModule->bufferSize(); if (bufsize > maxbufsize) maxbufsize = bufsize; } else { m_chunks.front()->frameCount = _AF_ATOMIC_NVFRAMES; for (size_t i=0; i inChunk = m_chunks[i]; SharedPtr outChunk = m_chunks[i+1]; int bufsize = inChunk->frameCount * inChunk->f.bytesPerFrame(true); if (bufsize > maxbufsize) maxbufsize = bufsize; if (i != m_modules.size() - 1) m_modules[i]->setSink(m_modules[i+1].get()); m_modules[i]->maxPush(); } if (!track->filemodhappy) return AF_FAIL; int bufsize = m_fileModule->bufferSize(); if (bufsize > maxbufsize) maxbufsize = bufsize; } for (size_t i=0; iallocate(maxbufsize); } if (isReading) { if (track->totalfframes == -1) track->totalvframes = -1; else track->totalvframes = llrint(track->totalfframes * (track->v.sampleRate / track->f.sampleRate)); track->nextfframe = fframepos; track->nextvframe = llrint(fframepos * track->v.sampleRate / track->f.sampleRate); m_isDirty = false; if (reset(file, track) == AF_FAIL) return AF_FAIL; } else { track->nextvframe = track->totalvframes = (AFframecount) (fframepos * track->v.sampleRate / track->f.sampleRate); m_isDirty = false; } return AF_SUCCEED; } const std::vector > &ModuleState::modules() const { return m_modules; } const std::vector > &ModuleState::chunks() const { return m_chunks; } status ModuleState::reset(AFfilehandle file, Track *track) { track->filemodhappy = true; for (std::vector >::reverse_iterator i=m_modules.rbegin(); i != m_modules.rend(); ++i) (*i)->reset1(); track->frames2ignore = 0; if (!track->filemodhappy) return AF_FAIL; for (std::vector >::iterator i=m_modules.begin(); i != m_modules.end(); ++i) (*i)->reset2(); if (!track->filemodhappy) return AF_FAIL; return AF_SUCCEED; } status ModuleState::sync(AFfilehandle file, Track *track) { track->filemodhappy = true; for (std::vector >::reverse_iterator i=m_modules.rbegin(); i != m_modules.rend(); ++i) (*i)->sync1(); if (!track->filemodhappy) return AF_FAIL; for (std::vector >::iterator i=m_modules.begin(); i != m_modules.end(); ++i) (*i)->sync2(); return AF_SUCCEED; } static const PCMInfo * const intmappings[6] = { &_af_default_signed_integer_pcm_mappings[1], &_af_default_signed_integer_pcm_mappings[2], &_af_default_signed_integer_pcm_mappings[3], &_af_default_signed_integer_pcm_mappings[4], NULL, NULL }; static FormatCode getFormatCode(const AudioFormat &format) { if (format.sampleFormat == AF_SAMPFMT_FLOAT) return kFloat; if (format.sampleFormat == AF_SAMPFMT_DOUBLE) return kDouble; if (format.isInteger()) { switch (format.bytesPerSample(false)) { case 1: return kInt8; case 2: return kInt16; case 3: return kInt24; case 4: return kInt32; } } /* NOTREACHED */ assert(false); return kUndefined; } static bool isInteger(FormatCode code) { return code >= kInt8 && code <= kInt32; } static bool isFloat(FormatCode code) { return code >= kFloat && code <= kDouble; } static bool isTrivialIntMapping(const AudioFormat &format, FormatCode code) { return intmappings[code] != NULL && format.pcm.slope == intmappings[code]->slope && format.pcm.intercept == intmappings[code]->intercept; } static bool isTrivialIntClip(const AudioFormat &format, FormatCode code) { return intmappings[code] != NULL && format.pcm.minClip == intmappings[code]->minClip && format.pcm.maxClip == intmappings[code]->maxClip; } status ModuleState::arrange(AFfilehandle file, Track *track) { bool isReading = file->m_access == _AF_READ_ACCESS; AudioFormat in, out; if (isReading) { in = track->f; out = track->v; } else { in = track->v; out = track->f; } FormatCode infc = getFormatCode(in); FormatCode outfc = getFormatCode(out); if (infc == kUndefined || outfc == kUndefined) return AF_FAIL; m_chunks.clear(); m_chunks.push_back(new Chunk()); m_chunks.back()->f = in; m_modules.clear(); if (isReading) { addModule(m_fileModule.get()); addModule(m_fileRebufferModule.get()); } // Convert to native byte order. if (in.byteOrder != _AF_BYTEORDER_NATIVE) { size_t bytesPerSample = in.bytesPerSample(!isReading); if (bytesPerSample > 1 && in.compressionType == AF_COMPRESSION_NONE) addModule(new SwapModule()); else in.byteOrder = _AF_BYTEORDER_NATIVE; } // Handle 24-bit integer input format. if (in.isInteger() && in.bytesPerSample(false) == 3) { if (isReading || in.compressionType != AF_COMPRESSION_NONE) addModule(new Expand3To4Module(in.isSigned())); } // Make data signed. if (in.isUnsigned()) addModule(new ConvertSign(infc, false)); in.pcm = m_chunks.back()->f.pcm; // Reverse the unsigned shift for output. if (out.isUnsigned()) { const double shift = intmappings[outfc]->minClip; out.pcm.intercept += shift; out.pcm.minClip += shift; out.pcm.maxClip += shift; } // Clip input samples if necessary. if (in.pcm.minClip < in.pcm.maxClip && !isTrivialIntClip(in, infc)) addModule(new Clip(infc, in.pcm)); bool alreadyClippedOutput = false; bool alreadyTransformedOutput = false; // Perform range transformation if input and output PCM mappings differ. bool transforming = (in.pcm.slope != out.pcm.slope || in.pcm.intercept != out.pcm.intercept) && !(isTrivialIntMapping(in, infc) && isTrivialIntMapping(out, outfc)); // Range transformation requires input to be floating-point. if (isInteger(infc) && transforming) { if (infc == kInt32 || outfc == kDouble || outfc == kInt32) { addConvertIntToFloat(infc, kDouble); infc = kDouble; } else { addConvertIntToFloat(infc, kFloat); infc = kFloat; } } if (transforming && infc == kDouble && isFloat(outfc)) addModule(new Transform(infc, in.pcm, out.pcm)); // Add format conversion if needed. if (isInteger(infc) && isInteger(outfc)) addConvertIntToInt(infc, outfc); else if (isInteger(infc) && isFloat(outfc)) addConvertIntToFloat(infc, outfc); else if (isFloat(infc) && isInteger(outfc)) { addConvertFloatToInt(infc, outfc, in.pcm, out.pcm); alreadyClippedOutput = true; alreadyTransformedOutput = true; } else if (isFloat(infc) && isFloat(outfc)) addConvertFloatToFloat(infc, outfc); if (transforming && !alreadyTransformedOutput && infc != kDouble) addModule(new Transform(outfc, in.pcm, out.pcm)); if (in.channelCount != out.channelCount) addModule(new ApplyChannelMatrix(infc, isReading, in.channelCount, out.channelCount, in.pcm.minClip, in.pcm.maxClip, track->channelMatrix)); // Perform clipping if necessary. if (!alreadyClippedOutput) { if (out.pcm.minClip < out.pcm.maxClip && !isTrivialIntClip(out, outfc)) addModule(new Clip(outfc, out.pcm)); } // Make data unsigned if necessary. if (out.isUnsigned()) addModule(new ConvertSign(outfc, true)); // Handle 24-bit integer output format. if (out.isInteger() && out.bytesPerSample(false) == 3) { if (!isReading || out.compressionType != AF_COMPRESSION_NONE) addModule(new Compress4To3Module(out.isSigned())); } if (out.byteOrder != _AF_BYTEORDER_NATIVE) { size_t bytesPerSample = out.bytesPerSample(isReading); if (bytesPerSample > 1 && out.compressionType == AF_COMPRESSION_NONE) addModule(new SwapModule()); else out.byteOrder = _AF_BYTEORDER_NATIVE; } if (!isReading) { addModule(m_fileRebufferModule.get()); addModule(m_fileModule.get()); } return AF_SUCCEED; } void ModuleState::addModule(Module *module) { if (!module) return; m_modules.push_back(module); module->setInChunk(m_chunks.back().get()); Chunk *chunk = new Chunk(); chunk->f = m_chunks.back()->f; m_chunks.push_back(chunk); module->setOutChunk(chunk); module->describe(); } void ModuleState::addConvertIntToInt(FormatCode input, FormatCode output) { if (input == output) return; assert(isInteger(input)); assert(isInteger(output)); addModule(new ConvertInt(input, output)); } void ModuleState::addConvertIntToFloat(FormatCode input, FormatCode output) { addModule(new ConvertIntToFloat(input, output)); } void ModuleState::addConvertFloatToInt(FormatCode input, FormatCode output, const PCMInfo &inputMapping, const PCMInfo &outputMapping) { addModule(new ConvertFloatToIntClip(input, output, inputMapping, outputMapping)); } void ModuleState::addConvertFloatToFloat(FormatCode input, FormatCode output) { if (input == output) return; assert((input == kFloat && output == kDouble) || (input == kDouble && output == kFloat)); addModule(new ConvertFloat(input, output)); } void ModuleState::print() { fprintf(stderr, "modules:\n"); for (size_t i=0; iname(), m_modules[i].get(), m_modules[i]->inChunk(), m_modules[i]->outChunk()); fprintf(stderr, "chunks:\n"); for (size_t i=0; if.description().c_str()); }