/* * Copyright (C) 2004-2005 Vadim Berezniker * http://www.kryptolus.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "common.h" #include #include "sabbu.h" #include "sound.h" #include "sound_out.h" #define BUFFER_LEN 64 void soundserver_fill(struct sound_play_data *data); void mySoundProc(void *pSoundBuffer,long bufferLen); void soundserver_close(void); gboolean soundserver_force_quit = FALSE; gboolean m_bServerRunning = FALSE; USER_CALLBACK m_pUserCallback = NULL; GMutex *running_mutex = NULL; GMutex *quit_mutex = NULL; snd_pcm_t *wave_handle = NULL; gboolean m_soundserver_mute[2]; int soundserver_open(USER_CALLBACK pUserCallback, int channels, int samplerate) { snd_pcm_hw_params_t *hardware; snd_pcm_uframes_t buffer_size, xfer_align, start_threshold ; char *device = "default"; int exact_rate; snd_pcm_uframes_t periodsize = 512; snd_pcm_uframes_t buffer_frames = 3 * periodsize; int err; snd_pcm_sw_params_t *sw_params; soundserver_force_quit = FALSE; soundserver_stop(); m_pUserCallback = pUserCallback; snd_pcm_hw_params_malloc(&hardware); if(snd_pcm_open(&wave_handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return FALSE; snd_pcm_nonblock(wave_handle, 0); if(snd_pcm_hw_params_any(wave_handle, hardware) < 0) return FALSE; if(snd_pcm_hw_params_set_access(wave_handle, hardware, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { g_warning("Unable to set access type"); return FALSE; } if ((err = snd_pcm_hw_params_set_format (wave_handle, hardware, SND_PCM_FORMAT_S16)) < 0) { g_warning("Unable to set format"); return FALSE; } exact_rate = snd_pcm_hw_params_set_rate_near(wave_handle, hardware, (unsigned int *) &samplerate, 0); if(snd_pcm_hw_params_set_channels(wave_handle, hardware, channels) < 0) { g_warning("Unable to set number of channels"); return FALSE; } if(snd_pcm_hw_params_set_period_size_near(wave_handle, hardware, &periodsize, 0) < 0) { g_warning("Unable to set period"); return FALSE; } if(snd_pcm_hw_params_set_buffer_size_near(wave_handle, hardware, &buffer_frames) < 0) { g_warning("Unable to set buffer size"); return FALSE; } if(snd_pcm_hw_params(wave_handle, hardware) < 0) return FALSE; /* extra check: if we have only one period, this code won't work */ snd_pcm_hw_params_get_period_size (hardware, &periodsize, 0) ; snd_pcm_hw_params_get_buffer_size (hardware, &buffer_size) ; if (periodsize == buffer_size) return FALSE; snd_pcm_hw_params_free (hardware) ; if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0) return FALSE; if ((err = snd_pcm_sw_params_current (wave_handle, sw_params)) != 0) return FALSE; /* note: set start threshold to delay start until the ring buffer is full */ snd_pcm_sw_params_current (wave_handle, sw_params) ; if ((err = snd_pcm_sw_params_get_xfer_align (sw_params, &xfer_align)) < 0) return FALSE; /* round up to closest transfer boundary */ start_threshold = (buffer_size / xfer_align) * xfer_align ; if (start_threshold < 1) start_threshold = 1 ; if ((err = snd_pcm_sw_params_set_start_threshold (wave_handle, sw_params, start_threshold)) < 0) return FALSE; if ((err = snd_pcm_sw_params (wave_handle, sw_params)) != 0) return FALSE; snd_pcm_sw_params_free (sw_params) ; snd_pcm_reset (wave_handle) ; return TRUE; } void soundserver_fill(struct sound_play_data *info) { static short buffer [BUFFER_LEN]; char *data = (char *) buffer; int readcount; int frames; int channels; while ((readcount = m_pUserCallback(buffer,BUFFER_LEN * 2, info))) { int total = 0; int retval; frames = BUFFER_LEN / info->sfinfo->channels; channels = info->sfinfo->channels; g_mutex_lock(quit_mutex); if(soundserver_force_quit) { g_mutex_unlock(quit_mutex); soundserver_close(); break; } g_mutex_unlock(quit_mutex); while (total < frames) { retval = snd_pcm_writei (wave_handle, data + total * channels, frames - total) ; if (retval >= 0) { total += retval; if (total == frames) break; continue; } if(retval == -EPIPE) { snd_pcm_prepare (wave_handle); } else { g_warning("error writing sound data: %d\n", retval) ; return; } } } snd_pcm_close (wave_handle); soundserver_close(); } void soundserver_close(void) { g_mutex_lock(running_mutex); m_bServerRunning = FALSE; g_mutex_unlock(running_mutex); gui_main_audio_play_mode(SOUND_NOT_PLAYING); sound_signal_pos_change(-1); } void soundserver_stop() { int slept_count = 0; g_mutex_lock(quit_mutex); if(!soundserver_force_quit) { soundserver_force_quit = TRUE; g_mutex_unlock(quit_mutex); while(1) { slept_count++; if(slept_count == 10) { g_warning("slept_count == 10, this is not good"); g_mutex_unlock(quit_mutex); m_bServerRunning = FALSE; break; } kry_sleep(100); g_mutex_lock(running_mutex); if(!m_bServerRunning) break; g_mutex_unlock(running_mutex); } // when the loop breaks, the mutex remains locked g_mutex_unlock(running_mutex); } else { g_mutex_unlock(quit_mutex); } soundserver_force_quit = FALSE; return; } void soundserver_init() { quit_mutex = g_mutex_new(); running_mutex = g_mutex_new(); } void soundserver_mute(int channel) { m_soundserver_mute[channel] = 1; } void soundserver_play(struct sound_play_data *data) { g_thread_create((GThreadFunc) soundserver_fill, data, FALSE, NULL); } enum sound_position_type soundserver_get_position_type() { return POSITION_AUTOMATIC; } enum soundserver_position_rv soundserver_get_position(int *pos) { return SOUNDSERVER_ERROR; }