/* mic_alsa.cpp - this file is part of DeSmuME * * Copyright (C) 2009 DeSmuME Team * * This file 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 file 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 this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "types.h" #include "mic.h" #include "debug.h" #include "readwrite.h" #include "emufile.h" #include "utils/ringbuffer.h" #define MIC_BUFSIZE 4096 BOOL Mic_Inited = FALSE; jack_ringbuffer_t * Mic_Buffer; int MicButtonPressed; static GThread *mic_reader; // Handle for the PCM device static snd_pcm_t *pcm_handle; static snd_pcm_uframes_t mic_bufsize; static gpointer snd_pcm_read(gpointer data) { int error; snd_pcm_sframes_t frames; char buf[MIC_BUFSIZE]; while (Mic_Inited) { if ((error = snd_pcm_wait (pcm_handle, 1000)) < 0) { g_printerr("Failed to poll: %s\n", snd_strerror(error)); continue; } error = snd_pcm_avail(pcm_handle); if (error == 0) continue; if (error == -EPIPE) { g_printerr("xrun! %s\n", snd_strerror(error)); continue; //return GINT_TO_POINTER(FALSE); } if (error < 0) { g_printerr("alsa_pcm_avail_update error %s\n", snd_strerror(error)); continue; //return GINT_TO_POINTER(FALSE); } frames = error > mic_bufsize ? mic_bufsize : error; g_printerr ("frames: %ld\n", frames); memset(buf, 0, sizeof(buf)); error = snd_pcm_readi(pcm_handle, buf, frames); if (error < 0) error = snd_pcm_recover(pcm_handle, error, 0); if (error < 0) { LOG("snd_pcm_readi FAIL!: %s\n", snd_strerror(error)); continue; //return GINT_TO_POINTER(FALSE); } jack_ringbuffer_write(Mic_Buffer, buf, frames); } return GINT_TO_POINTER(TRUE); } BOOL Mic_Init() { snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t periods; int err; if (Mic_Inited) return TRUE; if ((err = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0) { g_printerr("Failed to open device: %s\n", snd_strerror(err)); return FALSE; } /* Hardware params */ snd_pcm_hw_params_alloca(&hwparams); if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { g_printerr("Failed to setup hw parameters: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { g_printerr("Failed to set access: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S8)) < 0) { g_printerr("Failed to set format: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 1)) < 0) { g_printerr("Failed to set channels: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, 16000, 0)) < 0) { g_printerr("Failed to set rate: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { g_printerr("Failed to set hw parameters: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_hw_params_get_buffer_size(hwparams, &mic_bufsize)) < 0) { g_printerr("Failed to get buffer size: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_hw_params_get_period_size(hwparams, &periods, 0)) < 0) { g_printerr("Failed to get period size: %s\n", snd_strerror(err)); return FALSE; } /* Software params */ snd_pcm_sw_params_alloca(&swparams); if ((err = snd_pcm_sw_params_current (pcm_handle, swparams)) < 0) { g_printerr("Failed to set current sw parameters: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_sw_params_set_avail_min (pcm_handle, swparams, mic_bufsize)) < 0) { g_printerr("Failed to set minimum available count: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_sw_params_set_start_threshold (pcm_handle, swparams, 0U)) < 0) { g_printerr("Failed to set start mode: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_sw_params (pcm_handle, swparams)) < 0) { g_printerr("Failed to set sw parameters: %s\n", snd_strerror(err)); return FALSE; } if ((err = snd_pcm_prepare (pcm_handle)) < 0) { g_printerr("Failed to prepare audio interface to use: %s\n", snd_strerror(err)); return FALSE; } Mic_Buffer = jack_ringbuffer_create(MIC_BUFSIZE); Mic_Inited = TRUE; mic_reader = g_thread_create(snd_pcm_read, NULL, TRUE, NULL); return TRUE; } void Mic_Reset() { if (!Mic_Inited) return; jack_ringbuffer_reset(Mic_Buffer); } void Mic_DeInit() { if (!Mic_Inited) return; Mic_Inited = FALSE; g_thread_join(mic_reader); jack_ringbuffer_free(Mic_Buffer); snd_pcm_drop(pcm_handle); snd_pcm_close(pcm_handle); } u8 Mic_ReadSample() { char tmp; u8 ret; if (!Mic_Inited) return 0; jack_ringbuffer_read(Mic_Buffer, &tmp, 1); if (Mic_Buffer->read_ptr & 0x1) { ret = ((tmp & 0x1) << 7); } else { ret = ((tmp & 0xFE) >> 1); } return ret; } /* FIXME: stub! */ void mic_savestate(EMUFILE* os) { write32le((u32)(-1),os); } bool mic_loadstate(EMUFILE* is, int size) { is->fseek(size, SEEK_CUR); return TRUE; }