1 /*
2 * Copyright (C) 2004-2005 Vadim Berezniker
3 * http://www.kryptolus.com
4 *
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; either version 2, or (at your option)
8 * any later version.
9 *
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 General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Make; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21 #include "stdafx.h"
22
23 #include "common.h"
24
25 #include <alsa/asoundlib.h>
26
27 #include "sabbu.h"
28 #include "sound.h"
29 #include "sound_out.h"
30
31 #define BUFFER_LEN 64
32
33 void soundserver_fill(struct sound_play_data *data);
34 void mySoundProc(void *pSoundBuffer,long bufferLen);
35 void soundserver_close(void);
36
37 gboolean soundserver_force_quit = FALSE;
38 gboolean m_bServerRunning = FALSE;
39 USER_CALLBACK m_pUserCallback = NULL;
40 GMutex *running_mutex = NULL;
41 GMutex *quit_mutex = NULL;
42 snd_pcm_t *wave_handle = NULL;
43 gboolean m_soundserver_mute[2];
44
45
soundserver_open(USER_CALLBACK pUserCallback,int channels,int samplerate)46 int soundserver_open(USER_CALLBACK pUserCallback, int channels, int samplerate)
47 {
48 snd_pcm_hw_params_t *hardware;
49 snd_pcm_uframes_t buffer_size, xfer_align, start_threshold ;
50 char *device = "default";
51 int exact_rate;
52 snd_pcm_uframes_t periodsize = 512;
53 snd_pcm_uframes_t buffer_frames = 3 * periodsize;
54 int err;
55 snd_pcm_sw_params_t *sw_params;
56
57
58 soundserver_force_quit = FALSE;
59
60 soundserver_stop();
61
62 m_pUserCallback = pUserCallback;
63
64 snd_pcm_hw_params_malloc(&hardware);
65
66 if(snd_pcm_open(&wave_handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0)
67 return FALSE;
68
69 snd_pcm_nonblock(wave_handle, 0);
70
71 if(snd_pcm_hw_params_any(wave_handle, hardware) < 0)
72 return FALSE;
73
74 if(snd_pcm_hw_params_set_access(wave_handle, hardware, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
75 {
76 g_warning("Unable to set access type");
77 return FALSE;
78 }
79
80 if ((err = snd_pcm_hw_params_set_format (wave_handle, hardware, SND_PCM_FORMAT_S16)) < 0)
81 {
82 g_warning("Unable to set format");
83 return FALSE;
84 }
85
86 exact_rate = snd_pcm_hw_params_set_rate_near(wave_handle, hardware, (unsigned int *) &samplerate, 0);
87
88 if(snd_pcm_hw_params_set_channels(wave_handle, hardware, channels) < 0)
89 {
90 g_warning("Unable to set number of channels");
91 return FALSE;
92 }
93
94 if(snd_pcm_hw_params_set_period_size_near(wave_handle, hardware, &periodsize, 0) < 0)
95 {
96 g_warning("Unable to set period");
97 return FALSE;
98 }
99
100 if(snd_pcm_hw_params_set_buffer_size_near(wave_handle, hardware, &buffer_frames) < 0)
101 {
102 g_warning("Unable to set buffer size");
103 return FALSE;
104 }
105
106 if(snd_pcm_hw_params(wave_handle, hardware) < 0)
107 return FALSE;
108
109 /* extra check: if we have only one period, this code won't work */
110 snd_pcm_hw_params_get_period_size (hardware, &periodsize, 0) ;
111 snd_pcm_hw_params_get_buffer_size (hardware, &buffer_size) ;
112 if (periodsize == buffer_size)
113 return FALSE;
114
115 snd_pcm_hw_params_free (hardware) ;
116
117 if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0)
118 return FALSE;
119
120 if ((err = snd_pcm_sw_params_current (wave_handle, sw_params)) != 0)
121 return FALSE;
122
123 /* note: set start threshold to delay start until the ring buffer is full */
124 snd_pcm_sw_params_current (wave_handle, sw_params) ;
125 if ((err = snd_pcm_sw_params_get_xfer_align (sw_params, &xfer_align)) < 0)
126 return FALSE;
127
128 /* round up to closest transfer boundary */
129 start_threshold = (buffer_size / xfer_align) * xfer_align ;
130 if (start_threshold < 1)
131 start_threshold = 1 ;
132 if ((err = snd_pcm_sw_params_set_start_threshold (wave_handle, sw_params, start_threshold)) < 0)
133 return FALSE;
134
135 if ((err = snd_pcm_sw_params (wave_handle, sw_params)) != 0)
136 return FALSE;
137 snd_pcm_sw_params_free (sw_params) ;
138 snd_pcm_reset (wave_handle) ;
139
140 return TRUE;
141 }
142
soundserver_fill(struct sound_play_data * info)143 void soundserver_fill(struct sound_play_data *info)
144 {
145 static short buffer [BUFFER_LEN];
146 char *data = (char *) buffer;
147 int readcount;
148 int frames;
149 int channels;
150
151 while ((readcount = m_pUserCallback(buffer,BUFFER_LEN * 2, info)))
152 {
153 int total = 0;
154 int retval;
155 frames = BUFFER_LEN / info->sfinfo->channels;
156 channels = info->sfinfo->channels;
157
158 g_mutex_lock(quit_mutex);
159 if(soundserver_force_quit)
160 {
161 g_mutex_unlock(quit_mutex);
162 soundserver_close();
163 break;
164 }
165 g_mutex_unlock(quit_mutex);
166
167
168 while (total < frames)
169 {
170 retval = snd_pcm_writei (wave_handle, data + total * channels, frames - total) ;
171
172 if (retval >= 0)
173 {
174 total += retval;
175 if (total == frames)
176 break;
177
178 continue;
179 }
180
181 if(retval == -EPIPE)
182 {
183 snd_pcm_prepare (wave_handle);
184 }
185 else
186 {
187 g_warning("error writing sound data: %d\n", retval) ;
188 return;
189 }
190 }
191 }
192
193 snd_pcm_close (wave_handle);
194 soundserver_close();
195 }
196
soundserver_close(void)197 void soundserver_close(void)
198 {
199 g_mutex_lock(running_mutex);
200 m_bServerRunning = FALSE;
201 g_mutex_unlock(running_mutex);
202
203 gui_main_audio_play_mode(SOUND_NOT_PLAYING);
204 sound_signal_pos_change(-1);
205 }
206
soundserver_stop()207 void soundserver_stop()
208 {
209 int slept_count = 0;
210 g_mutex_lock(quit_mutex);
211 if(!soundserver_force_quit)
212 {
213 soundserver_force_quit = TRUE;
214 g_mutex_unlock(quit_mutex);
215 while(1)
216 {
217 slept_count++;
218 if(slept_count == 10)
219 {
220 g_warning("slept_count == 10, this is not good");
221 g_mutex_unlock(quit_mutex);
222 m_bServerRunning = FALSE;
223 break;
224 }
225
226 kry_sleep(100);
227
228 g_mutex_lock(running_mutex);
229 if(!m_bServerRunning)
230 break;
231 g_mutex_unlock(running_mutex);
232 }
233 // when the loop breaks, the mutex remains locked
234 g_mutex_unlock(running_mutex);
235 }
236 else
237 {
238 g_mutex_unlock(quit_mutex);
239 }
240
241 soundserver_force_quit = FALSE;
242 return;
243 }
244
soundserver_init()245 void soundserver_init()
246 {
247 quit_mutex = g_mutex_new();
248 running_mutex = g_mutex_new();
249 }
250
soundserver_mute(int channel)251 void soundserver_mute(int channel)
252 {
253 m_soundserver_mute[channel] = 1;
254 }
255
soundserver_play(struct sound_play_data * data)256 void soundserver_play(struct sound_play_data *data)
257 {
258 g_thread_create((GThreadFunc) soundserver_fill, data, FALSE, NULL);
259 }
260
soundserver_get_position_type()261 enum sound_position_type soundserver_get_position_type()
262 {
263 return POSITION_AUTOMATIC;
264 }
265
soundserver_get_position(int * pos)266 enum soundserver_position_rv soundserver_get_position(int *pos)
267 {
268 return SOUNDSERVER_ERROR;
269 }
270
271
272
273