/* ALSArtaudio.c - hacked version of rtaudio.c to enable realtime full-duplex audio support for csound under Linux/ALSA. This file incorporates the functions from LINUXaudio.c (with the exception of sndsetvolume() which isn't used in this hacked version) so that file is no longer needed - fcf */ /* * $Id: ALSArtaudio.c,v 1.6 1999/06/23 18:23:57 nicb Exp $ */ /* This module is included when RTAUDIO is defined at compile time. It provides an interface between Csound realtime record/play calls and the device-driver code that controls the actual hardware. */ /* This file contains an updated version of the csound to ALSA interface. It is tested against ALSA 0.5.8. Code in this file (ALSArtaudio-stream.c) utilizes the ALSA stream transfer interface, which induces much less latency compared to ALSA block mode transfer, (look at ALSArtaudio-block.c). - Markus Gruber */ #include #include "cs.h" #include "soundio.h" #include static snd_pcm_t *capture_handle = NULL, *playback_handle = NULL; extern long nrecs; static int processed = 0; extern OPARMS O; #ifdef PIPES extern FILE* Linepipe; #define _pclose pclose #endif char errorstring[1024]; static int getformat() { int p = 0; switch ( O.informat ) { case AE_UNCH: /* unsigned char - standard Linux 8-bit format */ p = SND_PCM_SFMT_U8; break; case AE_CHAR: /* signed char. supported by ALSA */ p = SND_PCM_SFMT_S8; break; case AE_ULAW: p = SND_PCM_SFMT_MU_LAW; break; case AE_ALAW: p = SND_PCM_SFMT_A_LAW; break; case AE_SHORT: p = SND_PCM_SFMT_S16_LE; /* Linux on Intel x86 is little-endian */ break; case AE_LONG: die("ALSA sound driver does not (yet) support long integer samples"); case AE_FLOAT: die("ALSA sound driver does not (yet) support floating-point samples"); default: die("unknown sample format"); } return (p); } void recopen(int nchnls, int dsize, FLOAT esr, int scale) /* open for audio input */ { extern int incard; static snd_pcm_channel_params_t params; int err; char *cardname; if ((err = snd_card_get_longname(incard, &cardname))) { sprintf(errorstring, "%s\n", snd_strerror(err)); die(errorstring); } fprintf(stderr, "ALSA incard: %s\n", cardname); /* Open ALSA audio driver, card #incard, device #0 in CAPTURE mode */ if((err = snd_pcm_open(&capture_handle, incard, 0, SND_PCM_OPEN_CAPTURE))){ sprintf(errorstring, "Opening CAPTURE channel: %s\n", snd_strerror(err)); die(errorstring); } //snd_pcm_channel_flush(capture_handle, SND_PCM_CHANNEL_CAPTURE); /* be in right state */ memset(¶ms, 0, sizeof(snd_pcm_channel_params_t)); params.channel = SND_PCM_CHANNEL_CAPTURE; params.mode = SND_PCM_MODE_STREAM; params.format.interleave = 1; params.format.format = getformat(); params.format.rate = (int)esr; params.format.voices = nchnls; /* digital */ params.start_mode = SND_PCM_START_GO; params.stop_mode = SND_PCM_STOP_ROLLOVER; params.buf.stream.queue_size = 1024*1024;/* maximum for smooth action shorter queue would cause less latency, but is more choppy */ params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE; params.buf.stream.max_fill = 1024; if ((err = snd_pcm_plugin_params(capture_handle, ¶ms))) { sprintf(errorstring, "CAPTURE channel parameterizing error: %s\n", snd_strerror(err)); die(errorstring); } if ((err = snd_pcm_plugin_prepare(capture_handle, SND_PCM_CHANNEL_CAPTURE))) { sprintf(errorstring, "CAPTURE channel prepare error: %s\n", snd_strerror(err)); die(errorstring); } if ((err = snd_pcm_channel_go(capture_handle, SND_PCM_CHANNEL_CAPTURE))) { sprintf(errorstring, "CAPTURE channel go error: %s\n", snd_strerror(err)); die(errorstring); } fprintf(stderr, "Running CAPTURE mode at %d hz on card #%d\n", params.format.rate, incard); } void playopen(int nchnls, int dsize, FLOAT esr, int scale) /* open for audio output */ { extern int outcard; static snd_pcm_channel_params_t params; int err; char *cardname; if ((err = snd_card_get_longname(outcard, &cardname))) { sprintf(errorstring, "%s\n", snd_strerror(err)); die(errorstring); } fprintf(stderr, "ALSA outcard: %s\n", cardname); /* Open ALSA audio driver, card # outcard, device #0 in PLAYBACK mode */ if((err = snd_pcm_open(&playback_handle, outcard, 0, SND_PCM_OPEN_PLAYBACK))){ sprintf(errorstring, "Opening PLAYBACK channel: %s\n", snd_strerror(err)); die(errorstring); } //snd_pcm_channel_flush(playback_handle, SND_PCM_CHANNEL_PLAYBACK); /* be in right state */ memset(¶ms, 0, sizeof(snd_pcm_channel_params_t)); params.channel = SND_PCM_CHANNEL_PLAYBACK; params.mode = SND_PCM_MODE_STREAM; params.format.interleave = 1; params.format.format = getformat(); params.format.rate = (int)esr; params.format.voices = nchnls; /* digital */ params.start_mode = SND_PCM_START_GO; params.stop_mode = SND_PCM_STOP_ROLLOVER; params.buf.stream.queue_size = 1024*1024; /* see recopen */ params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE; params.buf.stream.max_fill = 1024; if ((err = snd_pcm_plugin_params(playback_handle, ¶ms))) { sprintf(errorstring, "PLAYBACK channel parameterizing error: %s\n", snd_strerror(err)); die(errorstring); } if ((err = snd_pcm_plugin_prepare(playback_handle, SND_PCM_CHANNEL_PLAYBACK))) { sprintf(errorstring, "PLAYBACK channel prepare error: %s\n", snd_strerror(err)); die(errorstring); } if ((err = snd_pcm_channel_go(playback_handle, SND_PCM_CHANNEL_PLAYBACK))) { sprintf(errorstring, "PLAYBACK channel go error: %s\n", snd_strerror(err)); die(errorstring); } fprintf(stderr, "Running PLAYBACK mode at %d hz on card #%d\n", params.format.rate, outcard); #ifdef HIPRI setscheduler(); #endif } int rtrecord(char *inbuf, int nbytes) { int count; while (1) { if ((count = snd_pcm_plugin_read(capture_handle, (void *)inbuf, (size_t)nbytes)) < 0) { if (count == -EAGAIN) { usleep(10000); continue; } sprintf(errorstring, "Capture error: %s", snd_strerror(count)); die(errorstring); } break; } return(count); } void rtplay(char *outbuf, int nbytes) { int count, pos = 0; do { if ((count = snd_pcm_plugin_write(playback_handle, (void *)(&outbuf[pos]), (size_t)nbytes)) < 0) { if (count == -EAGAIN) { usleep(10000); continue; } sprintf(errorstring, "Playback error: %s\n", snd_strerror(count)); die(errorstring); } nbytes -= count; pos += count; //fprintf(stderr, "bytes = %d, count written = %d\n", nbytes, count); } while (nbytes); nrecs++; } void rtclose(void) /* close the I/O device entirely */ { /* called only when both complete */ int err; if(capture_handle) { snd_pcm_channel_flush(capture_handle, SND_PCM_CHANNEL_CAPTURE); if ((err = snd_pcm_close(capture_handle))) { sprintf(errorstring, "CAPTURE channel close error: %s\n", snd_strerror(err)); die(errorstring); } } if(playback_handle) { if ((err = snd_pcm_plugin_playback_drain(playback_handle))) { sprintf(errorstring, "PLAYBACK channel drain error: %s\n", snd_strerror(err)); die(errorstring); } snd_pcm_channel_flush(capture_handle, SND_PCM_CHANNEL_CAPTURE); if ((err = snd_pcm_close(playback_handle))) { sprintf(errorstring, "PLAYBACK channel close error: %s\n", snd_strerror(err)); die(errorstring); } } if (O.Linein) { #ifdef PIPES if (O.Linename[0]=='|') _pclose(Linepipe); #endif } }