[Sugar-devel] [PATCH TamTam 1/2 v2] Output to ALSA directly from csound

Daniel Drake dsd at laptop.org
Wed May 1 11:25:48 EDT 2013


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



More information about the Sugar-devel mailing list