sm64pc/tools/audiofile-0.3.6/libaudiofile/modules/ModuleState.cpp
2020-05-07 20:21:22 +02:00

505 lines
13 KiB
C++

/*
Audio File Library
Copyright (C) 2000, Silicon Graphics, Inc.
Copyright (C) 2010-2013, Michael Pruett <michael@68k.org>
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 <algorithm>
#include <cassert>
#include <cmath>
#include <functional>
#include <stdio.h>
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<Chunk> inChunk = m_chunks[i];
SharedPtr<Chunk> 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<m_modules.size(); i++)
{
SharedPtr<Chunk> inChunk = m_chunks[i];
SharedPtr<Chunk> 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; i<m_chunks.size(); i++)
{
if ((isReading && i==m_chunks.size() - 1) || (!isReading && i==0))
continue;
m_chunks[i]->allocate(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<SharedPtr<Module> > &ModuleState::modules() const
{
return m_modules;
}
const std::vector<SharedPtr<Chunk> > &ModuleState::chunks() const
{
return m_chunks;
}
status ModuleState::reset(AFfilehandle file, Track *track)
{
track->filemodhappy = true;
for (std::vector<SharedPtr<Module> >::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<SharedPtr<Module> >::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<SharedPtr<Module> >::reverse_iterator i=m_modules.rbegin();
i != m_modules.rend(); ++i)
(*i)->sync1();
if (!track->filemodhappy)
return AF_FAIL;
for (std::vector<SharedPtr<Module> >::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; i<m_modules.size(); i++)
fprintf(stderr, " %s (%p) in %p out %p\n",
m_modules[i]->name(), m_modules[i].get(),
m_modules[i]->inChunk(),
m_modules[i]->outChunk());
fprintf(stderr, "chunks:\n");
for (size_t i=0; i<m_chunks.size(); i++)
fprintf(stderr, " %p %s\n",
m_chunks[i].get(),
m_chunks[i]->f.description().c_str());
}