/* 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 <alsa/asoundlib.h>
#include <glib.h>
#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;
}