Initial community commit
This commit is contained in:
406
Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h
Normal file
406
Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h
Normal file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* WAVTools.h
|
||||
* ----------
|
||||
* Purpose: Definition of WAV file structures and helper functions
|
||||
* Notes : (currently none)
|
||||
* Authors: OpenMPT Devs
|
||||
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openmpt/all/BuildSettings.hpp"
|
||||
|
||||
#include "mpt/uuid/uuid.hpp"
|
||||
|
||||
#include "../common/FileReader.h"
|
||||
#include "Loaders.h"
|
||||
|
||||
#ifndef MODPLUG_NO_FILESAVE
|
||||
#include "mpt/io/io.hpp"
|
||||
#include "mpt/io/io_virtual_wrapper.hpp"
|
||||
#endif
|
||||
|
||||
OPENMPT_NAMESPACE_BEGIN
|
||||
|
||||
struct FileTags;
|
||||
|
||||
// RIFF header
|
||||
struct RIFFHeader
|
||||
{
|
||||
// 32-Bit chunk identifiers
|
||||
enum RIFFMagic
|
||||
{
|
||||
idRIFF = MagicLE("RIFF"), // magic for WAV files
|
||||
idLIST = MagicLE("LIST"), // magic for samples in DLS banks
|
||||
idWAVE = MagicLE("WAVE"), // type for WAV files
|
||||
idwave = MagicLE("wave"), // type for samples in DLS banks
|
||||
};
|
||||
|
||||
uint32le magic; // RIFF (in WAV files) or LIST (in DLS banks)
|
||||
uint32le length; // Size of the file, not including magic and length
|
||||
uint32le type; // WAVE (in WAV files) or wave (in DLS banks)
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(RIFFHeader, 12)
|
||||
|
||||
|
||||
// General RIFF Chunk header
|
||||
struct RIFFChunk
|
||||
{
|
||||
// 32-Bit chunk identifiers
|
||||
enum ChunkIdentifiers
|
||||
{
|
||||
idfmt_ = MagicLE("fmt "), // Sample format information
|
||||
iddata = MagicLE("data"), // Sample data
|
||||
idpcm_ = MagicLE("pcm "), // IMA ADPCM samples
|
||||
idfact = MagicLE("fact"), // Compressed samples
|
||||
idsmpl = MagicLE("smpl"), // Sampler and loop information
|
||||
idinst = MagicLE("inst"), // Instrument information
|
||||
idLIST = MagicLE("LIST"), // List of chunks
|
||||
idxtra = MagicLE("xtra"), // OpenMPT extra infomration
|
||||
idcue_ = MagicLE("cue "), // Cue points
|
||||
idwsmp = MagicLE("wsmp"), // DLS bank samples
|
||||
idCSET = MagicLE("CSET"), // Character Set
|
||||
id____ = 0x00000000, // Found when loading buggy MPT samples
|
||||
|
||||
// Identifiers in "LIST" chunk
|
||||
idINAM = MagicLE("INAM"), // title
|
||||
idISFT = MagicLE("ISFT"), // software
|
||||
idICOP = MagicLE("ICOP"), // copyright
|
||||
idIART = MagicLE("IART"), // artist
|
||||
idIPRD = MagicLE("IPRD"), // product (album)
|
||||
idICMT = MagicLE("ICMT"), // comment
|
||||
idIENG = MagicLE("IENG"), // engineer
|
||||
idISBJ = MagicLE("ISBJ"), // subject
|
||||
idIGNR = MagicLE("IGNR"), // genre
|
||||
idICRD = MagicLE("ICRD"), // date created
|
||||
|
||||
idYEAR = MagicLE("YEAR"), // year
|
||||
idTRCK = MagicLE("TRCK"), // track number
|
||||
idTURL = MagicLE("TURL"), // url
|
||||
};
|
||||
|
||||
uint32le id; // See ChunkIdentifiers
|
||||
uint32le length; // Chunk size without header
|
||||
|
||||
size_t GetLength() const
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
ChunkIdentifiers GetID() const
|
||||
{
|
||||
return static_cast<ChunkIdentifiers>(id.get());
|
||||
}
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(RIFFChunk, 8)
|
||||
|
||||
|
||||
// Format Chunk
|
||||
struct WAVFormatChunk
|
||||
{
|
||||
// Sample formats
|
||||
enum SampleFormats
|
||||
{
|
||||
fmtPCM = 1,
|
||||
fmtFloat = 3,
|
||||
fmtALaw = 6,
|
||||
fmtULaw = 7,
|
||||
fmtIMA_ADPCM = 17,
|
||||
fmtMP3 = 85,
|
||||
fmtExtensible = 0xFFFE,
|
||||
};
|
||||
|
||||
uint16le format; // Sample format, see SampleFormats
|
||||
uint16le numChannels; // Number of audio channels
|
||||
uint32le sampleRate; // Sample rate in Hz
|
||||
uint32le byteRate; // Bytes per second (should be freqHz * blockAlign)
|
||||
uint16le blockAlign; // Size of a sample, in bytes (do not trust this value, it's incorrect in some files)
|
||||
uint16le bitsPerSample; // Bits per sample
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(WAVFormatChunk, 16)
|
||||
|
||||
|
||||
// Extension of the WAVFormatChunk structure, used if format == formatExtensible
|
||||
struct WAVFormatChunkExtension
|
||||
{
|
||||
uint16le size;
|
||||
uint16le validBitsPerSample;
|
||||
uint32le channelMask;
|
||||
mpt::GUIDms subFormat;
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(WAVFormatChunkExtension, 24)
|
||||
|
||||
|
||||
// Sample information chunk
|
||||
struct WAVSampleInfoChunk
|
||||
{
|
||||
uint32le manufacturer;
|
||||
uint32le product;
|
||||
uint32le samplePeriod; // 1000000000 / sampleRate
|
||||
uint32le baseNote; // MIDI base note of sample
|
||||
uint32le pitchFraction;
|
||||
uint32le SMPTEFormat;
|
||||
uint32le SMPTEOffset;
|
||||
uint32le numLoops; // number of loops
|
||||
uint32le samplerData;
|
||||
|
||||
// Set up information
|
||||
void ConvertToWAV(uint32 freq, uint8 rootNote)
|
||||
{
|
||||
manufacturer = 0;
|
||||
product = 0;
|
||||
samplePeriod = 1000000000 / freq;
|
||||
if(rootNote != 0)
|
||||
baseNote = rootNote - NOTE_MIN;
|
||||
else
|
||||
baseNote = NOTE_MIDDLEC - NOTE_MIN;
|
||||
pitchFraction = 0;
|
||||
SMPTEFormat = 0;
|
||||
SMPTEOffset = 0;
|
||||
numLoops = 0;
|
||||
samplerData = 0;
|
||||
}
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(WAVSampleInfoChunk, 36)
|
||||
|
||||
|
||||
// Sample loop information chunk (found after WAVSampleInfoChunk in "smpl" chunk)
|
||||
struct WAVSampleLoop
|
||||
{
|
||||
// Sample Loop Types
|
||||
enum LoopType
|
||||
{
|
||||
loopForward = 0,
|
||||
loopBidi = 1,
|
||||
loopBackward = 2,
|
||||
};
|
||||
|
||||
uint32le identifier;
|
||||
uint32le loopType; // See LoopType
|
||||
uint32le loopStart; // Loop start in samples
|
||||
uint32le loopEnd; // Loop end in samples
|
||||
uint32le fraction;
|
||||
uint32le playCount; // Loop Count, 0 = infinite
|
||||
|
||||
// Apply WAV loop information to a mod sample.
|
||||
void ApplyToSample(SmpLength &start, SmpLength &end, SmpLength sampleLength, SampleFlags &flags, ChannelFlags enableFlag, ChannelFlags bidiFlag, bool mptLoopFix) const;
|
||||
|
||||
// Convert internal loop information into a WAV loop.
|
||||
void ConvertToWAV(SmpLength start, SmpLength end, bool bidi);
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(WAVSampleLoop, 24)
|
||||
|
||||
|
||||
// Instrument information chunk
|
||||
struct WAVInstrumentChunk
|
||||
{
|
||||
uint8 unshiftedNote; // Root key of sample, 0...127
|
||||
int8 finetune; // Finetune of root key in cents
|
||||
int8 gain; // in dB
|
||||
uint8 lowNote; // Note range, 0...127
|
||||
uint8 highNote;
|
||||
uint8 lowVelocity; // Velocity range, 0...127
|
||||
uint8 highVelocity;
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(WAVInstrumentChunk, 7)
|
||||
|
||||
|
||||
// MPT-specific "xtra" chunk
|
||||
struct WAVExtraChunk
|
||||
{
|
||||
enum Flags
|
||||
{
|
||||
setPanning = 0x20,
|
||||
};
|
||||
|
||||
uint32le flags;
|
||||
uint16le defaultPan;
|
||||
uint16le defaultVolume;
|
||||
uint16le globalVolume;
|
||||
uint16le reserved;
|
||||
uint8le vibratoType;
|
||||
uint8le vibratoSweep;
|
||||
uint8le vibratoDepth;
|
||||
uint8le vibratoRate;
|
||||
|
||||
// Set up sample information
|
||||
void ConvertToWAV(const ModSample &sample, MODTYPE modType)
|
||||
{
|
||||
if(sample.uFlags[CHN_PANNING])
|
||||
{
|
||||
flags = WAVExtraChunk::setPanning;
|
||||
} else
|
||||
{
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
defaultPan = sample.nPan;
|
||||
defaultVolume = sample.nVolume;
|
||||
globalVolume = sample.nGlobalVol;
|
||||
vibratoType = sample.nVibType;
|
||||
vibratoSweep = sample.nVibSweep;
|
||||
vibratoDepth = sample.nVibDepth;
|
||||
vibratoRate = sample.nVibRate;
|
||||
|
||||
if((modType & MOD_TYPE_XM) && (vibratoDepth | vibratoRate))
|
||||
{
|
||||
// XM vibrato is upside down
|
||||
vibratoSweep = 255 - vibratoSweep;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(WAVExtraChunk, 16)
|
||||
|
||||
|
||||
// Sample cue point structure for the "cue " chunk
|
||||
struct WAVCuePoint
|
||||
{
|
||||
uint32le id; // Unique identification value
|
||||
uint32le position; // Play order position
|
||||
uint32le riffChunkID; // RIFF ID of corresponding data chunk
|
||||
uint32le chunkStart; // Byte Offset of Data Chunk
|
||||
uint32le blockStart; // Byte Offset to sample of First Channel
|
||||
uint32le offset; // Byte Offset to sample byte of First Channel
|
||||
|
||||
// Set up sample information
|
||||
void ConvertToWAV(uint32 id_, SmpLength offset_)
|
||||
{
|
||||
id = id_;
|
||||
position = offset_;
|
||||
riffChunkID = static_cast<uint32>(RIFFChunk::iddata);
|
||||
chunkStart = 0; // we use no Wave List Chunk (wavl) as we have only one data block, so this should be 0.
|
||||
blockStart = 0; // ditto
|
||||
offset = offset_;
|
||||
}
|
||||
};
|
||||
|
||||
MPT_BINARY_STRUCT(WAVCuePoint, 24)
|
||||
|
||||
|
||||
class WAVReader
|
||||
{
|
||||
protected:
|
||||
FileReader file;
|
||||
FileReader sampleData, smplChunk, instChunk, xtraChunk, wsmpChunk, cueChunk;
|
||||
FileReader::ChunkList<RIFFChunk> infoChunk;
|
||||
|
||||
FileReader::off_t sampleLength;
|
||||
WAVFormatChunk formatInfo;
|
||||
uint16 subFormat;
|
||||
uint16 codePage;
|
||||
bool isDLS;
|
||||
bool mayBeCoolEdit16_8;
|
||||
|
||||
uint16 GetFileCodePage(FileReader::ChunkList<RIFFChunk> &chunks);
|
||||
|
||||
public:
|
||||
WAVReader(FileReader &inputFile);
|
||||
|
||||
bool IsValid() const { return sampleData.IsValid(); }
|
||||
|
||||
void FindMetadataChunks(FileReader::ChunkList<RIFFChunk> &chunks);
|
||||
|
||||
// Self-explanatory getters.
|
||||
WAVFormatChunk::SampleFormats GetSampleFormat() const { return IsExtensibleFormat() ? static_cast<WAVFormatChunk::SampleFormats>(subFormat) : static_cast<WAVFormatChunk::SampleFormats>(formatInfo.format.get()); }
|
||||
uint16 GetNumChannels() const { return formatInfo.numChannels; }
|
||||
uint16 GetBitsPerSample() const { return formatInfo.bitsPerSample; }
|
||||
uint32 GetSampleRate() const { return formatInfo.sampleRate; }
|
||||
uint16 GetBlockAlign() const { return formatInfo.blockAlign; }
|
||||
FileReader GetSampleData() const { return sampleData; }
|
||||
FileReader GetWsmpChunk() const { return wsmpChunk; }
|
||||
bool IsExtensibleFormat() const { return formatInfo.format == WAVFormatChunk::fmtExtensible; }
|
||||
bool MayBeCoolEdit16_8() const { return mayBeCoolEdit16_8; }
|
||||
|
||||
// Get size of a single sample point, in bytes.
|
||||
uint16 GetSampleSize() const { return static_cast<uint16>(((static_cast<uint32>(GetNumChannels()) * static_cast<uint32>(GetBitsPerSample())) + 7) / 8); }
|
||||
|
||||
// Get sample length (in samples)
|
||||
SmpLength GetSampleLength() const { return mpt::saturate_cast<SmpLength>(sampleLength); }
|
||||
|
||||
// Apply sample settings from file (loop points, MPT extra settings, ...) to a sample.
|
||||
void ApplySampleSettings(ModSample &sample, mpt::Charset sampleCharset, mpt::charbuf<MAX_SAMPLENAME> &sampleName);
|
||||
};
|
||||
|
||||
|
||||
#ifndef MODPLUG_NO_FILESAVE
|
||||
|
||||
class WAVWriter
|
||||
{
|
||||
protected:
|
||||
// Output stream
|
||||
mpt::IO::OFileBase &s;
|
||||
|
||||
// Cursor position
|
||||
std::size_t position = 0;
|
||||
// Total number of bytes written to file / memory
|
||||
std::size_t totalSize = 0;
|
||||
|
||||
// Currently written chunk
|
||||
std::size_t chunkStartPos = 0;
|
||||
RIFFChunk chunkHeader;
|
||||
bool finalized = false;
|
||||
|
||||
public:
|
||||
// Output to stream
|
||||
WAVWriter(mpt::IO::OFileBase &stream);
|
||||
~WAVWriter();
|
||||
|
||||
// Finalize the file by closing the last open chunk and updating the file header. Returns total size of file.
|
||||
std::size_t Finalize();
|
||||
// Begin writing a new chunk to the file.
|
||||
void StartChunk(RIFFChunk::ChunkIdentifiers id);
|
||||
|
||||
// Skip some bytes... For example after writing sample data.
|
||||
void Skip(size_t numBytes) { Seek(position + numBytes); }
|
||||
// Get position in file (not counting any changes done to the file from outside this class, i.e. through GetFile())
|
||||
std::size_t GetPosition() const { return position; }
|
||||
|
||||
// Write some data to the file.
|
||||
template<typename T>
|
||||
void Write(const T &data)
|
||||
{
|
||||
Write(mpt::as_raw_memory(data));
|
||||
}
|
||||
|
||||
// Write a buffer to the file.
|
||||
void Write(mpt::const_byte_span data);
|
||||
|
||||
// Use before writing raw data directly to the underlying stream s
|
||||
void WriteBeforeDirect();
|
||||
// Use after writing raw data directly to the underlying stream s
|
||||
void WriteAfterDirect(bool success, std::size_t count);
|
||||
|
||||
// Write the WAV format to the file.
|
||||
void WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChannels, WAVFormatChunk::SampleFormats encoding);
|
||||
// Write text tags to the file.
|
||||
void WriteMetatags(const FileTags &tags);
|
||||
// Write a sample loop information chunk to the file.
|
||||
void WriteLoopInformation(const ModSample &sample);
|
||||
// Write a sample's cue points to the file.
|
||||
void WriteCueInformation(const ModSample &sample);
|
||||
// Write MPT's sample information chunk to the file.
|
||||
void WriteExtraInformation(const ModSample &sample, MODTYPE modType, const char *sampleName = nullptr);
|
||||
|
||||
protected:
|
||||
// Seek to a position in file.
|
||||
void Seek(std::size_t pos);
|
||||
// End current chunk by updating the chunk header and writing a padding byte if necessary.
|
||||
void FinalizeChunk();
|
||||
|
||||
// Write a single tag into a open idLIST chunk
|
||||
void WriteTag(RIFFChunk::ChunkIdentifiers id, const mpt::ustring &utext);
|
||||
};
|
||||
|
||||
#endif // MODPLUG_NO_FILESAVE
|
||||
|
||||
OPENMPT_NAMESPACE_END
|
||||
Reference in New Issue
Block a user