Commit | Line | Data |
---|---|---|
ef79bbde P |
1 | /*************************************************************************** |
2 | alsa.c - description | |
3 | ------------------- | |
4 | begin : Sat Mar 01 2003 | |
5 | copyright : (C) 2002 by Pete Bernert | |
6 | email : BlackDove@addcom.de | |
7 | ***************************************************************************/ | |
8 | /*************************************************************************** | |
9 | * * | |
10 | * This program is free software; you can redistribute it and/or modify * | |
11 | * it under the terms of the GNU General Public License as published by * | |
12 | * the Free Software Foundation; either version 2 of the License, or * | |
13 | * (at your option) any later version. See also the license.txt file for * | |
14 | * additional informations. * | |
15 | * * | |
16 | ***************************************************************************/ | |
17 | ||
07c13dfd | 18 | #include <stdio.h> |
ef79bbde P |
19 | #define ALSA_PCM_NEW_HW_PARAMS_API |
20 | #define ALSA_PCM_NEW_SW_PARAMS_API | |
21 | #include <alsa/asoundlib.h> | |
07c13dfd | 22 | #include "out.h" |
ef79bbde P |
23 | |
24 | static snd_pcm_t *handle = NULL; | |
25 | static snd_pcm_uframes_t buffer_size; | |
26 | ||
1b908f9e | 27 | static void alsa_finish(void); |
28 | ||
ef79bbde | 29 | // SETUP SOUND |
07c13dfd | 30 | static int alsa_init(void) |
ef79bbde P |
31 | { |
32 | snd_pcm_hw_params_t *hwparams; | |
33 | snd_pcm_status_t *status; | |
1b908f9e | 34 | snd_ctl_t *ctl_handle = NULL; |
35 | snd_ctl_card_info_t *info; | |
ef79bbde P |
36 | unsigned int pspeed; |
37 | int pchannels; | |
38 | int format; | |
39 | unsigned int buffer_time = 100000; | |
40 | unsigned int period_time = buffer_time / 4; | |
1b908f9e | 41 | const char *alsa_name = "default"; |
42 | const char *name; | |
43 | int retval = -1; | |
ef79bbde P |
44 | int err; |
45 | ||
1b908f9e | 46 | name = getenv("ALSA_NAME"); |
47 | if (name != NULL) | |
48 | alsa_name = name; | |
49 | ||
50 | snd_ctl_card_info_alloca(&info); | |
51 | if ((err = snd_ctl_open(&ctl_handle, alsa_name, 0)) < 0) { | |
52 | printf("control open: %s\n", snd_strerror(err)); | |
53 | } | |
54 | else if ((err = snd_ctl_card_info(ctl_handle, info)) < 0) { | |
55 | printf("control info: %s\n", snd_strerror(err)); | |
56 | snd_ctl_card_info_clear(info); | |
57 | } | |
58 | if (ctl_handle != NULL) | |
59 | snd_ctl_close(ctl_handle); | |
60 | ||
61 | name = snd_ctl_card_info_get_name(info); | |
62 | if (name != NULL) { | |
63 | if (strcasecmp(name, "PulseAudio") == 0) { | |
64 | // PulseAudio's ALSA emulation is known to be broken.. | |
65 | printf("alsa: refusing to run under PulseAudio's emulation\n"); | |
66 | return -1; | |
67 | } | |
68 | else { | |
69 | printf("alsa: using '%s', set ALSA_NAME to change\n", name); | |
70 | } | |
71 | } | |
72 | ||
97ea4077 | 73 | pchannels=2; |
ef79bbde P |
74 | |
75 | pspeed = 44100; | |
76 | format = SND_PCM_FORMAT_S16; | |
77 | ||
1b908f9e | 78 | if ((err = snd_pcm_open(&handle, alsa_name, |
ef79bbde P |
79 | SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) |
80 | { | |
81 | printf("Audio open error: %s\n", snd_strerror(err)); | |
07c13dfd | 82 | return -1; |
ef79bbde P |
83 | } |
84 | ||
85 | if((err = snd_pcm_nonblock(handle, 0))<0) | |
86 | { | |
87 | printf("Can't set blocking moded: %s\n", snd_strerror(err)); | |
1b908f9e | 88 | goto out; |
ef79bbde P |
89 | } |
90 | ||
91 | snd_pcm_hw_params_alloca(&hwparams); | |
92 | ||
93 | if((err=snd_pcm_hw_params_any(handle, hwparams))<0) | |
94 | { | |
95 | printf("Broken configuration for this PCM: %s\n", snd_strerror(err)); | |
1b908f9e | 96 | goto out; |
ef79bbde P |
97 | } |
98 | ||
99 | if((err=snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED))<0) | |
100 | { | |
101 | printf("Access type not available: %s\n", snd_strerror(err)); | |
1b908f9e | 102 | goto out; |
ef79bbde P |
103 | } |
104 | ||
105 | if((err=snd_pcm_hw_params_set_format(handle, hwparams, format))<0) | |
106 | { | |
107 | printf("Sample format not available: %s\n", snd_strerror(err)); | |
1b908f9e | 108 | goto out; |
ef79bbde P |
109 | } |
110 | ||
111 | if((err=snd_pcm_hw_params_set_channels(handle, hwparams, pchannels))<0) | |
112 | { | |
113 | printf("Channels count not available: %s\n", snd_strerror(err)); | |
1b908f9e | 114 | goto out; |
ef79bbde P |
115 | } |
116 | ||
117 | if((err=snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0))<0) | |
118 | { | |
119 | printf("Rate not available: %s\n", snd_strerror(err)); | |
1b908f9e | 120 | goto out; |
ef79bbde P |
121 | } |
122 | ||
123 | if((err=snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0))<0) | |
124 | { | |
125 | printf("Buffer time error: %s\n", snd_strerror(err)); | |
1b908f9e | 126 | goto out; |
ef79bbde P |
127 | } |
128 | ||
129 | if((err=snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0))<0) | |
130 | { | |
131 | printf("Period time error: %s\n", snd_strerror(err)); | |
1b908f9e | 132 | goto out; |
ef79bbde P |
133 | } |
134 | ||
135 | if((err=snd_pcm_hw_params(handle, hwparams))<0) | |
136 | { | |
137 | printf("Unable to install hw params: %s\n", snd_strerror(err)); | |
1b908f9e | 138 | goto out; |
ef79bbde P |
139 | } |
140 | ||
141 | snd_pcm_status_alloca(&status); | |
142 | if((err=snd_pcm_status(handle, status))<0) | |
143 | { | |
144 | printf("Unable to get status: %s\n", snd_strerror(err)); | |
1b908f9e | 145 | goto out; |
ef79bbde P |
146 | } |
147 | ||
148 | buffer_size = snd_pcm_status_get_avail(status); | |
1b908f9e | 149 | retval = 0; |
150 | ||
151 | out: | |
152 | if (retval != 0) | |
153 | alsa_finish(); | |
154 | return retval; | |
ef79bbde P |
155 | } |
156 | ||
157 | // REMOVE SOUND | |
07c13dfd | 158 | static void alsa_finish(void) |
ef79bbde P |
159 | { |
160 | if(handle != NULL) | |
161 | { | |
162 | snd_pcm_drop(handle); | |
163 | snd_pcm_close(handle); | |
164 | handle = NULL; | |
165 | } | |
166 | } | |
167 | ||
168 | // GET BYTES BUFFERED | |
07c13dfd | 169 | static int alsa_busy(void) |
ef79bbde | 170 | { |
07c13dfd | 171 | int l; |
ef79bbde P |
172 | |
173 | if (handle == NULL) // failed to open? | |
f8edb5bc | 174 | return 1; |
ef79bbde P |
175 | l = snd_pcm_avail(handle); |
176 | if (l < 0) return 0; | |
177 | if (l < buffer_size / 2) // can we write in at least the half of fragments? | |
f8edb5bc | 178 | l = 1; // -> no? wait |
ef79bbde P |
179 | else l = 0; // -> else go on |
180 | ||
181 | return l; | |
182 | } | |
183 | ||
184 | // FEED SOUND DATA | |
07c13dfd | 185 | static void alsa_feed(void *pSound, int lBytes) |
ef79bbde P |
186 | { |
187 | if (handle == NULL) return; | |
188 | ||
189 | if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN) | |
190 | snd_pcm_prepare(handle); | |
97ea4077 | 191 | snd_pcm_writei(handle,pSound, lBytes / 4); |
ef79bbde | 192 | } |
07c13dfd | 193 | |
194 | void out_register_alsa(struct out_driver *drv) | |
195 | { | |
196 | drv->name = "alsa"; | |
197 | drv->init = alsa_init; | |
198 | drv->finish = alsa_finish; | |
199 | drv->busy = alsa_busy; | |
200 | drv->feed = alsa_feed; | |
201 | } |