X-Git-Url: https://scm.cri.ensmp.fr/git/Faustine.git/blobdiff_plain/1059e1cc0c2ecfa237406949aa26155b6a5b9154..66f23d4fabf89ad09adbd4dfc15ac6b5b2b7da83:/interpreter/preprocessor/faust-0.9.47mr3/architecture/audio/alsa-dsp.h diff --git a/interpreter/preprocessor/faust-0.9.47mr3/architecture/audio/alsa-dsp.h b/interpreter/preprocessor/faust-0.9.47mr3/architecture/audio/alsa-dsp.h new file mode 100644 index 0000000..e3a4f28 --- /dev/null +++ b/interpreter/preprocessor/faust-0.9.47mr3/architecture/audio/alsa-dsp.h @@ -0,0 +1,693 @@ +/************************************************************************ + + IMPORTANT NOTE : this file contains two clearly delimited sections : + the ARCHITECTURE section (in two parts) and the USER section. Each section + is governed by its own copyright and license. Please check individually + each section for license and copyright information. +*************************************************************************/ + +/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ + +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section 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 3 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, see . + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + + + ************************************************************************ + ************************************************************************/ + +#ifndef __alsa_dsp__ +#define __alsa_dsp__ + +#include +#include +#include +#include + +#include +#include "audio.h" +#include "dsp.h" + +/** +DEFAULT ALSA PARAMETERS CONTROLLED BY ENVIRONMENT VARIABLES + +Some default parameters of Faust's ALSA applications are controlled by the following environment variables : + + FAUST2ALSA_DEVICE = "hw:0" + FAUST2ALSA_FREQUENCY= 44100 + FAUST2ALSA_BUFFER = 1024 + FAUST2ALSA_PERIODS = 2 + +*/ + +using namespace std; + +// handle 32/64 bits int size issues + +#ifdef __x86_64__ + +#define uint32 unsigned int +#define uint64 unsigned long int + +#define int32 int +#define int64 long int + +#else + +#define uint32 unsigned int +#define uint64 unsigned long long int + +#define int32 int +#define int64 long long int +#endif + +// check 32/64 bits issues are correctly handled + +#define check_error(err) if (err) { printf("%s:%d, alsa error %d : %s\n", __FILE__, __LINE__, err, snd_strerror(err)); exit(1); } +#define check_error_msg(err,msg) if (err) { fprintf(stderr, "%s:%d, %s : %s(%d)\n", __FILE__, __LINE__, msg, snd_strerror(err), err); exit(1); } +#define display_error_msg(err,msg) if (err) { fprintf(stderr, "%s:%d, %s : %s(%d)\n", __FILE__, __LINE__, msg, snd_strerror(err), err); } + +#define max(x,y) (((x)>(y)) ? (x) : (y)) +#define min(x,y) (((x)<(y)) ? (x) : (y)) + +/** + * Used to set the priority and scheduling of the audi#include + #include +o thread + */ +static bool setRealtimePriority () +{ + struct passwd * pw; + int err; + uid_t uid; + struct sched_param param; + + uid = getuid (); + pw = getpwnam ("root"); + setuid (pw->pw_uid); + param.sched_priority = 50; /* 0 to 99 */ + err = sched_setscheduler(0, SCHED_RR, ¶m); + setuid (uid); + return (err != -1); +} + +/****************************************************************************** +******************************************************************************* + + AUDIO INTERFACE + +******************************************************************************* +*******************************************************************************/ + +enum { kRead = 1, kWrite = 2, kReadWrite = 3 }; + +/** + * A convenient class to pass parameters to AudioInterface + */ +class AudioParam +{ + public: + + const char* fCardName; + unsigned int fFrequency; + unsigned int fBuffering; + unsigned int fPeriods; + + unsigned int fSoftInputs; + unsigned int fSoftOutputs; + + public : + AudioParam() : + fCardName("hw:0"), + fFrequency(44100), + fBuffering(512), + fPeriods(2), + fSoftInputs(2), + fSoftOutputs(2) + {} + + AudioParam& cardName(const char* n) { fCardName = n; return *this; } + AudioParam& frequency(int f) { fFrequency = f; return *this; } + AudioParam& buffering(int fpb) { fBuffering = fpb; return *this; } + AudioParam& periods(int p) { fPeriods = p; return *this; } + AudioParam& inputs(int n) { fSoftInputs = n; return *this; } + AudioParam& outputs(int n) { fSoftOutputs = n; return *this; } +}; + +/** + * An ALSA audio interface + */ +class AudioInterface : public AudioParam +{ + public : + snd_pcm_t* fOutputDevice ; + snd_pcm_t* fInputDevice ; + snd_pcm_hw_params_t* fInputParams; + snd_pcm_hw_params_t* fOutputParams; + + snd_pcm_format_t fSampleFormat; + snd_pcm_access_t fSampleAccess; + + unsigned int fCardInputs; + unsigned int fCardOutputs; + + unsigned int fChanInputs; + unsigned int fChanOutputs; + + // interleaved mode audiocard buffers + void* fInputCardBuffer; + void* fOutputCardBuffer; + + // non interleaved mode audiocard buffers + void* fInputCardChannels[256]; + void* fOutputCardChannels[256]; + + // non interleaved mod, floating point software buffers + float* fInputSoftChannels[256]; + float* fOutputSoftChannels[256]; + + public : + + const char* cardName() { return fCardName; } + int frequency() { return fFrequency; } + int buffering() { return fBuffering; } + int periods() { return fPeriods; } + + float** inputSoftChannels() { return fInputSoftChannels; } + float** outputSoftChannels() { return fOutputSoftChannels; } + + + AudioInterface(const AudioParam& ap = AudioParam()) : AudioParam(ap) + { + + fInputDevice = 0; + fOutputDevice = 0; + fInputParams = 0; + fOutputParams = 0; + } + + /** + * Open the audio interface + */ + void open() + { + int err; + + // allocation d'un stream d'entree et d'un stream de sortie + err = snd_pcm_open( &fInputDevice, fCardName, SND_PCM_STREAM_CAPTURE, 0 ); check_error(err) + err = snd_pcm_open( &fOutputDevice, fCardName, SND_PCM_STREAM_PLAYBACK, 0 ); check_error(err) + + // recherche des parametres d'entree + err = snd_pcm_hw_params_malloc ( &fInputParams ); check_error(err); + setAudioParams(fInputDevice, fInputParams); + + // recherche des parametres de sortie + err = snd_pcm_hw_params_malloc ( &fOutputParams ); check_error(err) + setAudioParams(fOutputDevice, fOutputParams); + + // set the number of physical input and output channels close to what we need + fCardInputs = fSoftInputs; + fCardOutputs = fSoftOutputs; + + snd_pcm_hw_params_set_channels_near(fInputDevice, fInputParams, &fCardInputs); + snd_pcm_hw_params_set_channels_near(fOutputDevice, fOutputParams, &fCardOutputs); + + printf("inputs : %u, outputs : %u\n", fCardInputs, fCardOutputs); + + // enregistrement des parametres d'entree-sortie + + err = snd_pcm_hw_params (fInputDevice, fInputParams ); check_error (err); + err = snd_pcm_hw_params (fOutputDevice, fOutputParams ); check_error (err); + + //assert(snd_pcm_hw_params_get_period_size(fInputParams,NULL) == snd_pcm_hw_params_get_period_size(fOutputParams,NULL)); + + // allocation of alsa buffers + if (fSampleAccess == SND_PCM_ACCESS_RW_INTERLEAVED) { + fInputCardBuffer = calloc(interleavedBufferSize(fInputParams), 1); + fOutputCardBuffer = calloc(interleavedBufferSize(fOutputParams), 1); + + } else { + for (unsigned int i = 0; i < fCardInputs; i++) { + fInputCardChannels[i] = calloc(noninterleavedBufferSize(fInputParams), 1); + } + for (unsigned int i = 0; i < fCardOutputs; i++) { + fOutputCardChannels[i] = calloc(noninterleavedBufferSize(fOutputParams), 1); + } + + } + + // allocation of floating point buffers needed by the dsp code + + fChanInputs = max(fSoftInputs, fCardInputs); assert (fChanInputs < 256); + fChanOutputs = max(fSoftOutputs, fCardOutputs); assert (fChanOutputs < 256); + + for (unsigned int i = 0; i < fChanInputs; i++) { + fInputSoftChannels[i] = (float*) calloc (fBuffering, sizeof(float)); + for (unsigned int j = 0; j < fBuffering; j++) { + fInputSoftChannels[i][j] = 0.0; + } + } + + for (unsigned int i = 0; i < fChanOutputs; i++) { + fOutputSoftChannels[i] = (float*) calloc (fBuffering, sizeof(float)); + for (unsigned int j = 0; j < fBuffering; j++) { + fOutputSoftChannels[i][j] = 0.0; + } + } + } + + void setAudioParams(snd_pcm_t* stream, snd_pcm_hw_params_t* params) + { + int err; + + // set params record with initial values + err = snd_pcm_hw_params_any ( stream, params ); + check_error_msg(err, "unable to init parameters") + + // set alsa access mode (and fSampleAccess field) either to non interleaved or interleaved + + err = snd_pcm_hw_params_set_access (stream, params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); + if (err) { + err = snd_pcm_hw_params_set_access (stream, params, SND_PCM_ACCESS_RW_INTERLEAVED ); + check_error_msg(err, "unable to set access mode neither to non-interleaved or to interleaved"); + } + snd_pcm_hw_params_get_access(params, &fSampleAccess); + + + // search for 32-bits or 16-bits format + err = snd_pcm_hw_params_set_format (stream, params, SND_PCM_FORMAT_S32); + if (err) { + err = snd_pcm_hw_params_set_format (stream, params, SND_PCM_FORMAT_S16); + check_error_msg(err, "unable to set format to either 32-bits or 16-bits"); + } + snd_pcm_hw_params_get_format(params, &fSampleFormat); + // set sample frequency + snd_pcm_hw_params_set_rate_near (stream, params, &fFrequency, 0); + + // set period and period size (buffering) + err = snd_pcm_hw_params_set_period_size (stream, params, fBuffering, 0); + check_error_msg(err, "period size not available"); + + err = snd_pcm_hw_params_set_periods (stream, params, fPeriods, 0); + check_error_msg(err, "number of periods not available"); + } + + ssize_t interleavedBufferSize (snd_pcm_hw_params_t* params) + { + _snd_pcm_format format; snd_pcm_hw_params_get_format(params, &format); + snd_pcm_uframes_t psize; snd_pcm_hw_params_get_period_size(params, &psize, NULL); + unsigned int channels; snd_pcm_hw_params_get_channels(params, &channels); + ssize_t bsize = snd_pcm_format_size (format, psize * channels); + return bsize; + } + + ssize_t noninterleavedBufferSize (snd_pcm_hw_params_t* params) + { + _snd_pcm_format format; snd_pcm_hw_params_get_format(params, &format); + snd_pcm_uframes_t psize; snd_pcm_hw_params_get_period_size(params, &psize, NULL); + ssize_t bsize = snd_pcm_format_size (format, psize); + return bsize; + } + + void close() + {} + + /** + * Read audio samples from the audio card. Convert samples to floats and take + * care of interleaved buffers + */ + void read() + { + if (fSampleAccess == SND_PCM_ACCESS_RW_INTERLEAVED) { + + int count = snd_pcm_readi(fInputDevice, fInputCardBuffer, fBuffering); + if (count<0) { + display_error_msg(count, "reading samples"); + int err = snd_pcm_prepare(fInputDevice); + check_error_msg(err, "preparing input stream"); + } + + if (fSampleFormat == SND_PCM_FORMAT_S16) { + + short* buffer16b = (short*) fInputCardBuffer; + for (unsigned int s = 0; s < fBuffering; s++) { + for (unsigned int c = 0; c < fCardInputs; c++) { + fInputSoftChannels[c][s] = float(buffer16b[c + s*fCardInputs])*(1.0/float(SHRT_MAX)); + } + } + + } else if (fSampleFormat == SND_PCM_FORMAT_S32) { + + int32* buffer32b = (int32*) fInputCardBuffer; + for (unsigned int s = 0; s < fBuffering; s++) { + for (unsigned int c = 0; c < fCardInputs; c++) { + fInputSoftChannels[c][s] = float(buffer32b[c + s*fCardInputs])*(1.0/float(INT_MAX)); + } + } + } else { + + printf("unrecognized input sample format : %u\n", fSampleFormat); + exit(1); + } + + } else if (fSampleAccess == SND_PCM_ACCESS_RW_NONINTERLEAVED) { + + int count = snd_pcm_readn(fInputDevice, fInputCardChannels, fBuffering); + if (count<0) { + display_error_msg(count, "reading samples"); + int err = snd_pcm_prepare(fInputDevice); + check_error_msg(err, "preparing input stream"); + } + + if (fSampleFormat == SND_PCM_FORMAT_S16) { + + for (unsigned int c = 0; c < fCardInputs; c++) { + short* chan16b = (short*) fInputCardChannels[c]; + for (unsigned int s = 0; s < fBuffering; s++) { + fInputSoftChannels[c][s] = float(chan16b[s])*(1.0/float(SHRT_MAX)); + } + } + + } else if (fSampleFormat == SND_PCM_FORMAT_S32) { + + for (unsigned int c = 0; c < fCardInputs; c++) { + int32* chan32b = (int32*) fInputCardChannels[c]; + for (unsigned int s = 0; s < fBuffering; s++) { + fInputSoftChannels[c][s] = float(chan32b[s])*(1.0/float(INT_MAX)); + } + } + } else { + + printf("unrecognized input sample format : %u\n", fSampleFormat); + exit(1); + } + + } else { + check_error_msg(-10000, "unknow access mode"); + } + } + + /** + * write the output soft channels to the audio card. Convert sample + * format and interleaves buffers when needed + */ + void write() + { + recovery : + + if (fSampleAccess == SND_PCM_ACCESS_RW_INTERLEAVED) { + + if (fSampleFormat == SND_PCM_FORMAT_S16) { + + short* buffer16b = (short*) fOutputCardBuffer; + for (unsigned int f = 0; f < fBuffering; f++) { + for (unsigned int c = 0; c < fCardOutputs; c++) { + float x = fOutputSoftChannels[c][f]; + buffer16b[c + f*fCardOutputs] = short( max(min(x,1.0),-1.0) * float(SHRT_MAX) ) ; + } + } + + } else if (fSampleFormat == SND_PCM_FORMAT_S32) { + + int32* buffer32b = (int32*) fOutputCardBuffer; + for (unsigned int f = 0; f < fBuffering; f++) { + for (unsigned int c = 0; c < fCardOutputs; c++) { + float x = fOutputSoftChannels[c][f]; + buffer32b[c + f*fCardOutputs] = int( max(min(x,1.0),-1.0) * float(INT_MAX) ) ; + } + } + } else { + + printf("unrecognized output sample format : %u\n", fSampleFormat); + exit(1); + } + + int count = snd_pcm_writei(fOutputDevice, fOutputCardBuffer, fBuffering); + if (count<0) { + display_error_msg(count, "w3"); + int err = snd_pcm_prepare(fOutputDevice); + check_error_msg(err, "preparing output stream"); + goto recovery; + } + + + } else if (fSampleAccess == SND_PCM_ACCESS_RW_NONINTERLEAVED) { + + if (fSampleFormat == SND_PCM_FORMAT_S16) { + + for (unsigned int c = 0; c < fCardOutputs; c++) { + short* chan16b = (short*) fOutputCardChannels[c]; + for (unsigned int f = 0; f < fBuffering; f++) { + float x = fOutputSoftChannels[c][f]; + chan16b[f] = short( max(min(x,1.0),-1.0) * float(SHRT_MAX) ) ; + } + } + + } else if (fSampleFormat == SND_PCM_FORMAT_S32) { + + for (unsigned int c = 0; c < fCardOutputs; c++) { + int32* chan32b = (int32*) fOutputCardChannels[c]; + for (unsigned int f = 0; f < fBuffering; f++) { + float x = fOutputSoftChannels[c][f]; + chan32b[f] = int( max(min(x,1.0),-1.0) * float(INT_MAX) ) ; + } + } + + } else { + + printf("unrecognized output sample format : %u\n", fSampleFormat); + exit(1); + } + + int count = snd_pcm_writen(fOutputDevice, fOutputCardChannels, fBuffering); + if (count<0) { + display_error_msg(count, "w3"); + int err = snd_pcm_prepare(fOutputDevice); + check_error_msg(err, "preparing output stream"); + goto recovery; + } + + } else { + check_error_msg(-10000, "unknow access mode"); + } + } + + /** + * print short information on the audio device + */ + void shortinfo() + { + int err; + snd_ctl_card_info_t* card_info; + snd_ctl_t* ctl_handle; + err = snd_ctl_open (&ctl_handle, fCardName, 0); check_error(err); + snd_ctl_card_info_alloca (&card_info); + err = snd_ctl_card_info(ctl_handle, card_info); check_error(err); + printf("%s|%d|%d|%d|%d|%s\n", + snd_ctl_card_info_get_driver(card_info), + fCardInputs, fCardOutputs, + fFrequency, fBuffering, + snd_pcm_format_name((_snd_pcm_format)fSampleFormat)); + } + + /** + * print more detailled information on the audio device + */ + void longinfo() + { + int err; + snd_ctl_card_info_t* card_info; + snd_ctl_t* ctl_handle; + + printf("Audio Interface Description :\n"); + printf("Sampling Frequency : %d, Sample Format : %s, buffering : %d\n", + fFrequency, snd_pcm_format_name((_snd_pcm_format)fSampleFormat), fBuffering); + printf("Software inputs : %2d, Software outputs : %2d\n", fSoftInputs, fSoftOutputs); + printf("Hardware inputs : %2d, Hardware outputs : %2d\n", fCardInputs, fCardOutputs); + printf("Channel inputs : %2d, Channel outputs : %2d\n", fChanInputs, fChanOutputs); + + // affichage des infos de la carte + err = snd_ctl_open (&ctl_handle, fCardName, 0); check_error(err); + snd_ctl_card_info_alloca (&card_info); + err = snd_ctl_card_info(ctl_handle, card_info); check_error(err); + printCardInfo(card_info); + + // affichage des infos liees aux streams d'entree-sortie + if (fSoftInputs > 0) printHWParams(fInputParams); + if (fSoftOutputs > 0) printHWParams(fOutputParams); + } + + void printCardInfo(snd_ctl_card_info_t* ci) + { + printf("Card info (address : %p)\n", ci); + printf("\tID = %s\n", snd_ctl_card_info_get_id(ci)); + printf("\tDriver = %s\n", snd_ctl_card_info_get_driver(ci)); + printf("\tName = %s\n", snd_ctl_card_info_get_name(ci)); + printf("\tLongName = %s\n", snd_ctl_card_info_get_longname(ci)); + printf("\tMixerName = %s\n", snd_ctl_card_info_get_mixername(ci)); + printf("\tComponents = %s\n", snd_ctl_card_info_get_components(ci)); + printf("--------------\n"); + } + + void printHWParams( snd_pcm_hw_params_t* params ) + { + printf("HW Params info (address : %p)\n", params); +#if 0 + printf("\tChannels = %d\n", snd_pcm_hw_params_get_channels(params)); + printf("\tFormat = %s\n", snd_pcm_format_name((_snd_pcm_format)snd_pcm_hw_params_get_format(params))); + printf("\tAccess = %s\n", snd_pcm_access_name((_snd_pcm_access)snd_pcm_hw_params_get_access(params))); + printf("\tRate = %d\n", snd_pcm_hw_params_get_rate(params, NULL)); + printf("\tPeriods = %d\n", snd_pcm_hw_params_get_periods(params, NULL)); + printf("\tPeriod size = %d\n", (int)snd_pcm_hw_params_get_period_size(params, NULL)); + printf("\tPeriod time = %d\n", snd_pcm_hw_params_get_period_time(params, NULL)); + printf("\tBuffer size = %d\n", (int)snd_pcm_hw_params_get_buffer_size(params)); + printf("\tBuffer time = %d\n", snd_pcm_hw_params_get_buffer_time(params, NULL)); +#endif + printf("--------------\n"); + } + +}; + +// lopt : Scan Command Line long int Arguments +long lopt(int argc, char *argv[], const char* longname, const char* shortname, long def) +{ + for (int i=2; igetNumInputs()) + .outputs(DSP->getNumOutputs())); + } + virtual ~alsaaudio() { stop(); delete fAudio; } + + virtual bool init(const char */*name*/, dsp* DSP) { + AVOIDDENORMALS; + fAudio->open(); + DSP->init(fAudio->frequency()); + return true; + } + + virtual bool start() { + fRunning = true; + if (pthread_create( &fAudioThread, 0, __run, this)) + fRunning = false; + return fRunning; + } + + virtual void stop() { + if (fRunning) { + fRunning = false; + pthread_join (fAudioThread, 0); + } + } + + virtual void run() { + bool rt = setRealtimePriority(); + printf(rt ? "RT : ":"NRT: "); fAudio->shortinfo(); + fAudio->write(); + fAudio->write(); + while(fRunning) { + fAudio->read(); + fDSP->compute(fAudio->buffering(), fAudio->inputSoftChannels(), fAudio->outputSoftChannels()); + fAudio->write(); + } + } +}; + +void* __run (void* ptr) +{ + alsaaudio * alsa = (alsaaudio*)ptr; + alsa->run(); + return 0; +} + +#endif + +/********************END ARCHITECTURE SECTION (part 2/2)****************/ +