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 }