#pragma once
#include "BuildSettings.h"
#include "ChunkReader.h"
#include "Loaders.h"
#include "../common/mptUUID.h"
OPENMPT_NAMESPACE_BEGIN
struct FileTags;
struct RIFFHeader
{
enum RIFFMagic
{
idRIFF = MagicLE("RIFF"), idLIST = MagicLE("LIST"), idWAVE = MagicLE("WAVE"), idwave = MagicLE("wave"), };
uint32le magic; uint32le length; uint32le type; };
MPT_BINARY_STRUCT(RIFFHeader, 12)
struct RIFFChunk
{
enum ChunkIdentifiers
{
idfmt_ = MagicLE("fmt "), iddata = MagicLE("data"), idpcm_ = MagicLE("pcm "), idfact = MagicLE("fact"), idsmpl = MagicLE("smpl"), idinst = MagicLE("inst"), idLIST = MagicLE("LIST"), idxtra = MagicLE("xtra"), idcue_ = MagicLE("cue "), idwsmp = MagicLE("wsmp"), idCSET = MagicLE("CSET"), id____ = 0x00000000,
idINAM = MagicLE("INAM"), idISFT = MagicLE("ISFT"), idICOP = MagicLE("ICOP"), idIART = MagicLE("IART"), idIPRD = MagicLE("IPRD"), idICMT = MagicLE("ICMT"), idIENG = MagicLE("IENG"), idISBJ = MagicLE("ISBJ"), idIGNR = MagicLE("IGNR"), idICRD = MagicLE("ICRD"),
idYEAR = MagicLE("YEAR"), idTRCK = MagicLE("TRCK"), idTURL = MagicLE("TURL"), };
uint32le id; uint32le length;
size_t GetLength() const
{
return length;
}
ChunkIdentifiers GetID() const
{
return static_cast<ChunkIdentifiers>(id.get());
}
};
MPT_BINARY_STRUCT(RIFFChunk, 8)
struct WAVFormatChunk
{
enum SampleFormats
{
fmtPCM = 1,
fmtFloat = 3,
fmtALaw = 6,
fmtULaw = 7,
fmtIMA_ADPCM = 17,
fmtMP3 = 85,
fmtExtensible = 0xFFFE,
};
uint16le format; uint16le numChannels; uint32le sampleRate; uint32le byteRate; uint16le blockAlign; uint16le bitsPerSample; };
MPT_BINARY_STRUCT(WAVFormatChunk, 16)
struct WAVFormatChunkExtension
{
uint16le size;
uint16le validBitsPerSample;
uint32le channelMask;
GUIDms subFormat;
};
MPT_BINARY_STRUCT(WAVFormatChunkExtension, 24)
struct WAVSampleInfoChunk
{
uint32le manufacturer;
uint32le product;
uint32le samplePeriod; uint32le baseNote; uint32le pitchFraction;
uint32le SMPTEFormat;
uint32le SMPTEOffset;
uint32le numLoops; uint32le samplerData;
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)
struct WAVSampleLoop
{
enum LoopType
{
loopForward = 0,
loopBidi = 1,
loopBackward = 2,
};
uint32le identifier;
uint32le loopType; uint32le loopStart; uint32le loopEnd; uint32le fraction;
uint32le playCount;
void ApplyToSample(SmpLength &start, SmpLength &end, SmpLength sampleLength, SampleFlags &flags, ChannelFlags enableFlag, ChannelFlags bidiFlag, bool mptLoopFix) const;
void ConvertToWAV(SmpLength start, SmpLength end, bool bidi);
};
MPT_BINARY_STRUCT(WAVSampleLoop, 24)
struct WAVInstrumentChunk
{
uint8 unshiftedNote; int8 finetune; int8 gain; uint8 lowNote; uint8 highNote;
uint8 lowVelocity; uint8 highVelocity;
};
MPT_BINARY_STRUCT(WAVInstrumentChunk, 7)
struct WAVExtraChunk
{
enum Flags
{
setPanning = 0x20,
};
uint32le flags;
uint16le defaultPan;
uint16le defaultVolume;
uint16le globalVolume;
uint16le reserved;
uint8le vibratoType;
uint8le vibratoSweep;
uint8le vibratoDepth;
uint8le vibratoRate;
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))
{
vibratoSweep = 255 - vibratoSweep;
}
}
};
MPT_BINARY_STRUCT(WAVExtraChunk, 16)
struct WAVCuePoint
{
uint32le id; uint32le position; uint32le riffChunkID; uint32le chunkStart; uint32le blockStart; uint32le offset;
void ConvertToWAV(uint32 id_, SmpLength offset_)
{
id = id_;
position = offset_;
riffChunkID = static_cast<uint32>(RIFFChunk::iddata);
chunkStart = 0; blockStart = 0; offset = offset_;
}
};
MPT_BINARY_STRUCT(WAVCuePoint, 24)
class WAVReader
{
protected:
ChunkReader file;
FileReader sampleData, smplChunk, instChunk, xtraChunk, wsmpChunk, cueChunk;
ChunkReader::ChunkList<RIFFChunk> infoChunk;
FileReader::off_t sampleLength;
WAVFormatChunk formatInfo;
uint16 subFormat;
uint16 codePage;
bool isDLS;
bool mayBeCoolEdit16_8;
uint16 GetFileCodePage(ChunkReader::ChunkList<RIFFChunk> &chunks);
public:
WAVReader(FileReader &inputFile);
bool IsValid() const { return sampleData.IsValid(); }
void FindMetadataChunks(ChunkReader::ChunkList<RIFFChunk> &chunks);
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; }
uint16 GetSampleSize() const { return ((GetNumChannels() * GetBitsPerSample()) + 7) / 8; }
SmpLength GetSampleLength() const { return mpt::saturate_cast<SmpLength>(sampleLength); }
void ApplySampleSettings(ModSample &sample, mpt::Charset charset, char (&sampleCharset)[MAX_SAMPLENAME]);
};
#ifndef MODPLUG_NO_FILESAVE
class WAVWriter
{
protected:
std::ostream *s = nullptr;
mpt::byte_span memory;
size_t position = 0;
size_t totalSize = 0;
size_t chunkStartPos = 0;
RIFFChunk chunkHeader;
public:
WAVWriter(std::ostream *stream);
WAVWriter(mpt::byte_span data);
~WAVWriter() noexcept(false);
bool IsValid() const { return s != nullptr || !memory.empty(); }
size_t Finalize();
void StartChunk(RIFFChunk::ChunkIdentifiers id);
void Skip(size_t numBytes) { Seek(position + numBytes); }
size_t GetPosition() const { return position; }
void Truncate() { totalSize = position; }
template<typename T>
void Write(const T &data)
{
MPT_STATIC_ASSERT((mpt::is_binary_safe<T>::value));
Write(&data, sizeof(T));
}
void WriteBuffer(const char *data, size_t size)
{
Write(data, size);
}
template<typename T, size_t size>
void WriteArray(const T (&data)[size])
{
Write(data, sizeof(T) * size);
}
void WriteFormat(uint32 sampleRate, uint16 bitDepth, uint16 numChannels, WAVFormatChunk::SampleFormats encoding);
void WriteMetatags(const FileTags &tags);
void WriteLoopInformation(const ModSample &sample);
void WriteCueInformation(const ModSample &sample);
void WriteExtraInformation(const ModSample &sample, MODTYPE modType, const char *sampleName = nullptr);
protected:
void Seek(size_t pos);
void FinalizeChunk();
void Write(const void *data, size_t numBytes);
void WriteTag(RIFFChunk::ChunkIdentifiers id, const mpt::ustring &utext);
};
#endif
OPENMPT_NAMESPACE_END