1 /* MikMod sound library
2 (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 /*==============================================================================
25 Driver for Advanced Linux Sound Architecture (ALSA)
27 ==============================================================================*/
33 #include "mikmod_internals.h"
50 #include <alsa/asoundlib.h>
51 #if defined(SND_LIB_VERSION) && (SND_LIB_VERSION >= 0x20000)
55 #if defined(SND_LIB_VERSION) && (SND_LIB_VERSION < 0x600)
56 #error ALSA Version too old. Please upgrade your Linux distribution.
63 /* runtime link with libasound */
64 #ifndef HAVE_RTLD_GLOBAL
65 #define RTLD_GLOBAL (0)
67 static int (*alsa_pcm_subformat_mask_malloc)(snd_pcm_subformat_mask_t **);
68 static const char * (*alsa_strerror)(int);
69 static int (*alsa_pcm_resume)(snd_pcm_t *);
70 static int (*alsa_pcm_prepare)(snd_pcm_t *);
71 static int (*alsa_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *);
72 static int (*alsa_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *);
73 static int (*alsa_pcm_hw_params_current)(snd_pcm_t *, snd_pcm_hw_params_t *);
74 static int (*alsa_pcm_hw_params_set_access)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
75 static int (*alsa_pcm_hw_params_set_format)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
76 static int (*alsa_pcm_hw_params_set_rate_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
77 static int (*alsa_pcm_hw_params_set_channels_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *);
78 static int (*alsa_pcm_hw_params_set_buffer_time_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
79 static int (*alsa_pcm_hw_params_set_period_time_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
80 static int (*alsa_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
81 static int (*alsa_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
82 static int (*alsa_pcm_sw_params_sizeof)(void);
83 static int (*alsa_pcm_hw_params_sizeof)(void);
84 static int (*alsa_pcm_open)(snd_pcm_t**, const char *, int, int);
85 static int (*alsa_pcm_close)(snd_pcm_t*);
86 static int (*alsa_pcm_drain)(snd_pcm_t*);
87 static int (*alsa_pcm_drop)(snd_pcm_t*);
88 static int (*alsa_pcm_start)(snd_pcm_t *);
89 static snd_pcm_sframes_t (*alsa_pcm_writei)(snd_pcm_t*,const void*,snd_pcm_uframes_t);
91 static void* libasound = NULL;
94 /* compile-time link with libasound */
95 #define alsa_pcm_subformat_mask_malloc snd_pcm_subformat_mask_malloc
96 #define alsa_strerror snd_strerror
97 #define alsa_pcm_hw_params_any snd_pcm_hw_params_any
98 #define alsa_pcm_hw_params snd_pcm_hw_params
99 #define alsa_pcm_hw_params_current snd_pcm_hw_params_current
100 #define alsa_pcm_hw_params_set_access snd_pcm_hw_params_set_access
101 #define alsa_pcm_hw_params_set_format snd_pcm_hw_params_set_format
102 #define alsa_pcm_hw_params_set_rate_near snd_pcm_hw_params_set_rate_near
103 #define alsa_pcm_hw_params_set_channels_near snd_pcm_hw_params_set_channels_near
104 #define alsa_pcm_hw_params_set_buffer_time_near snd_pcm_hw_params_set_buffer_time_near
105 #define alsa_pcm_hw_params_set_period_time_near snd_pcm_hw_params_set_period_time_near
106 #define alsa_pcm_hw_params_get_buffer_size snd_pcm_hw_params_get_buffer_size
107 #define alsa_pcm_hw_params_get_period_size snd_pcm_hw_params_get_period_size
108 #define alsa_pcm_resume snd_pcm_resume
109 #define alsa_pcm_prepare snd_pcm_prepare
110 #define alsa_pcm_close snd_pcm_close
111 #define alsa_pcm_drain snd_pcm_drain
112 #define alsa_pcm_drop snd_pcm_drop
113 #define alsa_pcm_start snd_pcm_start
114 #define alsa_pcm_open snd_pcm_open
115 #define alsa_pcm_writei snd_pcm_writei
116 #endif /* MIKMOD_DYNAMIC */
118 #if defined(MIKMOD_DEBUG)
119 # define dbgprint fprintf
120 #elif defined (__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
121 # define dbgprint(f, fmt, args...) do {} while (0)
123 # define dbgprint(f, ...) do {} while (0)
125 static BOOL enabled = 0;
126 static snd_pcm_t *pcm_h = NULL;
127 static SBYTE *audiobuffer = NULL;
128 static snd_pcm_sframes_t period_size;
129 static int bytes_written = 0, bytes_played = 0;
130 static int global_frame_size;
132 #ifdef MIKMOD_DYNAMIC
133 static int ALSA_Link(void)
135 if (libasound) return 0;
137 /* load libasound.so */
138 libasound = dlopen("libasound.so.2",RTLD_LAZY|RTLD_GLOBAL);
139 if (!libasound) libasound = dlopen("libasound.so",RTLD_LAZY|RTLD_GLOBAL);
140 if (!libasound) return 1;
142 if (!(alsa_pcm_subformat_mask_malloc = (int (*)(snd_pcm_subformat_mask_t **))
143 dlsym(libasound,"snd_pcm_subformat_mask_malloc"))) return 1;
144 if (!(alsa_strerror = (const char* (*)(int))
145 dlsym(libasound,"snd_strerror"))) return 1;
146 if (!(alsa_pcm_prepare = (int (*)(snd_pcm_t *))
147 dlsym(libasound,"snd_pcm_prepare"))) return 1;
148 if (!(alsa_pcm_sw_params_sizeof = (int (*)(void))
149 dlsym(libasound,"snd_pcm_sw_params_sizeof"))) return 1;
150 if (!(alsa_pcm_hw_params_sizeof = (int (*)(void))
151 dlsym(libasound,"snd_pcm_hw_params_sizeof"))) return 1;
152 if (!(alsa_pcm_resume = (int (*)(snd_pcm_t *))
153 dlsym(libasound,"snd_pcm_resume"))) return 1;
154 if (!(alsa_pcm_hw_params_any = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *))
155 dlsym(libasound,"snd_pcm_hw_params_any"))) return 1;
156 if (!(alsa_pcm_hw_params = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *))
157 dlsym(libasound,"snd_pcm_hw_params"))) return 1;
158 if (!(alsa_pcm_hw_params_current = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *))
159 dlsym(libasound,"snd_pcm_hw_params_current"))) return 1;
160 if (!(alsa_pcm_hw_params_set_access = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t))
161 dlsym(libasound,"snd_pcm_hw_params_set_access"))) return 1;
162 if (!(alsa_pcm_hw_params_set_format = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t))
163 dlsym(libasound,"snd_pcm_hw_params_set_format"))) return 1;
164 if (!(alsa_pcm_hw_params_set_rate_near = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *))
165 dlsym(libasound,"snd_pcm_hw_params_set_rate_near"))) return 1;
166 if (!(alsa_pcm_hw_params_set_channels_near = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *))
167 dlsym(libasound,"snd_pcm_hw_params_set_channels_near"))) return 1;
168 if (!(alsa_pcm_hw_params_set_buffer_time_near = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *))
169 dlsym(libasound,"snd_pcm_hw_params_set_buffer_time_near"))) return 1;
170 if (!(alsa_pcm_hw_params_set_period_time_near = (int (*)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *))
171 dlsym(libasound,"snd_pcm_hw_params_set_period_time_near"))) return 1;
172 if (!(alsa_pcm_hw_params_get_buffer_size = (int (*)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *))
173 dlsym(libasound,"snd_pcm_hw_params_get_buffer_size"))) return 1;
174 if (!(alsa_pcm_hw_params_get_period_size = (int (*)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *))
175 dlsym(libasound,"snd_pcm_hw_params_get_period_size"))) return 1;
176 if (!(alsa_pcm_open = (int (*)(snd_pcm_t**, const char *, int, int))
177 dlsym(libasound,"snd_pcm_open"))) return 1;
178 if (!(alsa_pcm_close = (int (*)(snd_pcm_t*))
179 dlsym(libasound,"snd_pcm_close"))) return 1;
180 if (!(alsa_pcm_drain = (int (*)(snd_pcm_t*))
181 dlsym(libasound,"snd_pcm_drain"))) return 1;
182 if (!(alsa_pcm_drop = (int (*)(snd_pcm_t*))
183 dlsym(libasound,"snd_pcm_drop"))) return 1;
184 if (!(alsa_pcm_start = (int (*)(snd_pcm_t *))
185 dlsym(libasound,"snd_pcm_start"))) return 1;
186 if (!(alsa_pcm_writei = (snd_pcm_sframes_t (*)(snd_pcm_t*,const void*,snd_pcm_uframes_t))
187 dlsym(libasound,"snd_pcm_writei"))) return 1;
192 static void ALSA_Unlink(void)
194 alsa_pcm_subformat_mask_malloc = NULL;
195 alsa_strerror = NULL;
196 alsa_pcm_resume = NULL;
197 alsa_pcm_prepare = NULL;
198 alsa_pcm_hw_params_any = NULL;
199 alsa_pcm_hw_params = NULL;
200 alsa_pcm_hw_params_current = NULL;
201 alsa_pcm_hw_params_set_access = NULL;
202 alsa_pcm_hw_params_set_format = NULL;
203 alsa_pcm_hw_params_set_rate_near = NULL;
204 alsa_pcm_hw_params_set_channels_near = NULL;
205 alsa_pcm_hw_params_set_buffer_time_near = NULL;
206 alsa_pcm_hw_params_set_period_time_near = NULL;
207 alsa_pcm_hw_params_get_buffer_size = NULL;
208 alsa_pcm_hw_params_get_period_size = NULL;
209 alsa_pcm_close = NULL;
210 alsa_pcm_drain = NULL;
211 alsa_pcm_drop = NULL;
212 alsa_pcm_start = NULL;
213 alsa_pcm_open = NULL;
214 alsa_pcm_writei = NULL;
222 /* This is done to override the identifiers expanded
223 * in the macros provided by the ALSA includes which are
226 #define snd_strerror alsa_strerror
227 #define snd_pcm_sw_params_sizeof alsa_pcm_sw_params_sizeof
228 #define snd_pcm_hw_params_sizeof alsa_pcm_hw_params_sizeof
229 #endif /* MIKMOD_DYNAMIC */
231 static void ALSA_CommandLine(const CHAR *cmdline)
236 static BOOL ALSA_IsThere(void)
238 snd_pcm_subformat_mask_t *ptr = NULL;
241 #ifdef MIKMOD_DYNAMIC
242 if (ALSA_Link()) return 0;
244 retval = (alsa_pcm_subformat_mask_malloc(&ptr) == 0) && (ptr != NULL);
246 #ifdef MIKMOD_DYNAMIC
252 static int ALSA_Init_internal(void)
254 snd_pcm_format_t pformat;
255 unsigned int btime = 250000; /* 250ms */
256 unsigned int ptime = 50000; /* 50ms */
257 snd_pcm_uframes_t psize;
258 snd_pcm_uframes_t bsize;
259 unsigned int rate, channels;
260 snd_pcm_hw_params_t * hwparams;
263 /* setup playback format structure */
264 pformat = (md_mode&DMODE_FLOAT)? SND_PCM_FORMAT_FLOAT :
265 (md_mode&DMODE_16BITS)? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8;
266 channels = (md_mode&DMODE_STEREO)?2:1;
269 #define MIKMOD_ALSA_DEVICE "default"
270 if ((err = alsa_pcm_open(&pcm_h, MIKMOD_ALSA_DEVICE, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
271 _mm_errno = MMERR_OPENING_AUDIO;
275 snd_pcm_hw_params_alloca(&hwparams);
276 err = alsa_pcm_hw_params_any(pcm_h, hwparams);
278 _mm_errno = MMERR_ALSA_NOCONFIG;
282 err = alsa_pcm_hw_params_set_access(pcm_h, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
283 if (!err) err = alsa_pcm_hw_params_set_format(pcm_h, hwparams, pformat);
284 if (!err) err = alsa_pcm_hw_params_set_rate_near(pcm_h, hwparams, &rate, NULL);
285 if (!err) err = alsa_pcm_hw_params_set_channels_near(pcm_h, hwparams, &channels);
286 if (!err) err = alsa_pcm_hw_params_set_buffer_time_near(pcm_h, hwparams, &btime, NULL);
287 if (!err) err = alsa_pcm_hw_params_set_period_time_near(pcm_h, hwparams, &ptime, NULL);
288 if (!err) err = alsa_pcm_hw_params(pcm_h, hwparams);
290 _mm_errno = MMERR_ALSA_SETPARAMS;
294 if (rate != md_mixfreq) {
295 _mm_errno = MMERR_ALSA_SETRATE;
298 if (!(md_mode&DMODE_STEREO) && channels != 1) {
299 _mm_errno = MMERR_ALSA_SETCHANNELS;
302 if ((md_mode&DMODE_STEREO) && channels != 2) {
303 _mm_errno = MMERR_ALSA_SETCHANNELS;
307 err = alsa_pcm_hw_params_current(pcm_h, hwparams);
308 if (!err) err = alsa_pcm_hw_params_get_buffer_size(hwparams, &bsize);
309 if (!err) err = alsa_pcm_hw_params_get_period_size(hwparams, &psize, NULL);
311 _mm_errno = MMERR_ALSA_BUFFERSIZE;
316 global_frame_size = channels *
317 ((md_mode&DMODE_FLOAT)? 4 : (md_mode&DMODE_16BITS)? 2 : 1);
319 if (!(audiobuffer=(SBYTE*)MikMod_malloc(period_size * global_frame_size))) {
320 _mm_errno = MMERR_OUT_OF_MEMORY;
324 /* sound device is ready to work */
330 alsa_pcm_close(pcm_h);
335 static int ALSA_Init(void)
338 /* TODO : Detect SSE2, then set md_mode |= DMODE_SIMDMIXER;*/
340 #ifdef MIKMOD_DYNAMIC
342 _mm_errno=MMERR_DYNAMIC_LINKING;
346 return ALSA_Init_internal();
349 static void ALSA_Exit_internal(void)
354 alsa_pcm_drain(pcm_h);
355 alsa_pcm_close(pcm_h);
358 MikMod_free(audiobuffer);
362 static void ALSA_Exit(void)
364 ALSA_Exit_internal();
365 #ifdef MIKMOD_DYNAMIC
370 /* Underrun and suspend recovery - from alsa-lib:test/pcm.c
372 static int xrun_recovery(snd_pcm_t *handle, int err)
374 if (err == -EPIPE) { /* under-run */
375 err = alsa_pcm_prepare(handle);
377 dbgprint(stderr, "Can't recover from underrun, prepare failed: %s\n", snd_strerror(err));
380 else if (err == -ESTRPIPE) {
381 while ((err = alsa_pcm_resume(handle)) == -EAGAIN)
382 sleep(1); /* wait until the suspend flag is released */
384 err = alsa_pcm_prepare(handle);
386 dbgprint(stderr, "Can't recover from suspend, prepare failed: %s\n", snd_strerror(err));
393 static void ALSA_Update(void)
397 if (!enabled) return;
399 if (bytes_written == 0 || bytes_played == bytes_written) {
400 bytes_written = VC_WriteBytes(audiobuffer,period_size * global_frame_size);
404 while (bytes_played < bytes_written)
406 err = alsa_pcm_writei(pcm_h, &audiobuffer[bytes_played], (bytes_written - bytes_played) / global_frame_size);
410 if ((err = xrun_recovery(pcm_h, err)) < 0) {
411 _mm_errno = MMERR_ALSA_PCM_RECOVER;
413 dbgprint(stderr, "Write error: %s\n", alsa_strerror(err));
417 bytes_played += err * global_frame_size;
421 static int ALSA_PlayStart(void)
425 if (pcm_h == NULL) return 1;
426 err = alsa_pcm_prepare(pcm_h);
428 err = alsa_pcm_start(pcm_h);
431 _mm_errno = MMERR_ALSA_PCM_START;
435 return VC_PlayStart();
438 static void ALSA_PlayStop(void)
441 if (pcm_h) alsa_pcm_drop(pcm_h);
444 static int ALSA_Reset(void)
446 ALSA_Exit_internal();
447 return ALSA_Init_internal();
450 MIKMODAPI MDRIVER drv_alsa = {
453 "Advanced Linux Sound Architecture (ALSA) driver v1.11",
473 VC_VoiceSetFrequency,
474 VC_VoiceGetFrequency,