1 /** 2 * Write 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 <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.sound.riff.writer; 15 16 import std.algorithm; 17 import std.conv; 18 import std.range; 19 20 import ae.utils.sound.riff.common; 21 22 struct ValueReprRange(T) 23 { 24 ubyte[T.sizeof] bytes; 25 size_t p; 26 27 this(ref T t) 28 { 29 bytes[] = (cast(ubyte[])((&t)[0..1]))[]; 30 } 31 32 @property ubyte front() { return bytes[p]; } 33 void popFront() { p++; } 34 @property bool empty() { return p == T.sizeof; } 35 @property size_t length() { return T.sizeof - p; } 36 } 37 38 auto valueReprRange(T)(auto ref T t) { return ValueReprRange!T(t); } 39 40 auto fourCC(char[4] name) 41 { 42 return valueReprRange(name); 43 } 44 45 auto riffChunk(R)(char[4] name, R data) 46 { 47 return chain( 48 fourCC(name), 49 valueReprRange(data.length.to!uint), 50 data 51 ); 52 } 53 54 auto makeRiff(R)(R r, uint sampleRate = 44100) 55 { 56 static if (!is(typeof(r.front) == struct)) 57 { 58 struct Mono { typeof(r.front) sample; } 59 return makeRiff(r.map!(s => Mono(s)), sampleRate); 60 } 61 else 62 { 63 enum numChannels = r.front.tupleof.length; 64 auto bytesPerSample = r.front.tupleof[0].sizeof; 65 auto bitsPerSample = bytesPerSample * 8; 66 67 return riffChunk("RIFF", 68 chain( 69 fourCC("WAVE"), 70 riffChunk("fmt ", 71 valueReprRange(WaveFmt( 72 1, // PCM 73 to!ushort(numChannels), 74 sampleRate, 75 to!uint (sampleRate * bytesPerSample * numChannels), 76 to!ushort(bytesPerSample * numChannels), 77 to!ushort(bitsPerSample), 78 )), 79 ), 80 riffChunk("data", 81 r.map!(s => valueReprRange(s)).joiner.takeExactly(r.length * r.front.sizeof), 82 ), 83 ), 84 ); 85 } 86 }