diff options
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h b/Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h new file mode 100644 index 00000000..380eaf80 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/soundlib/WAVTools.h @@ -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 |