egra: even more comments
[iv.d.git] / follin / drivers / alsa.d
blobda676a19d6b125283862ee0f7bd7df57d279189e
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module iv.follin.drivers.alsa /*is aliced*/;
19 //version = follin_init_debug;
20 //version = follin_write_debug;
21 version = follin_radio_silence_debug;
23 import core.atomic;
25 import iv.alice;
26 import iv.follin.exception;
27 import iv.follin.sdata;
30 // ////////////////////////////////////////////////////////////////////////// //
31 __gshared snd_pcm_t* apcm = null;
32 __gshared short* sndsilence = null;
35 // ////////////////////////////////////////////////////////////////////////// //
36 package(iv.follin) void sndDeinit () nothrow @trusted /*@nogc*/ {
37 if (apcm !is null) {
38 snd_pcm_drop(apcm);
39 snd_pcm_close(apcm);
40 apcm = null;
41 if (sndsilence !is null) {
42 import core.stdc.stdlib : free;
43 free(sndsilence);
44 sndsilence = null;
50 uint nextPowerOf2 (uint n) {
51 //static if (__VERSION__ > 2067) pragma(inline, true);
52 --n;
53 n |= n>>1;
54 n |= n>>2;
55 n |= n>>4;
56 n |= n>>8;
57 n |= n>>16;
58 ++n;
59 return n;
63 package(iv.follin) void sndInit (const(char)* alsaDev, uint srate) {
64 snd_pcm_hw_params_t* hw_params = null;
65 snd_pcm_sw_params_t* sw_params = null;
66 snd_pcm_uframes_t rlbufsize;
67 uint sr = srate;
69 static void alsaCall (int err, string msg) @trusted {
70 if (err < 0) {
71 import std.string : fromStringz;
72 throw new FollinException((msg~" ("~snd_strerror(err).fromStringz~")").idup);
76 fuck_alsa_messages();
78 alsaCall(snd_pcm_open(&apcm, alsaDev, SND_PCM_STREAM_PLAYBACK, 0), "cannot open audio device");
79 alsaCall(snd_pcm_hw_params_malloc(&hw_params), "cannot allocate hardware parameter structure");
80 alsaCall(snd_pcm_hw_params_any(apcm, hw_params), "cannot initialize hardware parameter structure");
81 alsaCall(snd_pcm_hw_params_set_access(apcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "cannot set access type");
82 alsaCall(snd_pcm_hw_params_set_format(apcm, hw_params, SND_PCM_FORMAT_S16_NATIVE), "cannot set sample format");
83 snd_pcm_hw_params_set_rate_resample(apcm, hw_params, 1); // ignore errors here
84 import core.stdc.string : strncmp;
85 if (strncmp(alsaDev, "plug:default", 12) == 0) {
86 alsaCall(snd_pcm_hw_params_set_rate_resample(apcm, hw_params, 1), "cannot turn on resampling");
87 alsaCall(snd_pcm_hw_params_set_rate(apcm, hw_params, sr, 0), "cannot set sample rate");
88 } else {
89 alsaCall(snd_pcm_hw_params_set_rate_near(apcm, hw_params, &sr, null), "cannot set sample rate");
91 alsaCall(snd_pcm_hw_params_set_channels(apcm, hw_params, 2/*numchans*/), "cannot set channel count");
93 //alsaCall(snd_pcm_hw_params_set_buffer_size_near(apcm, hw_params, &rlbufsize), "cannot set buffer size");
94 rlbufsize = sr/100;
95 rlbufsize = (rlbufsize < 512 ? 512 : nextPowerOf2(cast(uint)rlbufsize)); // can't do less
96 auto rbsp2 = cast(uint)rlbufsize;
97 rlbufsize *= 2; // we want room for at least two buffers, so one is always filled
98 //{ import core.stdc.stdio : fprintf, stderr; fprintf(stderr, "want: %u\n", cast(uint)rlbufsize); }
99 alsaCall(snd_pcm_hw_params_set_buffer_size_near(apcm, hw_params, &rlbufsize), "cannot set buffer size");
100 realSampleRate = sr;
101 sndSamplesSize = rbsp2; // in frames for now
102 latency = 1000*sndSamplesSize/sr;
103 version(follin_init_debug) {
104 import core.stdc.stdio : fprintf, stderr;
105 fprintf(stderr, "Follin: real soundcard sample rate: %u; frames in buffer: %u; latency: %u; rbsp2: %u\n", sr, cast(uint)rlbufsize, cast(uint)latency, rbsp2);
107 alsaCall(snd_pcm_hw_params(apcm, hw_params), "cannot set parameters");
108 snd_pcm_hw_params_free(hw_params);
110 realBufSize = cast(uint)rlbufsize;
112 alsaCall(snd_pcm_sw_params_malloc(&sw_params), "cannot allocate software parameters structure");
113 alsaCall(snd_pcm_sw_params_current(apcm, sw_params), "cannot initialize software parameters structure");
114 alsaCall(snd_pcm_sw_params_set_avail_min(apcm, sw_params, rbsp2/*rlbufsize*/), "cannot set minimum available count");
115 alsaCall(snd_pcm_sw_params_set_start_threshold(apcm, sw_params, rbsp2), "cannot set start mode");
116 alsaCall(snd_pcm_sw_params(apcm, sw_params), "cannot set software parameters");
117 alsaCall(snd_pcm_nonblock(apcm, 0), "cannot set blocking mode");
118 //alsaCall(snd_pcm_nonblock(apcm, 1), "cannot set non-blocking mode");
121 import core.stdc.stdlib : realloc;
122 sndsilence = cast(short*)realloc(sndsilence, sndSamplesSize*2*short.sizeof);
123 if (sndsilence is null) throw new FollinException("out of memory"); // `new` when `malloc` failed, nice
124 //sndsilence.length = sndSamplesSize*2; // frames->samples
125 sndsilence[0..sndSamplesSize*2] = 0;
130 // return `true` if good sample buffer was consumed
131 package(iv.follin) bool sndWriteBuffer (ref bool playbackStarted) {
132 version(follin_threads_debug) { import core.stdc.stdio; printf("playing %u buffer\n", atomicLoad(sndbufToPlay)); }
133 // now start playback, if it's necessary
134 if (!playbackStarted) {
135 if (snd_pcm_prepare(apcm) != 0) return false; // alas
136 if (snd_pcm_start(apcm) != 0) return false; // alas
137 playbackStarted = true;
139 // wait and write
140 waitnwrite: for (;;) {
141 auto avail = snd_pcm_avail/*_update*/(apcm); // "_update" for mmaped i/o
142 if (avail < 0) {
143 import core.stdc.errno : EPIPE;
144 import core.stdc.stdio;
145 if (avail != -EPIPE) {
146 fprintf(stderr, "ALSA ERROR: %s\n", snd_strerror(cast(int)avail));
148 snd_pcm_recover(apcm, cast(int)avail, 1);
149 //playbackStarted = false; //FIXME
150 //return false;
151 goto waitnwrite;
153 // now wait or write
154 auto used = realBufSize-avail;
155 if (used <= sndSamplesSize/2) {
156 // have room to write
157 bool res = true;
158 version(follin_write_debug) { import core.stdc.stdio; printf("avail: %u; used: %u; under: %u\n", cast(uint)avail, cast(uint)used, cast(uint)(sndSamplesSize/2-used)); }
159 version(follin_radio_silence_debug) { import core.stdc.stdio; if (sndSamplesSize-(sndSamplesSize/2-used) < 256) printf("radio silence: too much input buffer drained: %u\n", cast(uint)(sndSamplesSize/2-used)); }
160 auto paused = atomicLoad(sndPaused);
161 auto b2p = atomicLoad(sndbufToPlay);
162 auto bpos = (!paused ? sndbufptr[b2p] : sndsilence);
163 if (atomicLoad(sndbufToFill) == b2p && atomicLoad(sndbufFillingNow)) {
164 // radio silence
165 //bpos = sndsilence.ptr;
166 res = false;
167 version(follin_radio_silence_debug) { import core.stdc.stdio; printf("radio silence!\n"); }
169 if (paused) res = false;
170 snd_pcm_sframes_t err;
171 snd_pcm_sframes_t fleft = sndSamplesSize/2/*numchans*/;
172 while (fleft > 0) {
173 err = snd_pcm_writei(apcm, bpos, fleft);
174 if (err < 0) {
175 import core.stdc.stdio : fprintf, stderr;
176 import core.stdc.errno : EPIPE;
177 if (err == -EPIPE) {
178 fprintf(stderr, "ALSA: underrun!\n");
179 } else if (err == -11) {
180 // we can't write, that's wrong
181 fprintf(stderr, "ALSA: write failed (%s)\n", snd_strerror(cast(int)err));
182 } else {
183 fprintf(stderr, "ALSA: write failed %d (%s)\n", cast(int)err, snd_strerror(cast(int)err));
185 snd_pcm_recover(apcm, cast(int)err, 1);
186 fleft = sndSamplesSize/2/*numchans*/;
187 bpos = sndsilence; // write silence instead
188 res = false;
189 continue waitnwrite;
191 //version(follin_write_debug) { import core.stdc.stdio; printf("Follin: written %u of %u frames\n", cast(uint)err, cast(uint)fleft); }
192 bpos += cast(uint)(err*2/*numchans*/);
193 fleft -= err;
195 return res;
196 } else {
197 // no room, wait
198 uint over = used-sndSamplesSize/2;
199 uint mcswait = 1000_0*over/441;
200 version(follin_write_debug) { import core.stdc.stdio; printf("avail: %u; need: %u; used: %u; over: %u; mcswait=%u\n", cast(uint)avail, cast(uint)sndSamplesSize, cast(uint)used, over, mcswait); }
201 if (mcswait >= 100) {
202 // one second is 1_000_000_000 nanoseconds or 1_000_000 microseconds or 1_000 milliseconds
203 import core.sys.posix.signal : timespec;
204 import core.sys.posix.time : nanosleep;
205 timespec ts = void;
206 ts.tv_sec = 0;
207 ts.tv_nsec = (mcswait-1)*1000; // micro to nano
208 nanosleep(&ts, null); // idc how much time was passed
212 assert(0);
216 // ////////////////////////////////////////////////////////////////////////// //
217 // alsa bindings
218 import core.stdc.config;
219 extern(C) nothrow @trusted @nogc:
220 package(iv.follin):
222 // alsa bindings (bare minimum)
223 pragma(lib, "asound");
225 struct snd_pcm_t {}
226 struct snd_pcm_hw_params_t {}
227 struct snd_pcm_sw_params_t {}
228 struct snd_async_handler_t {}
229 alias snd_async_callback_t = void function (snd_async_handler_t* handler);
231 alias snd_pcm_stream_t = int;
232 alias snd_pcm_access_t = int;
233 alias snd_pcm_format = int;
235 alias snd_pcm_uframes_t = c_ulong;
236 alias snd_pcm_sframes_t = c_long;
238 enum SND_PCM_STREAM_PLAYBACK = 0;
239 enum SND_PCM_ACCESS_MMAP_INTERLEAVED = 0;
240 enum SND_PCM_ACCESS_RW_INTERLEAVED = 3;
242 enum SND_PCM_FORMAT_S16_LE = 2;
243 enum SND_PCM_FORMAT_S16_BE = 3;
244 enum SND_PCM_FORMAT_FLOAT_LE = 14;
245 enum SND_PCM_FORMAT_FLOAT_BE = 15;
247 version(LittleEndian) {
248 enum SND_PCM_FORMAT_S16_NATIVE = SND_PCM_FORMAT_S16_LE;
249 enum SND_PCM_FORMAT_FLOAT_NATIVE = SND_PCM_FORMAT_FLOAT_LE;
250 } else {
251 enum SND_PCM_FORMAT_S16_NATIVE = SND_PCM_FORMAT_S16_BE;
252 enum SND_PCM_FORMAT_FLOAT_NATIVE = SND_PCM_FORMAT_FLOAT_BE;
256 const(char)* snd_strerror (int errnum);
258 int snd_pcm_open (snd_pcm_t** pcm, const(char)* name, snd_pcm_stream_t stream, int mode);
259 int snd_pcm_close (snd_pcm_t* pcm);
260 int snd_pcm_prepare (snd_pcm_t* pcm);
261 int snd_pcm_drop (snd_pcm_t* pcm);
262 int snd_pcm_drain (snd_pcm_t* pcm);
263 int snd_pcm_recover (snd_pcm_t* pcm, int err, int silent);
264 int snd_pcm_nonblock (snd_pcm_t* pcm, int nonblock);
265 int snd_pcm_abort (snd_pcm_t* pcm) { return snd_pcm_nonblock(pcm, 2); }
266 int snd_pcm_start (snd_pcm_t* pcm);
267 int snd_pcm_pause (snd_pcm_t* pcm, int enable);
269 int snd_async_add_pcm_handler (snd_async_handler_t** handler, snd_pcm_t* pcm, snd_async_callback_t callback, void* private_data);
270 snd_pcm_t* snd_async_handler_get_pcm (snd_async_handler_t* handler);
271 int snd_async_del_handler (snd_async_handler_t* handler);
273 int snd_pcm_wait (snd_pcm_t* pcm, int timeout);
274 int snd_pcm_avail_delay (snd_pcm_t* pcm, snd_pcm_sframes_t* availp, snd_pcm_sframes_t* delayp);
275 snd_pcm_sframes_t snd_pcm_avail (snd_pcm_t* pcm);
276 snd_pcm_sframes_t snd_pcm_avail_update (snd_pcm_t* pcm);
278 snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t* pcm, const(void)* buffer, snd_pcm_uframes_t size);
279 snd_pcm_sframes_t snd_pcm_writen (snd_pcm_t* pcm, void** bufs, snd_pcm_uframes_t size);
281 int snd_pcm_hw_params (snd_pcm_t* pcm, snd_pcm_hw_params_t* params);
283 int snd_pcm_hw_params_current (snd_pcm_t* pcm, snd_pcm_hw_params_t* params);
284 int snd_pcm_hw_params_any (snd_pcm_t* pcm, snd_pcm_hw_params_t* params);
285 void snd_pcm_hw_params_free (snd_pcm_hw_params_t* params);
286 int snd_pcm_hw_params_malloc (snd_pcm_hw_params_t** params);
287 int snd_pcm_hw_params_set_access (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_access_t);
288 int snd_pcm_hw_params_set_channels (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint chans);
289 int snd_pcm_hw_params_set_format (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_format);
290 int snd_pcm_hw_params_set_rate (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint val, int dir);
291 int snd_pcm_hw_params_set_rate_near (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint*, int*);
292 int snd_pcm_hw_params_set_rate_resample (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint val);
294 int snd_pcm_hw_params_test_buffer_size (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t val);
295 int snd_pcm_hw_params_set_buffer_size (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t val);
296 int snd_pcm_hw_params_set_buffer_size_min (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val);
297 int snd_pcm_hw_params_set_buffer_size_max (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val);
298 int snd_pcm_hw_params_set_buffer_size_minmax (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* min, snd_pcm_uframes_t* max);
299 int snd_pcm_hw_params_set_buffer_size_near (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val);
300 int snd_pcm_hw_params_set_buffer_size_first (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val);
301 int snd_pcm_hw_params_set_buffer_size_last (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val);
303 // nanoseconds
304 int snd_pcm_hw_params_test_buffer_time (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint val, int dir);
305 int snd_pcm_hw_params_set_buffer_time (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint val, int dir);
306 int snd_pcm_hw_params_set_buffer_time_min (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
307 int snd_pcm_hw_params_set_buffer_time_max (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
308 int snd_pcm_hw_params_set_buffer_time_minmax (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* min, int* mindir, uint* max, int* maxdir);
309 int snd_pcm_hw_params_set_buffer_time_near (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
310 int snd_pcm_hw_params_set_buffer_time_first (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
311 int snd_pcm_hw_params_set_buffer_time_last (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
313 int snd_pcm_hw_params_set_period_size (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t val, int dir);
314 int snd_pcm_hw_params_set_period_size_min (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val, int* dir);
315 int snd_pcm_hw_params_set_period_size_max (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val, int* dir);
316 int snd_pcm_hw_params_set_period_size_minmax (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* min, int* mindir, snd_pcm_uframes_t* max, int* maxdir);
317 int snd_pcm_hw_params_set_period_size_near (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val, int* dir);
318 int snd_pcm_hw_params_set_period_size_first (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val, int* dir);
319 int snd_pcm_hw_params_set_period_size_last (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, snd_pcm_uframes_t* val, int* dir);
320 int snd_pcm_hw_params_set_period_size_integer (snd_pcm_t* pcm, snd_pcm_hw_params_t* params);
322 // nanoseconds
323 int snd_pcm_hw_params_test_period_time (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint val, int dir);
324 int snd_pcm_hw_params_set_period_time (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint val, int dir);
325 int snd_pcm_hw_params_set_period_time_min (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
326 int snd_pcm_hw_params_set_period_time_max (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
327 int snd_pcm_hw_params_set_period_time_minmax (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* min, int* mindir, uint* max, int* maxdir);
328 int snd_pcm_hw_params_set_period_time_near (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
329 int snd_pcm_hw_params_set_period_time_first (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
330 int snd_pcm_hw_params_set_period_time_last (snd_pcm_t* pcm, snd_pcm_hw_params_t* params, uint* val, int* dir);
332 int snd_pcm_sw_params (snd_pcm_t* pcm, snd_pcm_sw_params_t* params);
333 int snd_pcm_sw_params_current (snd_pcm_t* pcm, snd_pcm_sw_params_t* params);
334 void snd_pcm_sw_params_free (snd_pcm_sw_params_t* params);
335 int snd_pcm_sw_params_malloc (snd_pcm_sw_params_t** params);
336 int snd_pcm_sw_params_set_avail_min (snd_pcm_t* pcm, snd_pcm_sw_params_t* params, snd_pcm_uframes_t val);
337 int snd_pcm_sw_params_set_start_threshold (snd_pcm_t* pcm, snd_pcm_sw_params_t* params, snd_pcm_uframes_t val);
339 alias snd_lib_error_handler_t = void function (const(char)* file, int line, const(char)* function_, int err, const(char)* fmt, ...);
340 int snd_lib_error_set_handler (snd_lib_error_handler_t handler);
342 private void alsa_message_fucker (const(char)* file, int line, const(char)* function_, int err, const(char)* fmt, ...) {}
344 private void fuck_alsa_messages () {
345 snd_lib_error_set_handler(&alsa_message_fucker);