[Sugar-devel] [PATCH TamTam 1/2 v2] Output to ALSA directly from csound
Gonzalo Odiard
gonzalo at laptop.org
Thu May 2 09:44:23 EDT 2013
Thanks Daniel.
Two questions:
when was the change done? I mean, what sugar version should we mark as
compatible
with a activity with this change?
The second is, if you already have the binaries compiled for every xo,
if you can send me the binaries, I don't need do it again.
Gonzalo
On Wed, May 1, 2013 at 12:25 PM, Daniel Drake <dsd at laptop.org> wrote:
> TamTam sound is crackly on some setups (e.g. XO-1.5 and newer with
> dmix running at 48000Hz).
>
> Clooper seems to implement its own ALSA sample rate resampling,
> as well as upsampling of the period rate to overcome any differences
> in csound period size and ALSA period size. This code is the cause of
> the crackles.
>
> Switch to csound's internal ALSA backend, which works well, and does
> not have these problems. Tested on XO-1, XO-1.5, XO-1.75 and XO-4.
> ---
> common/Config.py | 5 -
> common/Resources/tamtamorc.csd | 2 +-
> common/Util/CSoundClient.py | 4 +-
> common/Util/Clooper/Makefile | 4 +-
> common/Util/Clooper/aclient.cpp | 127 +++------------------
> common/Util/Clooper/audio.cpp | 237
> ----------------------------------------
> 6 files changed, 17 insertions(+), 362 deletions(-)
> delete mode 100644 common/Util/Clooper/audio.cpp
>
> v2: totally remove dependency on ALSA header and library
>
> diff --git a/common/Config.py b/common/Config.py
> index b1c1318..bb9c3ff 100644
> --- a/common/Config.py
> +++ b/common/Config.py
> @@ -55,13 +55,8 @@ for i in (INSTANCE_DIR, DATA_DIR, SNDS_INFO_DIR,
> TMP_DIR):
> PLUGIN_DEBUG = os.getenv("CSOUND_LOGFILE", "")
> PLUGIN_VERBOSE = DEBUG
> PLUGIN_UNIVORC = join(FILES_DIR, "tamtamorc.csd")
> -PLUGIN_KSMPS = 64
> PLUGIN_RATE = 16000
>
> -## PLUGIN ALSA PARAMETERS:
> -PLUGIN_PERIOD = 1024
> -PLUGIN_NPERIODS = 2
> -
> try:
> from sugar3.graphics.toolbarbox import ToolbarBox, ToolbarButton
> HAVE_TOOLBOX = True
> diff --git a/common/Resources/tamtamorc.csd
> b/common/Resources/tamtamorc.csd
> index f0a63f1..3adf81b 100644
> --- a/common/Resources/tamtamorc.csd
> +++ b/common/Resources/tamtamorc.csd
> @@ -1,6 +1,6 @@
> <CsoundSynthesizer>
> <CsOptions>
> --n -m0 -W -s -d
> +-n -odac -m0 -W -s -d
> </CsOptions>
> <CsInstruments>
> sr=16000
> diff --git a/common/Util/CSoundClient.py b/common/Util/CSoundClient.py
> index 3cf6794..c082137 100644
> --- a/common/Util/CSoundClient.py
> +++ b/common/Util/CSoundClient.py
> @@ -47,9 +47,7 @@ class _CSoundClientPlugin:
>
> def __init__(self):
> sc_initialize( Config.PLUGIN_UNIVORC, Config.PLUGIN_DEBUG,
> - Config.PLUGIN_PERIOD, Config.PLUGIN_NPERIODS,
> - Config.PLUGIN_VERBOSE,
> - Config.PLUGIN_KSMPS, Config.PLUGIN_RATE)
> + Config.PLUGIN_VERBOSE, Config.PLUGIN_RATE)
> self.on = False
> #self.masterVolume = 100.0
> self.periods_per_buffer = 2
> diff --git a/common/Util/Clooper/Makefile b/common/Util/Clooper/Makefile
> index 0f28366..4fddb2e 100644
> --- a/common/Util/Clooper/Makefile
> +++ b/common/Util/Clooper/Makefile
> @@ -15,7 +15,7 @@ LIB_NAME = $(CSOUND_ARCH)_$(CSOUND_VERSION)
> CXXFLAGS = $(shell python-config --cflags) \
> -Wall -Werror -fPIC -O2 -finline
> LDFLAGS+= $(python-config --libs) \
> - -lasound -lcsound
> + -lcsound
>
> all : aclient.so
> rm -rf $(LIB_NAME)
> @@ -23,7 +23,7 @@ all : aclient.so
> mv aclient.so $(LIB_NAME)/
> touch $(LIB_NAME)/__init__.py
>
> -aclient.so : aclient.cpp audio.cpp
> +aclient.so : aclient.cpp
> g++ $(CXXFLAGS) -shared -o $@ $< $(LDFLAGS)
>
> clean :
> diff --git a/common/Util/Clooper/aclient.cpp
> b/common/Util/Clooper/aclient.cpp
> index f238c36..e68b8cb 100644
> --- a/common/Util/Clooper/aclient.cpp
> +++ b/common/Util/Clooper/aclient.cpp
> @@ -12,20 +12,8 @@
> #include <cmath>
>
> #include <csound/csound.h>
> -#include <alsa/asoundlib.h>
>
> -static double pytime(const struct timeval * tv)
> -{
> - struct timeval t;
> - if (!tv)
> - {
> - tv = &t;
> - gettimeofday(&t, NULL);
> - }
> - return (double) tv->tv_sec + (double) tv->tv_usec / 1000000.0;
> -}
> #include "log.cpp"
> -#include "audio.cpp"
>
>
> int VERBOSE = 3;
> @@ -510,52 +498,31 @@ struct TamTamSound
> MYFLT tick_total;
>
> /** the upsampling ratio from csound */
> - unsigned int csound_ksmps;
> - snd_pcm_uframes_t csound_frame_rate;
> - snd_pcm_uframes_t csound_period_size;
> - snd_pcm_uframes_t period0;
> - unsigned int period_per_buffer; //should be 2
> - int up_ratio; //if the hardware only supports a small integer
> multiple of our effective samplerate, do a real-time conversion
> + int csound_frame_rate;
> + long csound_period_size;
>
> log_t * ll;
> - SystemStuff * sys_stuff;
>
> - TamTamSound(log_t * ll, char * orc, snd_pcm_uframes_t period0,
> unsigned int ppb, int ksmps, int framerate )
> + TamTamSound(log_t * ll, char * orc, int framerate )
> : ThreadID(NULL), PERF_STATUS(STOP), csound(NULL),
> music(),
> ticks_per_period(0.0),
> tick_adjustment(0.0),
> tick_total(0.0),
> - csound_ksmps(ksmps), //must agree with the
> orchestra file
> csound_frame_rate(framerate), //must agree with the
> orchestra file
> - period0(period0),
> - period_per_buffer(ppb),
> - up_ratio(1),
> - ll( ll ),
> - sys_stuff(NULL)
> + ll( ll )
> {
> - sys_stuff = new SystemStuff(ll);
> - if (0 > sys_stuff->open(csound_frame_rate, 4, period0,
> period_per_buffer))
> - {
> - return;
> - }
> - sys_stuff->close(0);
> - up_ratio = sys_stuff->rate / csound_frame_rate;
> - csound_period_size = (sys_stuff->period_size % up_ratio == 0)
> - ? sys_stuff->period_size / up_ratio
> - : csound_ksmps * 4;
> -
> csound = csoundCreate(NULL);
> - int argc=3;
> + int argc=4;
> const char **argv = (const char**)malloc(argc*sizeof(char*));
> argv[0] = "csound";
> argv[1] = "-m0";
> - argv[2] = orc;
> + argv[2] = "-+rtaudio=alsa";
> + argv[3] = orc;
>
> ll->printf(1, "loading csound orchestra file %s\n", orc);
> //csoundInitialize(&argc, &argv, 0);
> csoundPreCompile(csound);
> - csoundSetHostImplementedAudioIO(csound, 1, csound_period_size);
> int result = csoundCompile(csound, argc, (char**)argv);
> if (result)
> {
> @@ -563,6 +530,8 @@ struct TamTamSound
> ll->printf( "ERROR: csoundCompile of orchestra %s failed with
> code %i\n", orc, result);
> }
> free(argv);
> + csound_period_size = csoundGetOutputBufferSize(csound);
> + csound_period_size /= 2; /* channels */
> setTickDuration(0.05);
> }
> ~TamTamSound()
> @@ -574,7 +543,6 @@ struct TamTamSound
> csoundDestroy(csound);
> }
> ll->printf(2, "TamTamSound destroyed\n");
> - if (sys_stuff) delete sys_stuff;
> delete ll;
> }
> bool good()
> @@ -586,76 +554,10 @@ struct TamTamSound
> {
> assert(csound);
>
> - const int nchannels = 2;
> - int nloops = 0;
> - long int csound_nsamples = csoundGetOutputBufferSize(csound);
> - long int csound_nframes = csound_nsamples / nchannels;
> -
> - ll->printf(2, "INFO: nsamples = %li nframes = %li\n",
> csound_nsamples, csound_nframes);
> -
> - if (0 > sys_stuff->open(csound_frame_rate, 4, period0,
> period_per_buffer))
> - {
> - ll->printf( "ERROR: failed to open alsa device, thread
> abort\n");
> - return 1;
> - }
> -
> - assert(up_ratio == (signed)(sys_stuff->rate / csound_frame_rate));
> -
> - bool do_upsample = (signed)sys_stuff->period_size !=
> csound_nframes;
> - short *upbuf = new short[ sys_stuff->period_size * nchannels ];
> - int cbuf_pos = csound_nframes; // trigger a call to
> csoundPerformBuffer immediately
> - float *cbuf = NULL;
> - int up_pos = 0;
> - int ratio_pos = 0;
> -
> tick_total = 0.0f;
> -
> while (PERF_STATUS == CONTINUE)
> {
> - if ( do_upsample ) //fill one period of audio buffer data by
> 0 or more calls to csound
> - {
> - up_pos = 0;
> - int messed = 0;
> - short cursample[2]={0,0};
> - while(!messed)
> - {
> - if (cbuf_pos == csound_nframes)
> - {
> - cbuf_pos = 0;
> - if (csoundPerformBuffer(csound)) { messed =
> 1;break;}
> - cbuf = csoundGetOutputBuffer(csound);
> - cursample[0] = (signed short int)
> (cbuf[cbuf_pos*2+0] * (1<<15));
> - cursample[1] = (signed short int)
> (cbuf[cbuf_pos*2+1] * (1<<15));
> -
> - }
> - upbuf[2*up_pos+0] = cursample[0];
> - upbuf[2*up_pos+1] = cursample[1];
> - if (++ratio_pos == up_ratio)
> - {
> - ratio_pos = 0;
> - ++cbuf_pos;
> - cursample[0] = (signed short int)
> (cbuf[cbuf_pos*2+0] * (1<<15));
> - cursample[1] = (signed short int)
> (cbuf[cbuf_pos*2+1] * (1<<15));
> - }
> -
> - if (++up_pos == (signed)sys_stuff->period_size) break;
> - }
> - if (messed || (up_pos != (signed)sys_stuff->period_size))
> break;
> -
> - if (0 > sys_stuff->writebuf(sys_stuff->period_size,
> upbuf)) break;
> - }
> - else //fill one period of audio directly from
> csound
> - {
> - if (csoundPerformBuffer(csound)) break;
> - cbuf = csoundGetOutputBuffer(csound);
> - for (int i = 0; i < csound_nframes * nchannels; ++i)
> - {
> - cbuf[i] *= (float) ((1<<15)-100.0f);
> - upbuf[i] = (signed short int) cbuf[i];
> - }
> - if (0 > sys_stuff->writebuf(csound_nframes,upbuf)) break;
> - }
> -
> + if (csoundPerformBuffer(csound)) break;
> if (tick_adjustment > - ticks_per_period)
> {
> MYFLT tick_inc = ticks_per_period + tick_adjustment;
> @@ -667,11 +569,8 @@ struct TamTamSound
> {
> tick_adjustment += ticks_per_period;
> }
> - ++nloops;
> }
>
> - sys_stuff->close(1);
> - delete [] upbuf;
> ll->printf(2, "INFO: performance thread returning 0\n");
> return 0;
> }
> @@ -820,8 +719,8 @@ DECL(sc_initialize) //(char * csd)
> {
> char * str;
> char * log_file;
> - int period, ppb, ksmps, framerate;
> - if (!PyArg_ParseTuple(args, "ssiiiii", &str, &log_file, &period,
> &ppb, &VERBOSE, &ksmps, &framerate ))
> + int framerate;
> + if (!PyArg_ParseTuple(args, "ssii", &str, &log_file, &VERBOSE,
> &framerate ))
> {
> return NULL;
> }
> @@ -840,7 +739,7 @@ DECL(sc_initialize) //(char * csd)
> fprintf(stderr, "Logging disabled on purpose\n");
> }
> g_log = new log_t(_debug, VERBOSE);
> - g_tt = new TamTamSound(g_log, str, period, ppb, ksmps, framerate);
> + g_tt = new TamTamSound(g_log, str, framerate);
> g_music = & g_tt->music;
> atexit(&cleanup);
> if (g_tt->good())
> diff --git a/common/Util/Clooper/audio.cpp b/common/Util/Clooper/audio.cpp
> deleted file mode 100644
> index 6f5a271..0000000
> --- a/common/Util/Clooper/audio.cpp
> +++ /dev/null
> @@ -1,237 +0,0 @@
> -#ifndef AUDIO_HXX
> -#define AUDIO_HXX
> -
> -/*
> - * Latency test program
> - *
> - * Author: Jaroslav Kysela <perex at suse.cz>
> - *
> - * This small demo program can be used for measuring latency between
> - * capture and playback. This latency is measured from driver (diff when
> - * playback and capture was started). Scheduler is set to SCHED_RR.
> - *
> - *
> - * 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
> - * the Free Software Foundation; either version 2 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, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
> USA
> - *
> - */
> -
> -#include <stdio.h>
> -#include <stdlib.h>
> -#include <string.h>
> -#include <sched.h>
> -#include <errno.h>
> -#include <getopt.h>
> -#include <sys/time.h>
> -#include <math.h>
> -
> -#include <string>
> -#include <alsa/asoundlib.h>
> -
> -#define ERROR_HERE ll->printf("ERROR_HERE: %s %i\n", __FILE__, __LINE__)
> -
> -struct SystemStuff
> -{
> - log_t * ll;
> -
> - snd_pcm_t *phandle;
> - snd_pcm_uframes_t period_size;
> - unsigned int rate;
> - const snd_pcm_format_t sample_format;
> - SystemStuff(log_t * ll) : ll(ll), phandle(NULL), period_size(0),
> rate(0), sample_format(SND_PCM_FORMAT_S16)
> - {
> - }
> - ~SystemStuff()
> - {
> - if (phandle) close(0);
> - }
> -
> - void setscheduler(void)
> - {
> - struct sched_param sched_param;
> -
> - if (sched_getparam(0, &sched_param) < 0) {
> - ll->printf( "Scheduler getparam failed...\n");
> - return;
> - }
> - sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
> - if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
> - ll->printf( "Scheduler set to Round Robin with priority
> %i...\n", sched_param.sched_priority);
> - return;
> - }
> - ll->printf( "!!!Scheduler set to Round Robin with priority %i
> FAILED!!!\n", sched_param.sched_priority);
> - }
> -
> - int open(unsigned int rate0, int upsample_max, snd_pcm_uframes_t
> period0, unsigned int p_per_buff)
> - {
> - snd_pcm_hw_params_t *hw;
> -
> - if (phandle)
> - {
> - ll->printf( "ERROR: open called twice! First close the sound
> device\n");
> - return -1;
> - }
> -
> - if ( 0 > snd_pcm_open(&phandle, "default",
> SND_PCM_STREAM_PLAYBACK, 0)) { ERROR_HERE; return -1; }
> - if ( 0 > snd_pcm_hw_params_malloc(&hw))
> { ERROR_HERE; snd_pcm_close(phandle); phandle = NULL; return -1; }
> -
> - //now we can be a bit flexible with the buffer size and the
> sample-rate...
> -
> - int upsample;
> - for (upsample = 1; upsample < upsample_max; ++upsample)
> - {
> - rate = rate0 * upsample;
> -
> - if ( 0 > snd_pcm_hw_params_any(phandle, hw))
> { ERROR_HERE; goto open_error;}
> -
> - //first do the compulsory steps... interleaved float, 2
> channel
> - if ( 0 > snd_pcm_hw_params_set_rate_resample(phandle, hw, 0))
> { ERROR_HERE; goto open_error;}
> - if ( 0 > snd_pcm_hw_params_test_access(phandle, hw,
> SND_PCM_ACCESS_RW_INTERLEAVED)){ ERROR_HERE; goto open_error;}
> - if ( 0 > snd_pcm_hw_params_set_access(phandle, hw,
> SND_PCM_ACCESS_RW_INTERLEAVED)){ ERROR_HERE; goto open_error;}
> - if ( 0 > snd_pcm_hw_params_test_format(phandle, hw,
> sample_format)) { ERROR_HERE; goto open_error;}
> - if ( 0 > snd_pcm_hw_params_set_format(phandle, hw,
> sample_format)) { ERROR_HERE; goto open_error;}
> - if ( 0 > snd_pcm_hw_params_set_channels(phandle, hw, 2))
> { ERROR_HERE; goto open_error;}
> -
> - if ( snd_pcm_hw_params_set_rate_near(phandle, hw, &rate, 0))
> - {
> - ll->printf("test_rate failed( %i\n", rate);
> - continue;
> - }
> - else
> - {
> - ll->printf(1, "success! setting rate : %i\n", rate);
> -
> - snd_pcm_uframes_t minb=0, maxb= 0;
> - int mind=0, maxd=0;
> - snd_pcm_hw_params_get_period_size_min(hw, &minb,&mind);
> - snd_pcm_hw_params_get_period_size_max(hw, &maxb,&maxd);
> - ll->printf(2, "FYI: period size range is
> [%li/%i,%li/%i]\n", minb,mind, maxb, maxd);
> -
> - if ((mind != 0) || (maxd == 0))
> - {
> - ll->printf(2, "watch out, mind and maxd non-zero...
> you didn't set rate_resample to 0 did you...\n");
> - }
> -
> - if (period0 < minb)
> - {
> - ll->printf(1, "requested period size (%li) < min
> (%li), adjusting to min\n", period_size, minb);
> - period_size = minb;
> - }
> - else if (period0 > maxb)
> - {
> - ll->printf(2, "requested period size (%li) < max
> (%li), adjusting to min\n", period_size, maxb);
> - period_size = maxb;
> - }
> - else
> - {
> - period_size = period0;
> - }
> -
> - ll->printf(1, "testing period size : %li\n",
> period_size);
> - if ( 0 > snd_pcm_hw_params_test_period_size(phandle, hw,
> period_size, 0)){ ERROR_HERE; goto open_error;}
> -
> -
> - ll->printf(1, "setting period size : %li\n",
> period_size);
> - if ( 0 > snd_pcm_hw_params_set_period_size_near(phandle,
> hw, &period_size, 0)){ ERROR_HERE; goto open_error;}
> -
> - ll->printf(1, "setting buffer size : %i * %li = %li\n",
> p_per_buff, period_size, p_per_buff * period_size);
> - snd_pcm_uframes_t buff_size = p_per_buff * period_size;
> - if ( 0 > snd_pcm_hw_params_set_buffer_size_near(phandle,
> hw, &buff_size)) { ERROR_HERE; goto open_error;}
> -
> - break;
> - }
> - }
> -
> - if (upsample_max == upsample) { ERROR_HERE; goto open_error; }
> -
> - if (0 > snd_pcm_hw_params(phandle, hw)) { ERROR_HERE; goto
> open_error; }
> -
> - snd_pcm_hw_params_free (hw);
> - return 0;
> -
> -open_error:
> - snd_pcm_hw_params_free (hw);
> - snd_pcm_close(phandle);
> - phandle = NULL;
> - return -1;
> - }
> - void close(int drain = 0)
> - {
> - if (!phandle)
> - {
> - ll->printf(0, "WARNING: attempt to close already-closed
> pcm\n");
> - return;
> - }
> - ll->printf(1, "INFO: closing phandle device\n");
> - if (drain) snd_pcm_drain(phandle);
> - snd_pcm_close(phandle);
> - phandle = NULL;
> - }
> - void prepare()
> - {
> - if (!phandle)
> - {
> - ll->printf(0, "ERROR: attempt to prepare a closed pcm\n");
> - return;
> - }
> - if (0 > snd_pcm_prepare(phandle)) { ERROR_HERE; }
> - }
> - int writebuf(snd_pcm_uframes_t frame_count, short int * frame_data)
> - {
> - if (!phandle)
> - {
> - ll->printf(0, "ERROR: attempt to write a closed phandle\n");
> - return -1;
> - }
> - int err = 0;
> - while (frame_count > 0) {
> - err = snd_pcm_writei (phandle, frame_data, frame_count );
> - if (err == (signed)frame_count) return 0; //success
> - if (err == -EAGAIN)
> - continue;
> - if (err < 0)
> - break;
> - frame_data += err * 4;
> - frame_count -= err;
> - }
> -
> - if (err >= 0)
> - {
> - ll->printf(0, "madness on line %s:%i\n", __FILE__, __LINE__);
> - return -1;
> - }
> -
> - const char * msg = NULL;
> - snd_pcm_state_t state = snd_pcm_state(phandle);
> - switch (state)
> - {
> - case SND_PCM_STATE_OPEN: msg = "open"; break;
> - case SND_PCM_STATE_SETUP: msg = "setup"; break;
> - case SND_PCM_STATE_PREPARED:msg = "prepared"; break;
> - case SND_PCM_STATE_RUNNING: msg = "running"; break;
> - case SND_PCM_STATE_XRUN: msg = "xrun"; break;
> - case SND_PCM_STATE_DRAINING: msg = "draining"; break;
> - case SND_PCM_STATE_PAUSED: msg = "paused"; break;
> - case SND_PCM_STATE_SUSPENDED: msg = "suspended"; break;
> - case SND_PCM_STATE_DISCONNECTED: msg = "disconnected"; break;
> - }
> - ll->printf(1, "WARNING: write failed (%s)\tstate =
> %s\ttime=%lf\n", snd_strerror (err), msg, pytime(NULL));
> - if (0 > snd_pcm_recover(phandle, err, 0)) { ERROR_HERE; return
> err;}
> - if (0 > snd_pcm_prepare(phandle)) { ERROR_HERE; return
> err;}
> - return 1; //warning
> - }
> -};
> -#undef ERROR_HERE
> -
> -#endif
> --
> 1.8.1.4
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.sugarlabs.org/archive/sugar-devel/attachments/20130502/3a733d06/attachment-0001.html>
More information about the Sugar-devel
mailing list