1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
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
|