winamp/Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h
2024-09-24 14:54:57 +02:00

407 lines
11 KiB
C++

/*
* 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