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 * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. *(at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
@ -25,166 +25,227 @@
#include "client.h" #include "client.h"
#include "snd_loc.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_uframes_t period_size;
static snd_pcm_hw_params_t * hw_params; 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; static struct sndinfo * si;
qboolean SNDDMA_Init(struct sndinfo * s) { /*
int i; * The sample rates which will be attempted.
int err; */
static int RATES[] = {
44100, 22050, 11025, 8000
};
if (snd_inited) /*
return 1; * Initialize ALSA pcm device, and bind it to sndinfo.
*/
snd_inited = 0; qboolean SNDDMA_Init(struct sndinfo *s){
int i, r, err, dir;
si = s;
si = s;
si->dma->samples = 1024;
if(!strcmp(si->device->string, "/dev/dsp")) //silly oss default
if ((err = snd_pcm_open(&pcm_handle, si->device->string, si->device->string = "default";
SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
si->Com_Printf("ALSA snd error, cannot open device %s (%s)\n", if((err = snd_pcm_open(&pcm_handle, si->device->string,
si->device->string, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0){
snd_strerror(err)); si->Com_Printf("ALSA: cannot open device %s(%s)\n",
return 0; si->device->string, snd_strerror(err));
} return false;
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;
} }
}
if (si->dma->samplebits == 8) { if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0){
if ((err = snd_pcm_hw_params_set_format(pcm_handle, hw_params, si->Com_Printf("ALSA: cannot allocate hw params(%s)\n",
SND_PCM_FORMAT_U8)) < 0) { snd_strerror(err));
si->Com_Printf("ALSA snd error, cannot set sample format (%s)\n", return false;
snd_strerror(err));
snd_pcm_hw_params_free(hw_params);
return 0;
} }
}
if((err = snd_pcm_hw_params_any(pcm_handle, hw_params)) < 0){
si->dma->speed = (int)si->speed->value; si->Com_Printf("ALSA: cannot init hw params(%s)\n", snd_strerror(err));
if (!si->dma->speed) { snd_pcm_hw_params_free(hw_params);
for (i = 0; i < sizeof(tryrates); i++) { return false;
int dir = 0; }
int test = tryrates[i];
if((err = snd_pcm_hw_params_set_access(pcm_handle, hw_params,
if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
&test, &dir)) < 0) { si->Com_Printf("ALSA: cannot set access(%s)\n", snd_strerror(err));
si->Com_Printf("ALSA snd error, cannot set sample rate %d (%s)\n", snd_pcm_hw_params_free(hw_params);
tryrates[i], snd_strerror(err)); return false;
} else { }
si->dma->speed = test;
if (dir != 0) { si->dma->samplebits = si->bits->value;
si->Com_Printf("alsa: The rate %d Hz is not supported by your hardware, using %d Hz instead.\n", test, err); 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->samplebits == 8){ //or 8 if specifically asked to
if (!si->dma->speed) { if((err = snd_pcm_hw_params_set_format(pcm_handle, hw_params,
si->Com_Printf("ALSA snd error couldn't set rate.\n"); SND_PCM_FORMAT_U8)) < 0){
snd_pcm_hw_params_free(hw_params); si->Com_Printf("ALSA: cannot set format(%s)\n", snd_strerror(err));
return 0; 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((err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size)) < 0){
if (si->dma->channels < 1 || si->dma->channels > 2) si->Com_Printf("ALSA snd error, cannot get buffer size(%s)\n", snd_strerror(err));
si->dma->channels = 2; snd_pcm_hw_params_free(hw_params);
if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hw_params, si->dma->channels)) < 0) { return false;
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); if((err = snd_pcm_hw_params_get_periods(hw_params, &periods, 0)) < 0){
return 0; si->Com_Printf("ALSA: cannot get periods(%s)\n", snd_strerror(err));
} snd_pcm_hw_params_free(hw_params);
return false;
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); period_bytes = period_size * si->dma->channels * si->dma->samplebits / 8;
return 0; buffer_bytes = buffer_size * si->dma->channels * si->dma->samplebits / 8;
}
si->dma->buffer = malloc(buffer_bytes); //allocate pcm frame buffer
si->dma->buffer = malloc(BUFFER_SIZE); memset(si->dma->buffer, 0, buffer_bytes);
memset(si->dma->buffer, 0, BUFFER_SIZE);
si->dma->samplepos = 0;
si->dma->samplepos = 0;
si->dma->samples = BUFFER_SIZE / (si->dma->samplebits / 8); si->dma->samples = buffer_size * si->dma->channels;
si->dma->submission_chunk = 1; si->dma->submission_chunk = period_size * si->dma->channels;
si->Com_Printf("alsa: buffer size is %d, %d samples\n", BUFFER_SIZE, si->dma->samples); si->Com_Printf("period size is %d (%d bytes)\n"
"buffer size is %d (%d bytes)\n%d periods\n", (int)period_size,
snd_inited = 1; period_bytes, (int)buffer_size, buffer_bytes, periods
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) {
snd_pcm_prepare(pcm_handle); snd_pcm_prepare(pcm_handle);
si->Com_Printf("alsa: buffer underrun\n");
} return true;
si->dma->samplepos += written / (si->dma->samplebits / 8);
} }
/*
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){}