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