diff options
author | N.N. <matju@users.sourceforge.net> | 2010-01-05 22:49:36 +0000 |
---|---|---|
committer | N.N. <matju@users.sourceforge.net> | 2010-01-05 22:49:36 +0000 |
commit | 8dbec761cf858ea65900c8a094599857208d8c3a (patch) | |
tree | 3228c023f87f23a354da3b57fdc2afe5b7052032 /desiredata/src/s_audio_alsamm.c | |
parent | 529e59635598e2d90a7a49f6b4c676f8366109ba (diff) |
svn path=/trunk/; revision=12907
Diffstat (limited to 'desiredata/src/s_audio_alsamm.c')
-rw-r--r-- | desiredata/src/s_audio_alsamm.c | 644 |
1 files changed, 0 insertions, 644 deletions
diff --git a/desiredata/src/s_audio_alsamm.c b/desiredata/src/s_audio_alsamm.c deleted file mode 100644 index 54dbbf50..00000000 --- a/desiredata/src/s_audio_alsamm.c +++ /dev/null @@ -1,644 +0,0 @@ -/* Copyright (c) 1997-2003 Guenter Geiger, Miller Puckette, Larry Troxler, -* Winfried Ritsch, Karl MacMillan, and others. -* For information on usage and redistribution, and for a DISCLAIMER OF ALL -* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ -/* - this audiodriverinterface inputs and outputs audio data using - the ALSA MMAP API available on linux. - this is optimized for hammerfall cards and does not make an attempt to be general - now, please adapt to your needs or let me know ... - constrains now: - - audio Card with ALSA-Driver > 1.0.3, - - alsa-device (preferable hw) with MMAP NONINTERLEAVED SIGNED-32Bit features - - up to 4 cards with has to be hardwaresynced - (winfried) -*/ -#include <alsa/asoundlib.h> -#include "desire.h" -#include <errno.h> -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <fcntl.h> -#include <sched.h> -#include "s_audio_alsa.h" - -/* needed for alsa 0.9 compatibility: */ -#if (SND_LIB_MAJOR < 1) -#define ALSAAPI9 -#endif -/* sample type magic ... - Hammerfall/HDSP/DSPMADI cards always 32Bit where lower 8Bit not used (played) in AD/DA, - but can have some bits set (subchannel coding) -*/ -#define ALSAMM_SAMPLEWIDTH_32 sizeof(t_alsa_sample32) - -#ifndef INT32_MAX -#define INT32_MAX 0x7fffffff -#endif - -/* maybe: - don't assume we can turn all 31 bits when doing float-to-fix; - otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. - but not now on hammerfall (w) -*/ - -/* 24 Bit are used so MAX Samplevalue not INT32_MAX ??? */ -#define F32MAX 0x7fffff00 -#define CLIP32(x) (((x)>F32MAX)?F32MAX:((x) < -F32MAX)?-F32MAX:(x)) - -#define ALSAMM_FORMAT SND_PCM_FORMAT_S32 -/* maximum of 4 devices. you can mix rme9632,hdsp9632 (18 chans) rme9652,hdsp9652 (26 chans), dsp-madi (64 chans) if synced */ - -/* we need same samplerate, buffertime and so on for each card soo we use global vars... - time is in us, size in frames (i hope so ;-) */ -static unsigned int alsamm_sr = 0; -static unsigned int alsamm_buffertime = 0; -static unsigned int alsamm_buffersize = 0; - -static bool debug=0; - -/* bad style: we asume all cards give the same answer at init so we make this vars global - to have a faster access in writing reading during send_dacs */ -static snd_pcm_sframes_t alsamm_period_size; -static unsigned int alsamm_periods; -static snd_pcm_sframes_t alsamm_buffer_size; - -/* if more than this sleep detected, should be more than periodsize/samplerate ??? */ -static double sleep_time; - -/* now we just sum all inputs/outputs of used cards to a global count - and use them all - ... later we should just use some channels of each card for pd - so we reduce the overhead of using alsways all channels, - and zero the rest once at start, - because rme9652 and hdsp forces us to use all channels - in mmap mode... - -Note on why: - normally hdsp and dspmadi can handle channel - count from one to all since they can switch on/off - the dma for them to reduce pci load, but this is only - implemented in alsa low level drivers for dspmadi now and maybe fixed for hdsp in future -*/ - -static int alsamm_inchannels = 0; -static int alsamm_outchannels = 0; - -/* Defines */ -#define WATCH_PERIODS 90 -static int in_avail[WATCH_PERIODS]; -static int out_avail[WATCH_PERIODS]; -static int in_offset[WATCH_PERIODS]; -static int out_offset[WATCH_PERIODS]; -static int out_cm[WATCH_PERIODS]; -static char *outaddr[WATCH_PERIODS]; -static char *inaddr[WATCH_PERIODS]; -static int xruns_watch[WATCH_PERIODS]; -static int broken_opipe; -static int dac_send = 0; -static int alsamm_xruns = 0; - -static void show_availist() { - for(int i=1; i<WATCH_PERIODS; i++) { - post("%2d:avail i=%7d %s o=%7d(%5d), offset i=%7d %s o=%7d, ptr i=%12p o=%12p, %d xruns ", - i,in_avail[i],(out_avail[i] != in_avail[i])? "!=" : "==" , out_avail[i],out_cm[i], - in_offset[i],(out_offset[i] != in_offset[i])? "!=" : "==" , out_offset[i], - inaddr[i], outaddr[i], xruns_watch[i]); - } -} - -/* protos */ -static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, int *chs); -static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int playback); -static void alsamm_start(); -static void alsamm_stop(); - -/* for debugging attach output of alsa mesages to stdout stream */ -snd_output_t* alsa_stdout; - -static void check_error(int err, const char *why) {if (err < 0) error("%s: %s", why, snd_strerror(err));} -class AlsaError : Error { -public: - int err; - AlsaError (int err) : err(err) {}}; -#define CHK(CODE) do {int err=CODE; if (err<0) {error("%s: %s", #CODE, snd_strerror(err)); throw AlsaError(err);}} while (0) -#define CH(CODE) do {int err=CODE; if (err<0) {error("%s: %s", #CODE, snd_strerror(err)); }} while (0) - -int alsamm_open_audio(int rate) { - int err; - snd_pcm_hw_params_t* hw_params; - snd_pcm_sw_params_t* sw_params; - /* fragsize is an old concept now use periods, used to be called fragments. */ - /* Be aware in ALSA periodsize can be in bytes, where buffersize is in frames, - but sometimes buffersize is in bytes and periods in frames, crazy alsa... - ...we use periodsize and buffersize in frames */ - snd_pcm_hw_params_alloca(&hw_params); - snd_pcm_sw_params_alloca(&sw_params); - /* see add_devname */ - /* first have a look which cards we can get and set up device infos for them */ - /* init some structures */ - for(int i=0; i<ALSA_MAXDEV;i++) { - alsai.dev[i].a_synced=alsao.dev[i].a_synced=0; - alsai.dev[i].a_channels=alsao.dev[i].a_channels=-1; /* query defaults */ - } - alsamm_inchannels = 0; - alsamm_outchannels = 0; - /* opening alsa debug channel */ - CH(snd_output_stdio_attach(&alsa_stdout, stdout, 0)); - /* Weak failure prevention: - first card found (out then in) is used as a reference for parameter, - so this set the globals and other cards hopefully dont change them - */ - alsamm_sr = rate; - /* set the asked buffer time (alsa buffertime in us)*/ - alsamm_buffertime = alsamm_buffersize = 0; - if(sys_blocksize == 0) - alsamm_buffertime = sys_schedadvance; - else - alsamm_buffersize = sys_blocksize; - if(sys_verbose) - post("syschedadvance=%d us(%d Samples)so buffertime max should be this=%d" - "or sys_blocksize=%d (samples) to use buffersize=%d", - sys_schedadvance,sys_advance_samples,alsamm_buffertime,sys_blocksize,alsamm_buffersize); - alsamm_periods = 0; /* no one wants periods setting from command line ;-) */ - for (int i=0; i<alsao.ndev;i++) { - /* post("open audio out %d, of %lx, %d",i,&alsa_device[i], alsao.dev[i].a_handle); */ - try { - CHK(set_hwparams(alsao.dev[i].a_handle, hw_params, &(alsao.dev[i].a_channels))); - CHK(set_swparams(alsao.dev[i].a_handle, sw_params,1)); - alsamm_outchannels += alsao.dev[i].a_channels; - alsao.dev[i].a_addr = (char **)malloc(sizeof(char *)*alsao.dev[i].a_channels); - if(!alsao.dev[i].a_addr) {error("playback device outaddr allocation error:"); continue;} - memset(alsao.dev[i].a_addr, 0, sizeof(char*) * alsao.dev[i].a_channels); - post("playback device with %d channels and buffer_time %d us opened", alsao.dev[i].a_channels, alsamm_buffertime); - } catch (AlsaError) {continue;} - } - for (int i=0; i<alsai.ndev; i++) { - if(sys_verbose) post("capture card %d:--------------------",i); - CHK(set_hwparams(alsai.dev[i].a_handle, hw_params, &(alsai.dev[i].a_channels))); - alsamm_inchannels += alsai.dev[i].a_channels; - CHK(set_swparams(alsai.dev[i].a_handle, sw_params,0)); - alsai.dev[i].a_addr = (char **)malloc(sizeof(char*)*alsai.dev[i].a_channels); - if(!alsai.dev[i].a_addr) {error("capture device inaddr allocation error:"); continue;} - memset(alsai.dev[i].a_addr, 0, sizeof(char*) * alsai.dev[i].a_channels); - if(sys_verbose) post("capture device with %d channels and buffertime %d us opened", alsai.dev[i].a_channels,alsamm_buffertime); - } - /* check for linked handles of input for each output*/ - for (int i=0; i<(alsao.ndev < alsai.ndev ? alsao.ndev:alsai.ndev); i++) { - if (alsao.dev[i].a_devno == alsai.dev[i].a_devno) { - if ((err = snd_pcm_link(alsai.dev[i].a_handle, alsao.dev[i].a_handle)) == 0) { - alsai.dev[i].a_synced = alsao.dev[i].a_synced = 1; - if(sys_verbose) post("Linking in and outs of card %d",i); - } else error("could not link in and outs"); - } - } - /* some globals */ - sleep_time = (float) alsamm_period_size/ (float) alsamm_sr; - if (debug) { - /* start ---------------------------- */ - if(sys_verbose) post("open_audio: after dacsend=%d (xruns=%d)done",dac_send,alsamm_xruns); - alsamm_xruns = dac_send = 0; /* reset debug */ - /* start alsa in open or better in send_dacs once ??? we will see */ - for (int i=0;i<alsao.ndev;i++) snd_pcm_dump(alsao.dev[i].a_handle, alsa_stdout); - for (int i=0;i<alsai.ndev;i++) snd_pcm_dump(alsai.dev[i].a_handle, alsa_stdout); - fflush(stdout); - } - sys_setchsr(alsamm_inchannels, alsamm_outchannels, alsamm_sr, sys_dacblocksize); - alsamm_start(); - /* report success */ - return 0; -} - -void alsamm_close_audio() { - if(debug&&sys_verbose) post("closing devices"); - alsamm_stop(); - for (int i=0; i<alsao.ndev; i++) { - //if(debug&&sys_verbose) post("unlink audio out %d, of %lx",i,used_outdevice[i]); - if(alsao.dev[i].a_synced) { - CHK(snd_pcm_unlink(alsao.dev[i].a_handle)); - alsao.dev[i].a_synced = 0; - } - CHK(snd_pcm_close(alsao.dev[i].a_handle)); - if(alsao.dev[i].a_addr) {free(alsao.dev[i].a_addr); alsao.dev[i].a_addr=0;} - alsao.dev[i].a_channels = 0; - } - for (int i=0; i<alsai.ndev; i++) { - CHK(snd_pcm_close(alsai.dev[i].a_handle)); - if(alsai.dev[i].a_addr) {free(alsai.dev[i].a_addr); alsai.dev[i].a_addr=0;} - alsai.dev[i].a_channels = 0; - } - alsai.ndev = alsao.ndev = 0; - if(debug) { - if(sys_verbose) post("close_audio: after dacsend=%d (xruns=%d)done",dac_send,alsamm_xruns); - alsamm_xruns = dac_send = 0; - } -} - -/* ------- PCM INITS --------------------------------- */ -static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params,int *chs) { - try { -#ifndef ALSAAPI9 - unsigned int rrate; - /* choose all parameters */ - CHK(snd_pcm_hw_params_any(handle, params)); - /* set the nointerleaved read/write format */ - CHK(snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)); - /* set the sample format */ - CHK(snd_pcm_hw_params_set_format(handle, params, ALSAMM_FORMAT)); - if(debug&&sys_verbose) post("Setting format to %s",snd_pcm_format_name(ALSAMM_FORMAT)); - /* first check samplerate since channels numbers are samplerate dependend (double speed) */ - /* set the stream rate */ - rrate = alsamm_sr; - if(debug&&sys_verbose) post("Samplerate request: %i Hz",rrate); - int dir=-1; - CHK(snd_pcm_hw_params_set_rate_near(handle, params, &rrate, &dir)); - if (rrate != alsamm_sr) { - post("Warning: rate %iHz doesn't match requested %iHz", rrate,alsamm_sr); - alsamm_sr = rrate; - } else if (sys_verbose) post("Samplerate is set to %iHz",alsamm_sr); - /* Info on channels */ - { - int maxchs,minchs,channels = *chs; - CHK(snd_pcm_hw_params_get_channels_max(params, (unsigned *)&maxchs)); - CHK(snd_pcm_hw_params_get_channels_min(params, (unsigned *)&minchs)); - if(debug&&sys_verbose) post("Getting channels:min=%d, max= %d for request=%d",minchs,maxchs,channels); - if(channels<0) channels=maxchs; - if(channels>maxchs) channels = maxchs; - if(channels<minchs) channels = minchs; - if(channels != *chs) post("requested channels=%d but used=%d",*chs,channels); - *chs = channels; - if(debug&&sys_verbose) post("trying to use channels: %d",channels); - } - /* set the count of channels */ - CHK(snd_pcm_hw_params_set_channels(handle, params, *chs)); - /* testing for channels */ - CH(snd_pcm_hw_params_get_channels(params,(unsigned int *)chs)); - if(debug&&sys_verbose) post("When setting channels count, got %d",*chs); - /* if buffersize is set use this instead buffertime */ - if(alsamm_buffersize > 0) { - if(debug&&sys_verbose) post("hw_params: ask for max buffersize of %d samples", (unsigned int) alsamm_buffersize); - alsamm_buffer_size = alsamm_buffersize; - CHK(snd_pcm_hw_params_set_buffer_size_near(handle, params, (unsigned long *)&alsamm_buffer_size)); - } - else { - if(alsamm_buffertime <= 0) /* should never happen, but use 20ms */ - alsamm_buffertime = 20000; - if(debug&&sys_verbose) post("hw_params: ask for max buffertime of %d ms", (unsigned int) (alsamm_buffertime*0.001) ); - CHK(snd_pcm_hw_params_set_buffer_time_near(handle, params, &alsamm_buffertime, &dir)); - } - CHK(snd_pcm_hw_params_get_buffer_time(params, (unsigned *)&alsamm_buffertime, &dir)); - if(debug&&sys_verbose) post("hw_params: got buffertime to %f ms", float(alsamm_buffertime*0.001)); - CHK(snd_pcm_hw_params_get_buffer_size(params, (unsigned long *)&alsamm_buffer_size)); - if(debug&&sys_verbose) post("hw_params: got buffersize to %d samples",(int) alsamm_buffer_size); - CHK(snd_pcm_hw_params_get_period_size(params, (unsigned long *)&alsamm_period_size, &dir)); - if(debug&&sys_verbose) post("Got period size of %d", (int) alsamm_period_size); - { - unsigned int pmin,pmax; - CHK(snd_pcm_hw_params_get_periods_min(params, &pmin, &dir)); - CHK(snd_pcm_hw_params_get_periods_min(params, &pmax, &dir)); - /* use maximum of periods */ - if( alsamm_periods <= 0) alsamm_periods = pmax; - alsamm_periods = (alsamm_periods > pmax)?pmax:alsamm_periods; - alsamm_periods = (alsamm_periods < pmin)?pmin:alsamm_periods; - CHK(snd_pcm_hw_params_set_periods(handle, params, alsamm_periods, dir)); - CHK(snd_pcm_hw_params_get_periods(params, &pmin, &dir)); - if(debug&&sys_verbose) post("Got periods of %d, where periodsmin=%d, periodsmax=%d",alsamm_periods,pmin,pmax); - } - /* write the parameters to device */ - CHK(snd_pcm_hw_params(handle, params)); -#endif /* ALSAAPI9 */ - return 0; - } catch (AlsaError e) {return e.err;} -} - -static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int playback) { - try { -#ifndef ALSAAPI9 - snd_pcm_uframes_t ps,ops; - snd_pcm_uframes_t bs,obs; - /* get the current swparams */ - CHK(snd_pcm_sw_params_current(handle, swparams)); - /* AUTOSTART: start the transfer on each write/commit ??? */ - snd_pcm_sw_params_get_start_threshold(swparams, &obs); - CHK(snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U)); - snd_pcm_sw_params_get_start_threshold(swparams, &bs); - if(debug&&sys_verbose) post("sw_params: got start_thresh_hold= %d (was %d)",(int) bs,(int)obs); - /* AUTOSTOP: never stop the machine */ - snd_pcm_sw_params_get_stop_threshold(swparams, &obs); - CHK(snd_pcm_sw_params_set_stop_threshold(handle, swparams, (snd_pcm_uframes_t)-1)); - snd_pcm_sw_params_get_stop_threshold(swparams, &bs); - if(debug&&sys_verbose) post("sw_params: set stop_thresh_hold= %d (was %d)", (int) bs,(int)obs); - /* AUTOSILENCE: silence if overrun.... */ - snd_pcm_sw_params_get_silence_threshold(swparams, &ops); - CHK(snd_pcm_sw_params_set_silence_threshold(handle, swparams, alsamm_period_size)); - snd_pcm_sw_params_get_silence_threshold(swparams, &ps); - if(debug&&sys_verbose) post("sw_params: set silence_threshold = %d (was %d)", (int) ps,(int)ops); - snd_pcm_sw_params_get_silence_size(swparams, &ops); - CHK(snd_pcm_sw_params_set_silence_size(handle, swparams, alsamm_period_size)); - snd_pcm_sw_params_get_silence_size(swparams, &ps); - if(debug&&sys_verbose) post("sw_params: set silence_size = %d (was %d)", (int) ps,(int)ops); - /* AVAIL: allow the transfer when at least period_size samples can be processed */ - snd_pcm_sw_params_get_avail_min(swparams, &ops); - CHK(snd_pcm_sw_params_set_avail_min(handle, swparams, sys_dacblocksize/2)); - snd_pcm_sw_params_get_avail_min(swparams, &ps); - if(debug&&sys_verbose) post("sw_params: set avail_min= %d (was %d)", (int) ps, (int) ops); - /* ALIGN: align all transfers to 1 sample */ - snd_pcm_sw_params_get_xfer_align(swparams, &ops); - CHK(snd_pcm_sw_params_set_xfer_align(handle, swparams, 1)); - snd_pcm_sw_params_get_xfer_align(swparams, &ps); - if(debug&&sys_verbose) post("sw_params: set xfer_align = %d (was %d)", (int) ps, (int) ops); - /* write the parameters to the playback device */ - CHK(snd_pcm_sw_params(handle, swparams)); - if(debug&&sys_verbose) post("set sw finished"); -#else - post("alsa: need version 1.0 or above for mmap operation"); -#endif /* ALSAAPI9 */ - return 0; - } catch (AlsaError e) {return e.err;} -} - -/* ALSA Transfer helps */ - -/* xrun_recovery is called if time to late or error - Note: use outhandle if synced i/o; the devices are linked so prepare has only be called on out, hopefully resume too... -*/ -static int xrun_recovery(snd_pcm_t *handle, int err) { - if (debug) alsamm_xruns++; /* count xruns */ - if (err == -EPIPE) { /* under-run */ - CH(snd_pcm_prepare(handle)); - CH(snd_pcm_start(handle)); - return 0; - } else if (err == -ESTRPIPE) { - while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1); /* wait until the suspend flag is released */ - if (err < 0) { - CH(snd_pcm_prepare(handle)); - CH(snd_pcm_start(handle)); - } - return 0; - } - return err; -} - -/* note that snd_pcm_avail has to be called before using this funtion */ -static int alsamm_get_channels(snd_pcm_t *dev, snd_pcm_uframes_t *avail, snd_pcm_uframes_t *offset, int nchns, char **addr) { - int err = 0; - const snd_pcm_channel_area_t *mm_areas; - if (nchns>0 && avail && offset) { - err = snd_pcm_mmap_begin(dev, &mm_areas, offset, avail); - if (err<0) {check_error(err,"setmems: begin_mmap failure ???"); return err;} - for (int chn=0; chn<nchns; chn++) { - const snd_pcm_channel_area_t *a = &mm_areas[chn]; - addr[chn] = (char *) a->addr + ((a->first + a->step * *offset) / 8); - } - return err; - } - return -1; -} - -static void alsamm_start() { - int err = 0; - /* first prepare for in/out */ - for (int devno=0; devno<alsao.ndev; devno++) { - snd_pcm_uframes_t offset, avail; - t_alsa_dev *dev = &alsao.dev[devno]; - /* snd_pcm_prepare also in xrun, but cannot harm here */ - err = snd_pcm_prepare(dev->a_handle); - if (err<0) {check_error(err,"outcard prepare error for playback"); return;} - offset = 0; - avail = snd_pcm_avail_update(dev->a_handle); - if (avail != (snd_pcm_uframes_t) alsamm_buffer_size) { - check_error(avail,"full buffer not available at start"); - } - /* cleaning out mmap buffer before start */ - if(debug&&sys_verbose) post("start: set mems for avail=%ld,offset=%ld at buffersize=%ld",avail,offset,alsamm_buffer_size); - if(avail > 0) { - int comitted = 0; - err = alsamm_get_channels(dev->a_handle, &avail, &offset, dev->a_channels,dev->a_addr); - if (err<0) {check_error(err,"setting initial out channelspointer failure ?"); continue;} - for (int chn=0; chn<dev->a_channels; chn++) memset(dev->a_addr[chn],0,avail*ALSAMM_SAMPLEWIDTH_32); - comitted = snd_pcm_mmap_commit (dev->a_handle, offset, avail); - avail = snd_pcm_avail_update(dev->a_handle); - if(debug&&sys_verbose) post("start: now channels cleared, out with avail=%ld, offset=%ld,comitted=%d",avail,offset,comitted); - } - /* now start, should be autostarted */ - avail = snd_pcm_avail_update(dev->a_handle); - if(debug&&sys_verbose) post("start: finish start, out with avail=%ld, offset=%ld",avail,offset); - /* we have no autostart so anyway start*/ - err = snd_pcm_start (dev->a_handle); - if (err<0) check_error(err,"could not start playback"); - } - for (int devno=0; devno<alsai.ndev; devno++) { - snd_pcm_uframes_t ioffset, iavail; - t_alsa_dev *dev = &alsai.dev[devno]; - /* if devices are synced then don't need to prepare; hopefully dma in aereas allready filled correct by the card */ - if (dev->a_synced == 0) { - err = snd_pcm_prepare (dev->a_handle); - if (err<0) {check_error(err,"incard prepare error for capture"); /* return err;*/} - } - ioffset = 0; - iavail = snd_pcm_avail_update (dev->a_handle); - /* cleaning out mmap buffer before start */ - if (debug) post("start in: set in mems for avail=%ld,offset=%ld at buffersize=%ld",iavail,ioffset,alsamm_buffer_size); - if (iavail > (snd_pcm_uframes_t) 0) { - if (debug) post("empty buffer not available at start, since avail %ld != %ld buffersize",iavail,alsamm_buffer_size); - err = alsamm_get_channels(dev->a_handle, &iavail, &ioffset, dev->a_channels,dev->a_addr); - if (err<0) {check_error(err,"getting in channelspointer failure ????"); continue;} - snd_pcm_mmap_commit (dev->a_handle, ioffset, iavail); - iavail = snd_pcm_avail_update (dev->a_handle); - if (debug) post("start in now avail=%ld",iavail); - } - if (debug) post("start: init inchannels with avail=%ld, offset=%ld",iavail,ioffset); - /* if devices are synced then dont need to start */ - /* start with autostart , but anyway start */ - if(dev->a_synced == 0) { - err = snd_pcm_start (dev->a_handle); - if (err<0) {check_error(err,"could not start capture"); continue;} - } - } -} - -static void alsamm_stop() { - for (int devno=0; devno<alsai.ndev; devno++) { - t_alsa_dev *dev = &alsai.dev[devno]; if(sys_verbose) post("stop in device %d",devno); - CH(snd_pcm_drop(dev->a_handle)); - } - for (int devno=0; devno<alsao.ndev;devno++) { - t_alsa_dev *dev = &alsao.dev[devno]; if(sys_verbose) post("stop out device %d",devno); - CH(snd_pcm_drop(dev->a_handle)); - } - if (debug) show_availist(); -} - -/* ---------- ADC/DAC tranfer in the main loop ------- */ -/* I see: (a guess as a documentation) - all DAC data is in sys_soundout array with - sys_dacblocksize (mostly 64) for each channels which - if we have more channels opened then dac-channels = sys_outchannels - we have to zero (silence them), which should be done once. - -Problems to solve: - - a) Since in ALSA MMAP, the MMAP reagion can change (don't ask me why) - we have to do it each cycle or we say on RME HAMMERFALL/HDSP/DSPMADI - it never changes to it once. so maybe we can do it once in open - - b) we never know if inputs are synced and zero them if not, - except we use the control interface to check for, but this is - a systemcall we cannot afford in RT Loops so we just dont - and if not it will click... users fault ;-))) -*/ - -int alsamm_send_dacs() { - static double timenow,timelast; - t_sample *fpo, *fpi, *fp1, *fp2; - int i, err, devno; - snd_pcm_sframes_t size; - snd_pcm_sframes_t commitres; - snd_pcm_state_t state; - /* unused channels should be zeroed out on startup (open) and stay this */ - int inchannels = sys_inchannels; - int outchannels = sys_outchannels; - timelast = sys_getrealtime(); - if (debug) { - if(dac_send++ < 0) post("dac send called in %d, out %d, xrun %d",inchannels,outchannels, alsamm_xruns); - if(alsamm_xruns && (alsamm_xruns % 1000) == 0) post("1000 xruns accoured"); - if(dac_send < WATCH_PERIODS) { - out_cm[dac_send] = -1; - in_avail[dac_send] = out_avail[dac_send] = -1; - in_offset[dac_send] = out_offset[dac_send] = -1; - outaddr[dac_send] = inaddr[dac_send] = NULL; - xruns_watch[dac_send] = alsamm_xruns; - } - } - if (!inchannels && !outchannels) return SENDDACS_NO; - /* here we should check if in and out samples are here. - but, the point is if out samples available also in sample should, - so we don't make a precheck of insamples here and let outsample check be the first of the first card. */ - /* OUTPUT Transfer */ - fpo = sys_soundout; - for (int devno=0; devno<alsao.ndev; devno++) { - t_alsa_dev *dev = &alsao.dev[devno]; - snd_pcm_t *out = dev->a_handle; - int ochannels =dev->a_channels; - /* how much samples available ??? */ - snd_pcm_sframes_t ooffset=0, oavail=snd_pcm_avail_update(out); - /* only one reason i can think about, the driver stopped and says broken pipe - so this should not happen if we have enough stopthreshold but if try to restart with next commit */ - if (oavail<0) { - if (debug) broken_opipe++; - err = xrun_recovery(out, -EPIPE); - if (err < 0) {check_error(err,"otavail<0 recovery failed"); return SENDDACS_NO;} - oavail = snd_pcm_avail_update(out); - } - /* check if we are late and have to (able to) catch up */ - /* xruns will be ignored since you cant do anything since already happend */ - state = snd_pcm_state(out); - if (state == SND_PCM_STATE_XRUN) { - err = xrun_recovery(out, -EPIPE); if (err<0) {check_error(err,"DAC XRUN recovery failed" ); return SENDDACS_NO;} - oavail = snd_pcm_avail_update(out); - } else if (state == SND_PCM_STATE_SUSPENDED) { - err = xrun_recovery(out, -ESTRPIPE); if (err<0) {check_error(err,"DAC SUSPEND recovery failed"); return SENDDACS_NO;} - oavail = snd_pcm_avail_update(out); - } - if(debug && dac_send < WATCH_PERIODS) out_avail[dac_send] = oavail; - /* we only transfer transfersize of bytes request, this should only happen on first card otherwise we got a problem */ - if(oavail < sys_dacblocksize) return SENDDACS_NO; - /* transfer now */ - size = sys_dacblocksize; fp1 = fpo; - /* since this can go over a buffer boundery we maybe need two steps to transfer - (normally when buffersize is a multiple of transfersize this should never happen) */ - while (size>0) { - snd_pcm_sframes_t oframes = size; - err = alsamm_get_channels(out, (unsigned long *)&oframes, (unsigned long *)&ooffset,ochannels,dev->a_addr); - if(debug && dac_send < WATCH_PERIODS) {out_offset[dac_send] = ooffset; outaddr[dac_send] = (char *) dev->a_addr[0];} - if (err<0) { - err = xrun_recovery(out, err); - if (err<0) {check_error(err,"MMAP begins avail error"); break; /* next card please */} - } - /* transfer into memory */ - for (int chn=0; chn<ochannels; chn++) { - t_alsa_sample32 *buf = (t_alsa_sample32 *)dev->a_addr[chn]; - /* osc(buf, oframes, (dac_send%1000 < 500)?-100.0:-10.0,440,&(indexes[chn])); */ - for (i = 0, fp2 = fp1 + chn*sys_dacblocksize; i < oframes; i++,fp2++) { - /* better but slower, better never clip ;-) buf[i] = CLIP32(s1); */ - buf[i] = int(*fp2 * F32MAX) & 0xFFFFFF00; - *fp2 = 0.0; - } - } - commitres = snd_pcm_mmap_commit(out, ooffset, oframes); - if (commitres < 0 || commitres != oframes) { - err = xrun_recovery(out, commitres >= 0 ? -EPIPE : commitres); - if (err<0) {check_error(err,"MMAP commit error"); return SENDDACS_NO;} - } - if(debug && dac_send < WATCH_PERIODS) out_cm[dac_send] = oframes; - fp1 += oframes; - size -= oframes; - } /* while size */ - fpo += ochannels*sys_dacblocksize; - }/* for devno */ - fpi = sys_soundin; /* star first card first channel */ - for (devno=0; devno<alsai.ndev; devno++) { - t_alsa_dev *dev = &alsai.dev[devno]; - snd_pcm_t *in = dev->a_handle; - int ichannels = dev->a_channels; - snd_pcm_sframes_t ioffset=0, iavail=snd_pcm_avail_update(in); - if (iavail<0) { - err = xrun_recovery(in, iavail); - if (err<0) {check_error(err,"input avail update failed"); return SENDDACS_NO;} - iavail=snd_pcm_avail_update(in); - } - state = snd_pcm_state(in); - if (state == SND_PCM_STATE_XRUN) { - err = xrun_recovery(in, -EPIPE); if (err<0) {check_error(err,"ADC XRUN recovery failed" ); return SENDDACS_NO;} - iavail=snd_pcm_avail_update(in); - } else if (state == SND_PCM_STATE_SUSPENDED) { - err = xrun_recovery(in,-ESTRPIPE); if (err<0) {check_error(err,"ADC SUSPEND recovery failed"); return SENDDACS_NO;} - iavail=snd_pcm_avail_update(in); - } - /* only transfer full transfersize or nothing */ - if(iavail < sys_dacblocksize) return SENDDACS_NO; - size = sys_dacblocksize; - fp1 = fpi; - /* since sysdata can go over a driver buffer boundery we maybe need two steps to transfer - (normally when buffersize is a multiple of transfersize this should never happen) */ - while(size>0) { - snd_pcm_sframes_t iframes = size; - err = alsamm_get_channels(in, (unsigned long *)&iframes, (unsigned long *)&ioffset,ichannels,dev->a_addr); - if (err<0) { - err = xrun_recovery(in, err); - if (err<0) {check_error(err,"MMAP begins avail error"); return SENDDACS_NO;} - } - if(debug && dac_send < WATCH_PERIODS) {in_avail[dac_send] = iavail; in_offset[dac_send] = ioffset; inaddr[dac_send] = dev->a_addr[0];} - /* transfer into memory */ - for (int chn=0; chn<ichannels; chn++) { - t_alsa_sample32 *buf = (t_alsa_sample32 *) dev->a_addr[chn]; - for (i = 0, fp2 = fp1 + chn*sys_dacblocksize; i < iframes; i++,fp2++) { - /* mask the lowest bits, since subchannels info can make zero samples nonzero */ - *fp2 = float(t_alsa_sample32(buf[i]&0xFFFFFF00)) * (1.0/float(INT32_MAX)); - } - } - commitres = snd_pcm_mmap_commit(in, ioffset, iframes); - if (commitres < 0 || commitres != iframes) { - post("please never"); - err = xrun_recovery(in, commitres >= 0 ? -EPIPE : commitres); - if (err<0) {check_error(err,"MMAP synced in commit error"); return SENDDACS_NO;} - } - fp1 += iframes; - size -= iframes; - } - fpi += ichannels*sys_dacblocksize; - } /* for out devno < alsamm_outcards */ - timenow = sys_getrealtime(); - if (timenow > timelast+sleep_time) { - if(debug && dac_send < 10 && sys_verbose) - post("slept %f > %f + %f (=%f)", timenow,timelast,sleep_time,(timelast + sleep_time)); - return SENDDACS_SLEPT; - } - return SENDDACS_YES; -} |