X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=snd_alsa.c;h=3f4292c84e7f73edf2a2f49a7f36707c0980a374;hp=23d66bd9285d59aa0bd2dc49d9bd9094bd0b1ad3;hb=ca4d37309cf7721bd7fd42d2a95ac5db7d057d01;hpb=2fb28c32491ce770550ebd59e0e9f222b4378b9a diff --git a/snd_alsa.c b/snd_alsa.c index 23d66bd9..3f4292c8 100644 --- a/snd_alsa.c +++ b/snd_alsa.c @@ -23,19 +23,20 @@ // ALSA module, used by Linux +#include "quakedef.h" #include -#include "quakedef.h" #include "snd_main.h" -#define NB_PERIODS 2 +#define NB_PERIODS 4 static snd_pcm_t* pcm_handle = NULL; static snd_pcm_sframes_t expected_delay = 0; static unsigned int alsasoundtime; +static snd_seq_t* seq_handle = NULL; /* ==================== @@ -47,14 +48,65 @@ May return a suggested format if the requested format isn't available */ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) { - const char* pcm_name; - int i, err; + const char* pcm_name, *seq_name; + int i, err, seq_client, seq_port; snd_pcm_hw_params_t* hw_params = NULL; snd_pcm_format_t snd_pcm_format; snd_pcm_uframes_t buffer_size; Con_Print ("SndSys_Init: using the ALSA module\n"); + seq_name = NULL; +// COMMANDLINEOPTION: Linux ALSA Sound: -sndseqin : selects which sequencer port to use for input, by default no sequencer port is used (MIDI note events from that port get mapped to MIDINOTE keys that can be bound) + i = COM_CheckParm ("-sndseqin"); // TODO turn this into a cvar, maybe + if (i != 0 && i < com_argc - 1) + seq_name = com_argv[i + 1]; + if(seq_name) + { + seq_client = atoi(seq_name); + seq_port = 0; + if(strchr(seq_name, ':')) + seq_port = atoi(strchr(seq_name, ':') + 1); + Con_Printf ("SndSys_Init: seq input port has been set to \"%d:%d\". Enabling sequencer input...\n", seq_client, seq_port); + err = snd_seq_open (&seq_handle, "default", SND_SEQ_OPEN_INPUT, 0); + if (err < 0) + { + Con_Print ("SndSys_Init: can't open seq device\n"); + goto seqdone; + } + err = snd_seq_set_client_name(seq_handle, gamename); + if (err < 0) + { + Con_Print ("SndSys_Init: can't set name of seq device\n"); + goto seqerror; + } + err = snd_seq_create_simple_port(seq_handle, gamename, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); + if(err < 0) + { + Con_Print ("SndSys_Init: can't create seq port\n"); + goto seqerror; + } + err = snd_seq_connect_from(seq_handle, 0, seq_client, seq_port); + if(err < 0) + { + Con_Printf ("SndSys_Init: can't connect to seq port \"%d:%d\"\n", seq_client, seq_port); + goto seqerror; + } + err = snd_seq_nonblock(seq_handle, 1); + if(err < 0) + { + Con_Print ("SndSys_Init: can't make seq nonblocking\n"); + goto seqerror; + } + + goto seqdone; + +seqerror: + snd_seq_close(seq_handle); + seq_handle = NULL; + } + +seqdone: // Check the requested sound format if (requested->width < 1 || requested->width > 2) { @@ -63,7 +115,7 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) if (suggested != NULL) { - memcpy (suggested, requested, sizeof (suggested)); + memcpy (suggested, requested, sizeof (*suggested)); if (requested->width < 1) suggested->width = 1; @@ -73,16 +125,16 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) Con_Printf ("SndSys_Init: suggesting sound width = %hu\n", suggested->width); } - + return false; } - + if (pcm_handle != NULL) { Con_Print ("SndSys_Init: WARNING: Init called before Shutdown!\n"); SndSys_Shutdown (); } - + // Determine the name of the PCM handle we'll use switch (requested->channels) { @@ -99,31 +151,31 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) pcm_name = "default"; break; } -// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm selects which pcm device to us, default is "default" +// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm selects which pcm device to use, default is "default" i = COM_CheckParm ("-sndpcm"); if (i != 0 && i < com_argc - 1) pcm_name = com_argv[i + 1]; // Open the audio device - Con_DPrintf ("SndSys_Init: PCM device is \"%s\"\n", pcm_name); + Con_Printf ("SndSys_Init: PCM device is \"%s\"\n", pcm_name); err = snd_pcm_open (&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't open audio device \"%s\" (%s)\n", pcm_name, snd_strerror (err)); return false; } - + // Allocate the hardware parameters err = snd_pcm_hw_params_malloc (&hw_params); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't allocate hardware parameters (%s)\n", snd_strerror (err)); goto init_error; } err = snd_pcm_hw_params_any (pcm_handle, hw_params); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't initialize hardware parameters (%s)\n", snd_strerror (err)); @@ -132,7 +184,7 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) // Set the access type err = snd_pcm_hw_params_set_access (pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't set access type (%s)\n", snd_strerror (err)); @@ -145,7 +197,7 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) else snd_pcm_format = SND_PCM_FORMAT_S16; err = snd_pcm_hw_params_set_format (pcm_handle, hw_params, snd_pcm_format); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't set sound width to %hu (%s)\n", requested->width, snd_strerror (err)); @@ -154,7 +206,7 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) // Set the sound channels err = snd_pcm_hw_params_set_channels (pcm_handle, hw_params, requested->channels); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't set sound channels to %hu (%s)\n", requested->channels, snd_strerror (err)); @@ -163,25 +215,34 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) // Set the sound speed err = snd_pcm_hw_params_set_rate (pcm_handle, hw_params, requested->speed, 0); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't set sound speed to %u (%s)\n", requested->speed, snd_strerror (err)); goto init_error; } - buffer_size = requested->speed / 5; + // pick a buffer size that is a power of 2 (by masking off low bits) + buffer_size = i = (int)(requested->speed * 0.15f); + while (buffer_size & (buffer_size-1)) + buffer_size &= (buffer_size-1); + // then check if it is the nearest power of 2 and bump it up if not + if (i - buffer_size >= buffer_size >> 1) + buffer_size *= 2; + err = snd_pcm_hw_params_set_buffer_size_near (pcm_handle, hw_params, &buffer_size); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't set sound buffer size to %lu (%s)\n", buffer_size, snd_strerror (err)); goto init_error; } + // pick a period size near the buffer_size we got from ALSA + snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size); buffer_size /= NB_PERIODS; err = snd_pcm_hw_params_set_period_size_near(pcm_handle, hw_params, &buffer_size, 0); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't set sound period size to %lu (%s)\n", buffer_size, snd_strerror (err)); @@ -189,7 +250,7 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) } err = snd_pcm_hw_params (pcm_handle, hw_params); - if (err != 0) + if (err < 0) { Con_Printf ("SndSys_Init: can't set hardware parameters (%s)\n", snd_strerror (err)); @@ -203,16 +264,19 @@ qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested) alsasoundtime = 0; if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO) Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_ALSA); - + return true; // It's not very clean, but it avoids a lot of duplicated code. init_error: - + if (hw_params != NULL) snd_pcm_hw_params_free (hw_params); - SndSys_Shutdown (); + + snd_pcm_close(pcm_handle); + pcm_handle = NULL; + return false; } @@ -226,6 +290,12 @@ Stop the sound card, delete "snd_renderbuffer" and free its other resources */ void SndSys_Shutdown (void) { + if (seq_handle != NULL) + { + snd_seq_close(seq_handle); + seq_handle = NULL; + } + if (pcm_handle != NULL) { snd_pcm_close(pcm_handle); @@ -255,11 +325,11 @@ static qboolean SndSys_Recover (int err_num) // We can only do something on underrun ("broken pipe") errors if (err_num != -EPIPE) return false; - + err = snd_pcm_prepare (pcm_handle); - if (err != 0) + if (err < 0) { - Con_DPrintf ("SndSys_Recover: unable to recover (%s)\n", + Con_Printf ("SndSys_Recover: unable to recover (%s)\n", snd_strerror (err)); // TOCHECK: should we stop the playback ? @@ -283,8 +353,8 @@ static snd_pcm_sframes_t SndSys_Write (const unsigned char* buffer, unsigned int written = snd_pcm_writei (pcm_handle, buffer, nbframes); if (written < 0) { - if (developer.integer >= 100) - Con_Printf ("SndSys_Write: audio write returned %ld (%s)!\n", + if (developer_insane.integer && vid_activewindow) + Con_DPrintf ("SndSys_Write: audio write returned %ld (%s)!\n", written, snd_strerror (written)); if (SndSys_Recover (written)) @@ -295,7 +365,12 @@ static snd_pcm_sframes_t SndSys_Write (const unsigned char* buffer, unsigned int written, snd_strerror (written)); } } - + if (written > 0) + { + snd_renderbuffer->startframe += written; + expected_delay += written; + } + return written; } @@ -312,7 +387,7 @@ void SndSys_Submit (void) unsigned int startoffset, factor; snd_pcm_uframes_t limit, nbframes; snd_pcm_sframes_t written; - + if (pcm_handle == NULL || snd_renderbuffer->startframe == snd_renderbuffer->endframe) return; @@ -325,14 +400,9 @@ void SndSys_Submit (void) if (nbframes > limit) { written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], limit); - if (written < 0) + if (written < 0 || (snd_pcm_uframes_t)written != limit) return; - snd_renderbuffer->startframe += written; - expected_delay += written; - if ((snd_pcm_uframes_t)written != limit) - return; - nbframes -= limit; startoffset = 0; } @@ -340,9 +410,6 @@ void SndSys_Submit (void) written = SndSys_Write (&snd_renderbuffer->ring[startoffset * factor], nbframes); if (written < 0) return; - - snd_renderbuffer->startframe += written; - expected_delay += written; } @@ -362,9 +429,9 @@ unsigned int SndSys_GetSoundTime (void) return 0; err = snd_pcm_delay (pcm_handle, &delay); - if (err != 0) + if (err < 0) { - if (developer.integer >= 100) + if (developer_insane.integer && vid_activewindow) Con_DPrintf ("SndSys_GetSoundTime: can't get playback delay (%s)\n", snd_strerror (err)); @@ -372,7 +439,7 @@ unsigned int SndSys_GetSoundTime (void) return 0; err = snd_pcm_delay (pcm_handle, &delay); - if (err != 0) + if (err < 0) { Con_DPrintf ("SndSys_GetSoundTime: can't get playback delay, again (%s)\n", snd_strerror (err)); @@ -389,9 +456,9 @@ unsigned int SndSys_GetSoundTime (void) else timediff = expected_delay - delay; expected_delay = delay; - + alsasoundtime += (unsigned int)timediff; - + return alsasoundtime; } @@ -421,3 +488,37 @@ void SndSys_UnlockRenderBuffer (void) { // Nothing to do } + +/* +==================== +SndSys_SendKeyEvents + +Send keyboard events originating from the sound system (e.g. MIDI) +==================== +*/ +void SndSys_SendKeyEvents(void) +{ + snd_seq_event_t *event; + if(!seq_handle) + return; + for(;;) + { + if(snd_seq_event_input(seq_handle, &event) <= 0) + break; + if(event) + { + switch(event->type) + { + case SND_SEQ_EVENT_NOTEON: + if(event->data.note.velocity) + { + Key_Event(K_MIDINOTE0 + event->data.note.note, 0, true); + break; + } + case SND_SEQ_EVENT_NOTEOFF: + Key_Event(K_MIDINOTE0 + event->data.note.note, 0, false); + break; + } + } + } +}