A lot of work to snd_alsa.c. It's not correct yet, but it's a lot closer.

Perhaps someone else will poke at it now.
This commit is contained in:
Jay Dolan 2006-01-07 19:02:50 +00:00
parent 712be75e22
commit 740dc42df5

View file

@ -1,18 +1,18 @@
/*
* 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 of the License, 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
* 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 of the License, 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
@ -25,166 +25,227 @@
#include "client.h"
#include "snd_loc.h"
static int snd_inited;
static snd_pcm_t *pcm_handle;
static snd_pcm_hw_params_t *hw_params;
static snd_pcm_t * pcm_handle;
static snd_pcm_hw_params_t * hw_params;
static snd_pcm_uframes_t period_size;
static snd_pcm_uframes_t buffer_size;
#define BUFFER_SIZE 4096
static int periods;
int tryrates[] = { 44100, 22051, 11025, 8000 };
static int period_bytes;
static int buffer_bytes;
/* sound info */
static struct sndinfo * si;
qboolean SNDDMA_Init(struct sndinfo * s) {
int i;
int err;
/*
* The sample rates which will be attempted.
*/
static int RATES[] = {
44100, 22050, 11025, 8000
};
if (snd_inited)
return 1;
snd_inited = 0;
si = s;
si->dma->samples = 1024;
if ((err = snd_pcm_open(&pcm_handle, si->device->string,
SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
si->Com_Printf("ALSA snd error, cannot open device %s (%s)\n",
si->device->string,
snd_strerror(err));
return 0;
}
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
si->Com_Printf("ALSA snd error, cannot allocate hw params (%s)\n",
snd_strerror(err));
return 0;
}
if ((err = snd_pcm_hw_params_any(pcm_handle, hw_params)) < 0) {
si->Com_Printf("ALSA snd error, cannot init hw params (%s)\n",
snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return 0;
}
if ((err = snd_pcm_hw_params_set_access(pcm_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
si->Com_Printf("ALSA snd error, cannot set access (%s)\n",
snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return 0;
}
si->dma->samplebits = si->bits->value;
if (si->dma->samplebits == 16 || si->dma->samplebits != 8) {
if ((err = snd_pcm_hw_params_set_format(pcm_handle, hw_params,
SND_PCM_FORMAT_S16_LE)) < 0) {
si->Com_Printf("ALSA snd error, 16 bit sound not supported, trying 8\n");
si->dma->samplebits = 8;
/*
* Initialize ALSA pcm device, and bind it to sndinfo.
*/
qboolean SNDDMA_Init(struct sndinfo *s){
int i, r, err, dir;
si = s;
if(!strcmp(si->device->string, "/dev/dsp")) //silly oss default
si->device->string = "default";
if((err = snd_pcm_open(&pcm_handle, si->device->string,
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0){
si->Com_Printf("ALSA: cannot open device %s(%s)\n",
si->device->string, snd_strerror(err));
return false;
}
}
if (si->dma->samplebits == 8) {
if ((err = snd_pcm_hw_params_set_format(pcm_handle, hw_params,
SND_PCM_FORMAT_U8)) < 0) {
si->Com_Printf("ALSA snd error, cannot set sample format (%s)\n",
snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return 0;
if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0){
si->Com_Printf("ALSA: cannot allocate hw params(%s)\n",
snd_strerror(err));
return false;
}
}
si->dma->speed = (int)si->speed->value;
if (!si->dma->speed) {
for (i = 0; i < sizeof(tryrates); i++) {
int dir = 0;
int test = tryrates[i];
if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params,
&test, &dir)) < 0) {
si->Com_Printf("ALSA snd error, cannot set sample rate %d (%s)\n",
tryrates[i], snd_strerror(err));
} else {
si->dma->speed = test;
if (dir != 0) {
si->Com_Printf("alsa: The rate %d Hz is not supported by your hardware, using %d Hz instead.\n", test, err);
if((err = snd_pcm_hw_params_any(pcm_handle, hw_params)) < 0){
si->Com_Printf("ALSA: cannot init hw params(%s)\n", snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
if((err = snd_pcm_hw_params_set_access(pcm_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
si->Com_Printf("ALSA: cannot set access(%s)\n", snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
si->dma->samplebits = si->bits->value;
if(si->dma->samplebits != 8){ //try 16 by default
si->dma->samplebits = 16; //ensure this is set for other calculations
if((err = snd_pcm_hw_params_set_format(pcm_handle, hw_params,
SND_PCM_FORMAT_S16)) < 0){
si->Com_Printf("ALSA: 16 bit not supported, trying 8\n");
si->dma->samplebits = 8;
}
break;
}
}
}
if (!si->dma->speed) {
si->Com_Printf("ALSA snd error couldn't set rate.\n");
snd_pcm_hw_params_free(hw_params);
return 0;
}
if(si->dma->samplebits == 8){ //or 8 if specifically asked to
if((err = snd_pcm_hw_params_set_format(pcm_handle, hw_params,
SND_PCM_FORMAT_U8)) < 0){
si->Com_Printf("ALSA: cannot set format(%s)\n", snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
}
si->dma->speed =(int)si->speed->value;
if(si->dma->speed){ //try specified rate
r = si->dma->speed;
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &r, &dir)) < 0)
si->Com_Printf("ALSA: cannot set rate %d(%s)\n", r, snd_strerror(err));
else { //rate succeeded, but is perhaps slightly different
if(dir != 0) si->Com_Printf("ALSA: rate %d not supported, using %d\n", si->dma->speed, r);
si->dma->speed = r;
}
}
if(!si->dma->speed){ //or all available ones
for(i = 0; i < sizeof(RATES); i++){
r = RATES[i];
dir = 0;
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &r, &dir)) < 0)
si->Com_Printf("ALSA: cannot set rate %d(%s)\n", r, snd_strerror(err));
else { //rate succeeded, but is perhaps slightly different
si->dma->speed = r;
if(dir != 0) si->Com_Printf("ALSA: rate %d not supported, using %d\n", RATES[i], r);
break;
}
}
}
if(!si->dma->speed){ //failed
si->Com_Printf("ALSA: cannot set rate\n");
snd_pcm_hw_params_free(hw_params);
return false;
}
si->dma->channels = (int)si->channels->value;
if(si->dma->channels < 1 || si->dma->channels > 2)
si->dma->channels = 2; //ensure either stereo or mono
if((err = snd_pcm_hw_params_set_channels(pcm_handle, hw_params,
si->dma->channels)) < 0){
si->Com_Printf("ALSA: cannot set channels %d(%s)\n",
si->dma->channels, snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
if((err = snd_pcm_hw_params(pcm_handle, hw_params)) < 0){ //set params
si->Com_Printf("ALSA: cannot set params(%s)\n", snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
if((err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, 0)) < 0){
si->Com_Printf("ALSA: cannot get period size(%s)\n", snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
si->dma->channels = (int)si->channels->value;
if (si->dma->channels < 1 || si->dma->channels > 2)
si->dma->channels = 2;
if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hw_params, si->dma->channels)) < 0) {
si->Com_Printf("ALSA snd error couldn't set channels %d (%s).\n",
si->dma->channels, snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return 0;
}
if ((err = snd_pcm_hw_params(pcm_handle, hw_params)) < 0) {
si->Com_Printf("ALSA snd error couldn't set params (%s).\n",snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return 0;
}
si->dma->buffer = malloc(BUFFER_SIZE);
memset(si->dma->buffer, 0, BUFFER_SIZE);
si->dma->samplepos = 0;
si->dma->samples = BUFFER_SIZE / (si->dma->samplebits / 8);
si->dma->submission_chunk = 1;
si->Com_Printf("alsa: buffer size is %d, %d samples\n", BUFFER_SIZE, si->dma->samples);
snd_inited = 1;
return 1;
}
int SNDDMA_GetDMAPos(void) {
if (snd_inited)
return si->dma->samplepos;
else
si->Com_Printf("Sound not inizialized\n");
return 0;
}
void SNDDMA_Shutdown(void) {
if (snd_inited) {
snd_pcm_drop(pcm_handle);
snd_pcm_close(pcm_handle);
snd_inited = 0;
}
free(si->dma->buffer);
si->dma->buffer = NULL;
}
/* SNDDMA_Submit
* Send sound to device if buffer isn't really the dma buffer
*/
void SNDDMA_Submit(void) {
int written;
if(!snd_inited)
return;
if ((written = snd_pcm_writei(pcm_handle, si->dma->buffer, si->dma->samples * (si->dma->samplebits / 8))) < 0) {
if((err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size)) < 0){
si->Com_Printf("ALSA snd error, cannot get buffer size(%s)\n", snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
if((err = snd_pcm_hw_params_get_periods(hw_params, &periods, 0)) < 0){
si->Com_Printf("ALSA: cannot get periods(%s)\n", snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return false;
}
period_bytes = period_size * si->dma->channels * si->dma->samplebits / 8;
buffer_bytes = buffer_size * si->dma->channels * si->dma->samplebits / 8;
si->dma->buffer = malloc(buffer_bytes); //allocate pcm frame buffer
memset(si->dma->buffer, 0, buffer_bytes);
si->dma->samplepos = 0;
si->dma->samples = buffer_size * si->dma->channels;
si->dma->submission_chunk = period_size * si->dma->channels;
si->Com_Printf("period size is %d (%d bytes)\n"
"buffer size is %d (%d bytes)\n%d periods\n", (int)period_size,
period_bytes, (int)buffer_size, buffer_bytes, periods
);
snd_pcm_prepare(pcm_handle);
si->Com_Printf("alsa: buffer underrun\n");
}
si->dma->samplepos += written / (si->dma->samplebits / 8);
return true;
}
void SNDDMA_BeginPainting(void) {
/*
* Returns the current sample position, if sound is running.
*/
int SNDDMA_GetDMAPos(void){
if(si->dma->buffer)
return si->dma->samplepos;
si->Com_Printf("Sound not inizialized\n");
return 0;
}
/*
* Closes the ALSA pcm device and frees the dma buffer.
*/
void SNDDMA_Shutdown(void){
if(si->dma->buffer){
snd_pcm_drop(pcm_handle);
snd_pcm_close(pcm_handle);
}
free(si->dma->buffer);
si->dma->buffer = NULL;
}
/*
* Writes the dma buffer to the ALSA pcm device.
*/
void SNDDMA_Submit(void){
int w;
void *start;
if(!si->dma->buffer)
return;
start = (void *)&si->dma->buffer[si->dma->samplepos];
if((w = snd_pcm_writei(pcm_handle, start, period_size)) < 0){ //xrun
//si->Com_Printf("ALSA: buffer underrun(%s)\n", snd_strerror(w));
snd_pcm_prepare(pcm_handle);
}
else { //mark progress
//si->Com_Printf("wrote %d frames\n", w);
si->dma->samplepos += w * si->dma->channels;
if(si->dma->samplepos >= si->dma->samples)
si->dma->samplepos = 0; //wrap
}
}
/*
* No clue :)
*/
void SNDDMA_BeginPainting(void){}