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