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
;
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*/ {
41 if (sndsilence
!is null) {
42 import core
.stdc
.stdlib
: free
;
50 uint nextPowerOf2 (uint n
) {
51 //static if (__VERSION__ > 2067) pragma(inline, true);
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
;
69 static void alsaCall (int err
, string msg
) @trusted {
71 import std
.string
: fromStringz
;
72 throw new FollinException((msg
~" ("~snd_strerror(err
).fromStringz
~")").idup
);
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");
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");
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");
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;
140 waitnwrite
: for (;;) {
141 auto avail
= snd_pcm_avail
/*_update*/(apcm
); // "_update" for mmaped i/o
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
154 auto used
= realBufSize
-avail
;
155 if (used
<= sndSamplesSize
/2) {
156 // have room to write
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
)) {
165 //bpos = sndsilence.ptr;
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*/;
173 err
= snd_pcm_writei(apcm
, bpos
, fleft
);
175 import core
.stdc
.stdio
: fprintf
, stderr
;
176 import core
.stdc
.errno
: 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
));
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
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*/);
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
;
207 ts
.tv_nsec
= (mcswait
-1)*1000; // micro to nano
208 nanosleep(&ts
, null); // idc how much time was passed
216 // ////////////////////////////////////////////////////////////////////////// //
218 import core
.stdc
.config
;
219 extern(C
) nothrow @trusted @nogc:
222 // alsa bindings (bare minimum)
223 pragma(lib
, "asound");
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
;
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
);
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
);
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
);