1 /** 2 * Read support for the RIFF file format (used in .wav files). 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.utils.sound.riff.reader; 15 16 import std.algorithm.searching; 17 import std.exception; 18 19 import ae.utils.sound.riff.common; 20 21 /// RIFF chunk. 22 struct Chunk 23 { 24 /// Chunk header. 25 struct Header 26 { 27 char[4] name; /// 28 uint length; /// 29 ubyte[0] data; /// Chunk data follows. 30 } 31 32 Header* header; /// ditto 33 34 /// Parse and consume one chunk from `data`. 35 this(ref ubyte[] data) 36 { 37 enforce(data.length >= Header.sizeof); 38 header = cast(Header*)data; 39 data = data[Header.sizeof..$]; 40 41 enforce(data.length >= header.length); 42 data = data[header.length..$]; 43 } 44 45 char[4] name() { return header.name; } /// Chunk name (from header). 46 ubyte[] data() { return header.data.ptr[0..header.length]; } /// Chunk data. 47 } 48 49 /// Reads chunks from an array of bytes. 50 struct Chunks 51 { 52 ubyte[] data; /// The array of bytes. 53 /// Range primitives. 54 bool empty() { return data.length == 0; } 55 Chunk front() { auto cData = data; return Chunk(cData); } /// ditto 56 void popFront() { auto c = Chunk(data); } /// ditto 57 } 58 59 /// Return a `Chunk` from `data`. 60 auto readRiff(ubyte[] data) 61 { 62 return Chunk(data); 63 } 64 65 /// Get samples from a parsed RIFF file. 66 /// The format is expected to match `T`. 67 auto getWave(T)(Chunk chunk) 68 { 69 enforce(chunk.name == "RIFF", "Unknown file format"); 70 auto riffData = chunk.data; 71 enforce(riffData.skipOver("WAVE"), "Unknown RIFF contents"); 72 WaveFmt fmt = (cast(WaveFmt[])Chunks(riffData).find!(c => c.name == "fmt ").front.data)[0]; 73 enforce(fmt.format == 1, "Unknown WAVE format"); 74 enforce(fmt.sampleRate * T.sizeof == fmt.byteRate, "Format mismatch"); 75 auto data = Chunks(riffData).find!(c => c.name == "data").front.data; 76 return cast(T[])data; 77 }