sm64pc/tools/audiofile-0.3.6/libaudiofile/SampleVision.cpp

334 lines
8.0 KiB
C++

/*
Audio File Library
Copyright (C) 2012, 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 "SampleVision.h"
#include "File.h"
#include "Setup.h"
#include "Track.h"
#include "afinternal.h"
#include "util.h"
#include <string.h>
static const char *kSMPMagic = "SOUND SAMPLE DATA ";
static const unsigned kSMPMagicLength = 18;
static const char *kSMPVersion = "2.1 ";
static const unsigned kSMPVersionLength = 4;
static const unsigned kSMPNameLength = 30;
static const unsigned kSMPCommentLength = 60;
static const unsigned kSMPMarkerNameLength = 10;
static const uint32_t kSMPInvalidSamplePosition = 0xffffffffu;
static const uint8_t kSMPMIDIUnityPlaybackNote = 60;
static const _AFfilesetup sSampleVisionDefaultFileSetup =
{
_AF_VALID_FILESETUP,
AF_FILE_SAMPLEVISION,
true, // trackSet
true, // instrumentSet
true, // miscellaneousSet
1, // trackCount
NULL, // tracks
1, // instrumentCount
NULL, // instruments
0, // miscellaneousCount
NULL // miscellaneous
};
static void trimTrailingSpaces(char *s)
{
int n = strlen(s);
if (!n)
return;
while (--n > 0 && s[n] == ' ')
;
s[n+1] = '\0';
}
SampleVisionFile::SampleVisionFile() :
m_frameCountOffset(-1)
{
setFormatByteOrder(AF_BYTEORDER_LITTLEENDIAN);
}
SampleVisionFile::~SampleVisionFile()
{
}
bool SampleVisionFile::recognize(File *fh)
{
fh->seek(0, File::SeekFromBeginning);
char magic[kSMPMagicLength];
if (fh->read(magic, kSMPMagicLength) != (ssize_t) kSMPMagicLength)
return false;
return !strncmp(magic, kSMPMagic, kSMPMagicLength);
}
AFfilesetup SampleVisionFile::completeSetup(AFfilesetup setup)
{
if (setup->trackSet && setup->trackCount != 1)
{
_af_error(AF_BAD_NUMTRACKS, "SampleVision file must have 1 track");
return AF_NULL_FILESETUP;
}
TrackSetup *track = &setup->tracks[0];
if (track->sampleFormatSet)
{
if (!track->f.isSigned() || track->f.sampleWidth != 16)
{
_af_error(AF_BAD_SAMPFMT,
"SampleVision format supports only 16-bit signed integer audio data");
return AF_NULL_FILESETUP;
}
}
else
_af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP,
track->f.sampleWidth);
if (track->byteOrderSet && track->f.byteOrder != AF_BYTEORDER_LITTLEENDIAN)
{
// Treat error as correctable.
_af_error(AF_BAD_BYTEORDER, "SampleVision supports only little-endian data");
}
track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;
if (track->compressionSet && !track->f.isUncompressed())
{
_af_error(AF_BAD_COMPTYPE, "SampleVision does not support compressed audio data");
return AF_NULL_FILESETUP;
}
if (track->markersSet && track->markerCount)
{
_af_error(AF_BAD_NUMMARKS, "SampleVision does not support markers");
return AF_NULL_FILESETUP;
}
if (track->aesDataSet)
{
_af_error(AF_BAD_FILESETUP, "SampleVision does not support AES data");
return AF_NULL_FILESETUP;
}
return _af_filesetup_copy(setup, &sSampleVisionDefaultFileSetup, true);
}
status SampleVisionFile::readInit(AFfilesetup)
{
m_fh->seek(0, File::SeekFromBeginning);
char magic[kSMPMagicLength];
if (m_fh->read(magic, kSMPMagicLength) != (ssize_t) kSMPMagicLength)
return AF_FAIL;
if (strncmp(magic, kSMPMagic, kSMPMagicLength) != 0)
return AF_FAIL;
char version[kSMPVersionLength];
if (m_fh->read(version, kSMPVersionLength) != (ssize_t) kSMPVersionLength)
return AF_FAIL;
if (strncmp(version, kSMPVersion, kSMPVersionLength) != 0)
return AF_FAIL;
Track *track = allocateTrack();
char name[kSMPNameLength + 1];
m_fh->read(name, kSMPNameLength);
name[kSMPNameLength] = '\0';
trimTrailingSpaces(name);
if (strlen(name) > 0)
addMiscellaneous(AF_MISC_NAME, name);
char comment[kSMPCommentLength + 1];
m_fh->read(comment, kSMPCommentLength);
comment[kSMPCommentLength] = '\0';
trimTrailingSpaces(comment);
if (strlen(comment) > 0)
addMiscellaneous(AF_MISC_COMMENT, comment);
uint32_t frameCount;
readU32(&frameCount);
track->totalfframes = frameCount;
track->fpos_first_frame = m_fh->tell();
track->data_size = 2 * frameCount;
m_fh->seek(track->data_size, File::SeekFromCurrent);
uint16_t reserved;
readU16(&reserved);
parseLoops();
parseMarkers();
uint8_t midiNote;
uint32_t sampleRate;
uint32_t smpteOffset;
uint32_t cycleLength;
readU8(&midiNote);
readU32(&sampleRate);
readU32(&smpteOffset);
readU32(&cycleLength);
track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;
track->f.sampleRate = sampleRate;
track->f.channelCount = 1;
track->f.compressionType = AF_COMPRESSION_NONE;
track->f.framesPerPacket = 1;
_af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, 16);
track->f.computeBytesPerPacketPCM();
return AF_SUCCEED;
}
status SampleVisionFile::parseLoops()
{
for (int i=0; i<8; i++)
{
uint32_t startFrame, endFrame;
uint8_t type;
uint16_t count;
readU32(&startFrame);
readU32(&endFrame);
readU8(&type);
readU16(&count);
}
return AF_SUCCEED;
}
status SampleVisionFile::parseMarkers()
{
for (int i=0; i<8; i++)
{
char name[kSMPMarkerNameLength + 1];
m_fh->read(name, kSMPMarkerNameLength);
name[kSMPMarkerNameLength] = '\0';
uint32_t position;
readU32(&position);
}
return AF_SUCCEED;
}
void SampleVisionFile::addMiscellaneous(int type, const char *data)
{
m_miscellaneousCount++;
m_miscellaneous = (Miscellaneous *) _af_realloc(m_miscellaneous,
m_miscellaneousCount * sizeof (Miscellaneous));
Miscellaneous &m = m_miscellaneous[m_miscellaneousCount - 1];
m.id = m_miscellaneousCount;
m.type = type;
m.size = strlen(data);
m.position = 0;
m.buffer = _af_malloc(m.size);
memcpy(m.buffer, data, m.size);
}
status SampleVisionFile::writeInit(AFfilesetup setup)
{
if (initFromSetup(setup) == AF_FAIL)
return AF_FAIL;
m_fh->write(kSMPMagic, kSMPMagicLength);
m_fh->write(kSMPVersion, kSMPVersionLength);
char name[kSMPNameLength + 1];
char comment[kSMPCommentLength + 1];
memset(name, ' ', kSMPNameLength);
memset(comment, ' ', kSMPCommentLength);
m_fh->write(name, kSMPNameLength);
m_fh->write(comment, kSMPCommentLength);
uint32_t frameCount = 0;
m_frameCountOffset = m_fh->tell();
writeU32(&frameCount);
Track *track = getTrack();
track->fpos_first_frame = m_fh->tell();
return AF_SUCCEED;
}
status SampleVisionFile::update()
{
m_fh->seek(m_frameCountOffset, File::SeekFromBeginning);
Track *track = getTrack();
uint32_t frameCount = track->totalfframes;
writeU32(&frameCount);
writeTrailer();
return AF_SUCCEED;
}
status SampleVisionFile::writeTrailer()
{
Track *track = getTrack();
m_fh->seek(track->fpos_after_data, File::SeekFromBeginning);
uint16_t reserved = 0;
writeU16(&reserved);
writeLoops();
writeMarkers();
uint8_t midiNote = kSMPMIDIUnityPlaybackNote;
uint32_t sampleRate = track->f.sampleRate;
uint32_t smpteOffset = 0;
uint32_t cycleLength = 0;
writeU8(&midiNote);
writeU32(&sampleRate);
writeU32(&smpteOffset);
writeU32(&cycleLength);
return AF_SUCCEED;
}
status SampleVisionFile::writeLoops()
{
for (int i=0; i<8; i++)
{
uint32_t startFrame = kSMPInvalidSamplePosition, endFrame = 0;
uint8_t type = 0;
uint16_t count = 0;
writeU32(&startFrame);
writeU32(&endFrame);
writeU8(&type);
writeU16(&count);
}
return AF_SUCCEED;
}
status SampleVisionFile::writeMarkers()
{
for (int i=0; i<8; i++)
{
char name[kSMPMarkerNameLength + 1];
memset(name, ' ', kSMPMarkerNameLength);
name[kSMPMarkerNameLength] = '\0';
m_fh->write(name, kSMPMarkerNameLength);
uint32_t position = kSMPInvalidSamplePosition;
writeU32(&position);
}
return AF_SUCCEED;
}