1 /**
2  * Play waves using ALSA command-line tools
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.asound;
15 
16 import std.conv;
17 import std.exception;
18 import std.process;
19 import std.range;
20 import std.traits;
21 
22 /// Return the ALSA format name corresponding to the given type.
23 template aSoundFormat(T)
24 {
25 	version(LittleEndian)
26 		enum aSoundEndianness = "_LE";
27 	else
28 		enum aSoundEndianness = "_BE";
29 
30 	static if (is(T==ubyte))
31 		enum aSoundFormat = "U8";
32 	else
33 	static if (is(T==byte))
34 		enum aSoundFormat = "S8";
35 	else
36 	static if (is(T==ushort))
37 		enum aSoundFormat = "U16" ~ aSoundEndianness;
38 	else
39 	static if (is(T==short))
40 		enum aSoundFormat = "S16" ~ aSoundEndianness;
41 	else
42 	static if (is(T==uint))
43 		enum aSoundFormat = "U32" ~ aSoundEndianness;
44 	else
45 	static if (is(T==int))
46 		enum aSoundFormat = "S32" ~ aSoundEndianness;
47 	else
48 	static if (is(T==float))
49 		enum aSoundFormat = "FLOAT" ~ aSoundEndianness;
50 	else
51 	static if (is(T==double))
52 		enum aSoundFormat = "FLOAT64" ~ aSoundEndianness;
53 	else
54 		static assert(false, "Can't represent sample type in asound format: " ~ T.stringof);
55 }
56 
57 void playWave(Wave)(Wave wave, int sampleRate = 44100)
58 {
59 	alias Sample = typeof(wave.front);
60 	static if (is(Sample C : C[channels_], size_t channels_))
61 	{
62 		alias ChannelSample = C;
63 		enum channels = channels_;
64 	}
65 	else
66 	{
67 		alias ChannelSample = Sample;
68 		enum channels = 1;
69 	}
70 	auto p = pipe();
71 	auto pid = spawnProcess([
72 			"aplay",
73 			"--format", aSoundFormat!ChannelSample,
74 			"--channels", text(channels),
75 			"--rate", text(sampleRate),
76 		], p.readEnd());
77 	while (!wave.empty)
78 	{
79 		Sample[1] s;
80 		s[0] = wave.front;
81 		wave.popFront();
82 		p.writeEnd.rawWrite(s[]);
83 	}
84 	p.writeEnd.close();
85 	enforce(pid.wait() == 0, "aplay failed");
86 }
87 
88 unittest
89 {
90 	if (false)
91 		playWave(iota(100));
92 }