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