Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoramartin <olpc@localhost.localdomain>2007-03-20 19:07:20 (GMT)
committer amartin <olpc@localhost.localdomain>2007-03-20 19:07:20 (GMT)
commit5a688c51e8b59c7db4d9156c9b804a5fc8f01577 (patch)
tree5b26bce1932e843ffe2e848e26bf75a99bb00669
parent5f29f86ef43b756ccdd8c7001a7093ce54b38745 (diff)
parent631d817f5491db467f6184ec49fa34e235e82d8d (diff)
Merge branch 'master' of git+ssh://amartin@dev.laptop.org/git/projects/tamtam
-rw-r--r--Config.py3
-rw-r--r--Edit/MainWindow.py61
-rw-r--r--Edit/TrackInterface.py2
-rw-r--r--Generation/GenerationPitch.py7
-rw-r--r--Generation/GenerationRythm.py70
-rwxr-xr-xGeneration/Generator.py41
-rw-r--r--Resources/univorc.csd6
-rw-r--r--Util/CSoundClient.py3
-rw-r--r--Util/CSoundNote.py15
-rw-r--r--Util/Clooper/aclient.cpp2
-rwxr-xr-xUtil/Clooper/aclient.sobin0 -> 169404 bytes
-rw-r--r--Util/NoteDB.py63
-rw-r--r--mini.py231
-rw-r--r--miniTamTam/MiniSequencer.py30
-rw-r--r--miniTamTam/miniTamTamMain.py7
-rw-r--r--scripts/pull_aclient.sh2
16 files changed, 302 insertions, 241 deletions
diff --git a/Config.py b/Config.py
index 33cb6e1..3c76496 100644
--- a/Config.py
+++ b/Config.py
@@ -32,12 +32,13 @@ else:
#PLUGIN
PLUGIN_DEBUG = os.getenv('HOME')+"/tamtam.aclient.log"
+PLUGIN_VERBOSE = 4
PLUGIN_UNIVORC = TAM_TAM_ROOT + "/Resources/univorc.csd"
## PLUGIN ALSA PARAMETERS:
## for macbook pro
#PLUGIN_PERIOD = 1024
-#PLUGIN_NPERIODS = 3
+#PLUGIN_NPERIODS = 4
## for XO with root
#PLUGIN_PERIOD = 256
diff --git a/Edit/MainWindow.py b/Edit/MainWindow.py
index 1b9ffe7..e4e0ad1 100644
--- a/Edit/MainWindow.py
+++ b/Edit/MainWindow.py
@@ -7,6 +7,7 @@ import gobject
from Util.ThemeWidgets import *
from Util.Profiler import TP
from Util import NoteDB
+from Util import ControlStream
from Util.CSoundClient import new_csound_client
from Util.InstrumentPanel import InstrumentPanel
from Util.InstrumentPanel import DrumPanel
@@ -293,6 +294,7 @@ class MainWindow( SubActivity ):
# + + transport box
self.GUI["2transportBox"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
self.GUI["2recordButton"] = ImageButton( Config.IMAGE_ROOT+"recordGray.png", Config.IMAGE_ROOT+"recordGray.png", Config.IMAGE_ROOT+"recordGray.png", backgroundFill = Config.BG_COLOR )
+ self.GUI["2recordButton"].connect("clicked", self.handleSave )
self.GUI["2transportBox"].pack_start( self.GUI["2recordButton"] )
self.GUI["2playpauseBox"] = gtk.HBox()
self.GUI["2playpauseBox"].set_size_request( 90, -1 )
@@ -461,7 +463,7 @@ class MainWindow( SubActivity ):
for tid in range(Config.NUMBER_OF_TRACKS):
self.handleInstrumentChanged( ( tid, self.trackInstrument[tid] ) )
- first = self.noteDB.addPage( 4 )
+ first = self.noteDB.addPage( -1, NoteDB.Page(4) )
self.displayPage( first )
self.show_all() #gtk command
@@ -482,6 +484,8 @@ class MainWindow( SubActivity ):
def onActivate( self, arg ):
SubActivity.onActivate( self,arg )
# whatever needs to be done on initialization
+ self.csnd.loopPause()
+ self.csnd.loopClear()
for n in self.noteDB.getNotes( ):
self.csnd.loopPlay(n, 0) #adds all notes to c client in inactive state
@@ -489,6 +493,7 @@ class MainWindow( SubActivity ):
SubActivity.onDeactivate( self )
# clean up things like popups etc
self.releaseInstrumentPanel()
+ self.csnd.loopPause()
self.csnd.loopClear()
def setInstrumentPanel( self, instrumentPanel ):
@@ -1184,7 +1189,8 @@ class MainWindow( SubActivity ):
if after == -1: after = self.tuneInterface.getLastSelected()
if not beats: beats = self.noteDB.getPage( self.displayedPage ).beats
- self.displayPage( self.noteDB.addPage( beats, after ) )
+ # TODO think about network mode here...
+ self.displayPage( self.noteDB.addPage( -1, NoteDB.Page(beats), after ) )
def pageBeats( self, pageIds = -1 ):
@@ -1227,21 +1233,42 @@ class MainWindow( SubActivity ):
#-----------------------------------
# load and save functions
#-----------------------------------
- def handleSave(self, widget, data):
- pass
-
- chooser = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
-
- if chooser.run() == gtk.RESPONSE_OK:
- try:
- print 'INFO: serialize to file %s' % chooser.get_filename()
- f = open( chooser.get_filename(), 'w')
- pickle.dump( self._data, f )
- f.close()
- except IOError:
- print 'ERROR: failed to serialize to file %s' % chooser.get_filename()
-
- chooser.destroy()
+ def handleSave(self, widget):
+ try:
+ if (self.handleSaveCount == 1):
+ print 'DEBUG: clearing noteDB'
+ self.noteDB.deletePages( self.noteDB.pages.keys() )
+ # still leaves an empty page at start... grrr
+ print 'DEBUG: loading ofile.tam'
+ ifile = open('ofile.tam', 'r')
+ ttt = ControlStream.TamTamTable ( self.noteDB )
+ ttt.parseFile(ifile)
+ ifile.close()
+ self.handleSaveCount = 0
+ return
+ except AttributeError:
+ pass
+
+ print 'DEBUG: saving to ofile.tam'
+ ofile = open('ofile.tam', 'w')
+ ofilestream = ControlStream.TamTamOStream (ofile)
+ self.noteDB.dumpToStream(ofilestream)
+ ofile.close()
+ self.handleSaveCount = 1
+
+ if False:
+ chooser = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
+
+ if chooser.run() == gtk.RESPONSE_OK:
+ try:
+ print 'INFO: serialize to file %s' % chooser.get_filename()
+ f = open( chooser.get_filename(), 'w')
+ pickle.dump( self._data, f )
+ f.close()
+ except IOError:
+ print 'ERROR: failed to serialize to file %s' % chooser.get_filename()
+
+ chooser.destroy()
def handleLoad(self, widget, data):
chooser = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
diff --git a/Edit/TrackInterface.py b/Edit/TrackInterface.py
index fbd9e7d..5db645d 100644
--- a/Edit/TrackInterface.py
+++ b/Edit/TrackInterface.py
@@ -371,7 +371,7 @@ class TrackInterface( gtk.EventBox ):
i,
instrumentId = self.owner.getTrackInstrument(i).instrumentId )
cs.pageId = self.curPage
- id = self.noteDB.addNote( self.curPage, i, cs )
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
n = self.noteDB.getNote( self.curPage, i, id, self )
self.selectNotes( { i:[n] }, True )
n.playSampleNote( False )
diff --git a/Generation/GenerationPitch.py b/Generation/GenerationPitch.py
index 7bfe2ff..99fceaa 100644
--- a/Generation/GenerationPitch.py
+++ b/Generation/GenerationPitch.py
@@ -34,8 +34,10 @@ class GenerationPitch:
numberOfPitch = int( ( 1 - (parameters.pitchRegularity*.8) ) * 10 + 1 )
step = -(8 - (int(parameters.step * 8)))
max = len(table_pitch)-1
+ nextValue = self.pitchMethod.getNextValue
+ tonique = GenerationConstants.DEFAULT_TONIQUE
for i in range(numberOfPitch):
- append((table_pitch[self.pitchMethod.getNextValue(step, max)]) + GenerationConstants.DEFAULT_TONIQUE)
+ append((table_pitch[nextValue(step, max)]) + tonique)
restOfNotes = range( length - numberOfPitch )
for i in restOfNotes:
position = i % numberOfPitch
@@ -46,8 +48,9 @@ class GenerationPitch:
pitchSequence = []
append = pitchSequence.append
max = len(drumPitch) - 1
+ rand = random.randint
for i in range(length):
- append(drumPitch[ random.randint( 0, max ) ] )
+ append(drumPitch[ rand( 0, max ) ] )
return pitchSequence
# def harmonicPitchSequence( self, rythmSequence, parameters, table_pitch, harmonicSequence ):
diff --git a/Generation/GenerationRythm.py b/Generation/GenerationRythm.py
index 06dc1ab..56e9199 100644
--- a/Generation/GenerationRythm.py
+++ b/Generation/GenerationRythm.py
@@ -95,70 +95,76 @@ class GenerationRythm:
countDown = 0
onsetTime = None
beatsPerPage = int( barLength / Config.TICKS_PER_BEAT )
+ randInt = random.randint
+
+ upBeatsAppend = upBeats.append
if Config.INSTRUMENTS[ trackInstrument ].instrumentRegister == Config.PUNCH:
registerDensity = 0.5
downBeatRecurence = 4
+ upBeatOffset = Config.TICKS_PER_BEAT / 2
downBeats = [x for x in GenerationConstants.DRUM_PUNCH_ACCENTS[ beatsPerPage ]]
for downBeat in downBeats:
- upBeats.append( downBeat + Config.TICKS_PER_BEAT / 2 )
- #upBeats.append( ( downBeat[ 0 ] + Config.TICKS_PER_BEAT , downBeat[ 1 ] ) )
+ upBeatsAppend( downBeat + upBeatOffset )
- if Config.INSTRUMENTS[ trackInstrument ].instrumentRegister == Config.LOW:
- registerDensity =1.5
+ elif Config.INSTRUMENTS[ trackInstrument ].instrumentRegister == Config.LOW:
+ registerDensity = 1.5
downBeatRecurence = 4
+ upBeatOffset = Config.TICKS_PER_BEAT / 2
downBeats = [x for x in GenerationConstants.DRUM_LOW_ACCENTS[ beatsPerPage ]]
for downBeat in downBeats:
- upBeats.append( downBeat + Config.TICKS_PER_BEAT / 2 )
- #upBeats.append( ( downBeat[ 0 ] + Config.TICKS_PER_BEAT / 2 , downBeat[ 1 ] ) )
+ upBeatsAppend( downBeat + upBeatOffset )
- if Config.INSTRUMENTS[ trackInstrument ].instrumentRegister == Config.MID:
+ elif Config.INSTRUMENTS[ trackInstrument ].instrumentRegister == Config.MID:
registerDensity = 1
downBeatRecurence = 1
+ upBeatOffset = Config.TICKS_PER_BEAT / 4
downBeats = [x for x in GenerationConstants.DRUM_MID_ACCENTS[ beatsPerPage ]]
for downBeat in downBeats:
- upBeats.append( downBeat + Config.TICKS_PER_BEAT / 4 )
- #upBeats.append( ( downBeat[ 0 ] + Config.TICKS_PER_BEAT / 4 , downBeat[ 1 ] ) )
+ upBeatsAppend( downBeat + upBeatOffset )
- if Config.INSTRUMENTS[ trackInstrument ].instrumentRegister == Config.HIGH:
+ elif Config.INSTRUMENTS[ trackInstrument ].instrumentRegister == Config.HIGH:
registerDensity = 1.5
downBeatRecurence = 1
+ upBeatOffset = Config.TICKS_PER_BEAT / 4
downBeats = [x for x in GenerationConstants.DRUM_HIGH_ACCENTS[ beatsPerPage ]]
for downBeat in downBeats:
- upBeats.append( downBeat + Config.TICKS_PER_BEAT / 4 )
- #upBeats.append( ( downBeat[ 0 ] + Config.TICKS_PER_BEAT / 4 , downBeat[ 1 ] ) )
-
- for i in range( int( density * registerDensity * len( downBeats ) ) ):
- if random.random() < ( parameters.rythmRegularity * downBeatRecurence ) and binSelection.count( 1 ) < len( downBeats ):
- binSelection.append( 1 )
+ upBeatsAppend( downBeat + upBeatOffset )
+
+ list = range( int( density * registerDensity * len( downBeats ) ) )
+ rand = random.random
+ binCount = binSelection.count
+ binAppend = binSelection.append
+ for i in list:
+ if rand() < ( parameters.rythmRegularity * downBeatRecurence ) and binCount( 1 ) < len( downBeats ):
+ binAppend( 1 )
else:
- if binSelection.count( 0 ) < len( downBeats ):
- binSelection.append( 0 )
+ if binCount( 0 ) < len( downBeats ):
+ binAppend( 0 )
else:
- binSelection.append( 1 )
+ binAppend( 1 )
- countDown = binSelection.count( 1 )
+ countDown = binCount( 1 )
+ seqAppend = rythmSequence.append
length = len(downBeats) - 1
+ downPop = downBeats.pop
for i in range( countDown ):
-# while onsetTime in rythmSequence or onsetTime == None:
-# onsetTime = Utils.prob2( downBeats )
- ran1 = random.randint(0, length)
- ran2 = random.randint(0, length)
+ ran1 = randInt(0, length)
+ ran2 = randInt(0, length)
randMin = min(ran1, ran2)
- onsetTime = downBeats.pop(randMin)
- rythmSequence.append( onsetTime )
+ onsetTime = downPop(randMin)
+ seqAppend( onsetTime )
length -= 1
length = len(upBeats) - 1
+ upPop = upBeats.pop
for i in range( len( binSelection ) - countDown ):
-# while onsetTime in rythmSequence or onsetTime == None:
-# onsetTime = Utils.prob2( upBeats )
- ran1 = random.randint(0, length)
- ran2 = random.randint(0, length)
+ ran1 = randInt(0, length)
+ ran2 = randInt(0, length)
randMin = min(ran1, ran2)
- onsetTime = upBeats.pop(randMin)
- rythmSequence.append( onsetTime )
+ onsetTime = upPop(randMin)
+ seqAppend( onsetTime )
length -= 1
rythmSequence.sort()
diff --git a/Generation/Generator.py b/Generation/Generator.py
index 22c23de..066aab7 100755
--- a/Generation/Generator.py
+++ b/Generation/Generator.py
@@ -52,16 +52,19 @@ def generator1(
def makeGainSequence( onsetList ):
gainSequence = []
+ append = gainSequence.append
+ rand = random.uniform
max = GenerationConstants.GAIN_MAX_BOUNDARY
midMax = GenerationConstants.GAIN_MID_MAX_BOUNDARY
+ midMin = GenerationConstants.GAIN_MID_MIN_BOUNDARY
+ min = GenerationConstants.GAIN_MIN_BOUNDARY
for onset in onsetList:
if onset == 0:
- gain = random.uniform(GenerationConstants.GAIN_MID_MAX_BOUNDARY, GenerationConstants.GAIN_MAX_BOUNDARY)
+ append(rand(midMax, max))
elif ( onset % Config.TICKS_PER_BEAT) == 0:
- gain = random.uniform(GenerationConstants.GAIN_MID_MIN_BOUNDARY, GenerationConstants.GAIN_MID_MAX_BOUNDARY)
+ append(rand(midMin, midMax))
else:
- gain = random.uniform(GenerationConstants.GAIN_MIN_BOUNDARY, GenerationConstants.GAIN_MID_MIN_BOUNDARY)
- gainSequence.append(gain)
+ append(rand(min, midMin))
return gainSequence
def makeDurationSequence( onsetList, parameters, table_duration, barLength, currentInstrument ):
@@ -71,13 +74,14 @@ def generator1(
durationSequence = [duration] * len(onsetList)
return durationSequence
+ append = durationSequence.append
+ proba = Utils.prob2
if len( onsetList ) > 1:
for i in range(len(onsetList) - 1):
- duration = (onsetList[i+1] - onsetList[i]) * Utils.prob2( table_duration )
- durationSequence.append(duration)
- durationSequence.append(( barLength - onsetList[-1]) * Utils.prob2( table_duration ))
+ append((onsetList[i+1] - onsetList[i]) * proba( table_duration ))
+ append(( barLength - onsetList[-1]) * proba( table_duration ))
elif len( onsetList ) == 1:
- durationSequence.append( ( barLength - onsetList[0] ) * Utils.prob2( table_duration ))
+ append( ( barLength - onsetList[0] ) * proba( table_duration ))
return durationSequence
def pageGenerate( parameters, trackId, pageId, trackOfNotes, drumPitch = None ):
@@ -98,17 +102,21 @@ def generator1(
durationSequence = makeDurationSequence(rythmSequence, parameters, table_duration, barLength, currentInstrument)
numOfNotes = range(len(rythmSequence))
+ rand = random.random
+ append = trackNotes.append
+ pan = GenerationConstants.DEFAULT_PAN
+ instrument_id = Config.INSTRUMENTS[instrument[trackId]].instrumentId
for i in numOfNotes:
if drumPitch:
- if ( random.random() * fillDrum ) > ( parameters.silence * .7 ):
+ if ( rand() * fillDrum ) > ( parameters.silence * .5 ):
if fillDrum != 1:
if rythmSequence[i] not in trackOnsets or pitchSequence[i] not in trackPitchs:
- trackNotes.append( CSoundNote( rythmSequence[i], pitchSequence[i], gainSequence[i], GenerationConstants.DEFAULT_PAN, durationSequence[i], trackId, Config.INSTRUMENTS[instrument[ trackId ]].instrumentId, 0.002, 0.098, 0.1, 0, 1000, False, 'edit' ) )
+ append( CSoundNote( rythmSequence[i], pitchSequence[i], gainSequence[i], pan, durationSequence[i], trackId, instrument_id, 0.002, 0.098, 0.1, 0, 1000, False, 'edit' ) )
else:
- trackNotes.append( CSoundNote( rythmSequence[i], pitchSequence[i], gainSequence[i], GenerationConstants.DEFAULT_PAN, durationSequence[i], trackId, Config.INSTRUMENTS[instrument[ trackId ]].instrumentId, 0.002, 0.098, 0.1, 0, 1000, False, 'edit' ) )
+ append( CSoundNote( rythmSequence[i], pitchSequence[i], gainSequence[i], pan, durationSequence[i], trackId, instrument_id, 0.002, 0.098, 0.1, 0, 1000, False, 'edit' ) )
else:
- if random.random() > parameters.silence:
- trackNotes.append( CSoundNote( rythmSequence[i], pitchSequence[i], gainSequence[i], GenerationConstants.DEFAULT_PAN, durationSequence[i], trackId, Config.INSTRUMENTS[instrument[ trackId ]].instrumentId, 0.002, 0.098, 0.1, 0, 1000, False, 'edit' ) )
+ if rand() > parameters.silence:
+ append( CSoundNote( rythmSequence[i], pitchSequence[i], gainSequence[i], pan, durationSequence[i], trackId, instrument_id, 0.002, 0.1, 0.1, 0, 1000, False, 'edit' ) )
trackDictionary[ trackId ][ pageId ] = trackNotes
@@ -137,10 +145,11 @@ def generator1(
for pageId in pageIds:
barLength = Config.TICKS_PER_BEAT * nbeats[ pageId ]
trackOfNotes = []
+ pageCycle = selectedPageCount % 4 # this should be fix in the meta algo
if instrument[ trackId ][0:4] == 'drum':
- if ( selectedPageCount % 4 ) in [1,2]:
+ if pageCycle in [1,2]:
trackDictionary[ trackId ][ pageId ] = [ n for n in trackDictionary[ trackId ][ lastPageId ] ]
- elif ( selectedPageCount % 4 ) == 3:
+ elif pageCycle == 3:
trackOfNotes = [ n for n in trackDictionary[ trackId ][ lastPageId ] ]
trackOnsets = [n.onset for n in trackOfNotes]
trackPitchs = [n.pitch for n in trackOfNotes]
@@ -150,7 +159,7 @@ def generator1(
for drumPitch in GenerationConstants.DRUM_COMPLEXITY4:
pageGenerate( parameters, trackId, pageId, trackOfNotes, drumPitch )
parameters.rythmRegularity = rythmRegTemp
- elif ( selectedPageCount % 4 ) == 0:
+ elif pageCycle == 0:
fillDrum = 1
for drumPitch in streamOfPitch:
pageGenerate( parameters, trackId, pageId, trackOfNotes, drumPitch )
diff --git a/Resources/univorc.csd b/Resources/univorc.csd
index 501dba7..79fdf21 100644
--- a/Resources/univorc.csd
+++ b/Resources/univorc.csd
@@ -708,7 +708,7 @@ kvibrato oscil .006, ivibRand, 1
tigoto tieskip
-kpitch portk p4, igliss, p4
+kpitch portk p4, igliss, p4
kpan portk p7, igliss, p7
krg portk p5, igliss, p5
kcutoff portk p12, igliss, p12
@@ -763,8 +763,8 @@ a1 bqrez a1, p12, 6, p11-1
a1 balance a1, acomp
endif
-kenv adsr p9, 0.05, p6, p10
-a1 = a1*kenv*kvol
+aenv adsr p9*p3, 0.005, p6, p10*p3
+a1 = a1*aenv*kvol
gaoutL = a1*(1-p7)+gaoutL
gaoutR = a1*p7+gaoutR
diff --git a/Util/CSoundClient.py b/Util/CSoundClient.py
index 12bd698..7fd7a41 100644
--- a/Util/CSoundClient.py
+++ b/Util/CSoundClient.py
@@ -15,7 +15,8 @@ from Util import NoteDB
class _CSoundClientPlugin:
def __init__(self):
sc_initialize( Config.PLUGIN_UNIVORC, Config.PLUGIN_DEBUG,
- Config.PLUGIN_PERIOD, Config.PLUGIN_NPERIODS)
+ Config.PLUGIN_PERIOD, Config.PLUGIN_NPERIODS,
+ Config.PLUGIN_VERBOSE)
self.on = False
#self.masterVolume = 80.0
self.periods_per_buffer = 2
diff --git a/Util/CSoundNote.py b/Util/CSoundNote.py
index 77e85d7..d41fc11 100644
--- a/Util/CSoundNote.py
+++ b/Util/CSoundNote.py
@@ -2,10 +2,6 @@ import Config
from Generation.GenerationConstants import GenerationConstants
class CSoundNote :
- NOTE_ID_COUNTER = 0
- #-----------------------------------
- # initialization
- #-----------------------------------
def __init__( self,
onset,
pitch,
@@ -38,11 +34,8 @@ class CSoundNote :
self.filterCutoff = filterCutoff
self.tied = tied
self.mode = mode
- self.nchanges = 0
- self.noteId = self.NOTE_ID_COUNTER
- self.NOTE_ID_COUNTER += 1
- def __getstate__(self):
+ def __getstate__unused(self):
return {'onset': self.onset,
'pitch': self.pitch,
'amplitude': self.amplitude,
@@ -58,7 +51,7 @@ class CSoundNote :
'tied': self.tied,
'mode': self.mode }
- def __setstate__(self,dict):
+ def __setstate__unused(self,dict):
self.onset = dict['onset']
self.pitch = dict['pitch']
self.amplitude = dict['amplitude']
@@ -78,7 +71,9 @@ class CSoundNote :
def clone( self ):
return CSoundNote( self.onset, self.pitch, self.amplitude, self.pan,
self.duration, self.trackId, self.instrumentId,
- self.attack, self.decay, self.reverbSend, self.filterType, self.filterCutoff, self.tied, self.mode )
+ self.attack, self.decay, self.reverbSend,
+ self.filterType, self.filterCutoff, self.tied,
+ self.mode )
diff --git a/Util/Clooper/aclient.cpp b/Util/Clooper/aclient.cpp
index c9e298b..0d73c7e 100644
--- a/Util/Clooper/aclient.cpp
+++ b/Util/Clooper/aclient.cpp
@@ -849,7 +849,7 @@ DECL(sc_initialize) //(char * csd)
char * str;
char * log_file;
int period, ppb;
- if (!PyArg_ParseTuple(args, "ssii", &str, &log_file, &period, &ppb ))
+ if (!PyArg_ParseTuple(args, "ssiii", &str, &log_file, &period, &ppb, &VERBOSE ))
{
return NULL;
}
diff --git a/Util/Clooper/aclient.so b/Util/Clooper/aclient.so
new file mode 100755
index 0000000..2df6327
--- /dev/null
+++ b/Util/Clooper/aclient.so
Binary files differ
diff --git a/Util/NoteDB.py b/Util/NoteDB.py
index a3b7bc3..6679446 100644
--- a/Util/NoteDB.py
+++ b/Util/NoteDB.py
@@ -82,6 +82,15 @@ class NoteDB:
self.clipboard = [] # stores copied cs notes
self.clipboardArea = [] # stores the limits and tracks for each page in the clipboard
+ def dumpToStream(self, ostream):
+ for pid in self.pages:
+ ostream.page_add(pid, self.pages[pid])
+ for pid in self.noteD:
+ for tid in xrange( len( self.noteD[pid])):
+ for nid in self.noteD[pid][tid]:
+ ostream.note_add(self.noteD[pid][tid][nid])
+ ostream.tune_set(self.tune)
+
#-- private --------------------------------------------
def _genId( self ):
self.nextId += 1
@@ -93,14 +102,14 @@ class NoteDB:
#=======================================================
# Page Functions
- def addPage( self, beats, after = False ):
- id = self._newPage( beats )
- at = self._insertPage( id, after )
+ def addPage( self, pid, page, after = False ):
+ pid = self._newPage( pid, page )
+ at = self._insertPage( pid, after )
for l in self.pageListeners:
- l.notifyPageAdd( id, at )
+ l.notifyPageAdd( pid, at )
- return id
+ return pid
def deletePages( self, which ):
beats = self.pages[self.tune[0]].beats
@@ -125,7 +134,7 @@ class NoteDB:
self.tune.pop(at)
if not len(self.tune):
- self.addPage( beats ) # always have at least one page
+ self.addPage( -1, Page(beats) ) # always have at least one page
safe = self.tune[0]
else:
safe = self.tune[max(ind-1,0)]
@@ -147,7 +156,7 @@ class NoteDB:
new = {}
for cp in sorted:
- id = self._newPage( self.pages[cp].beats )
+ id = self._newPage( -1, Page(self.pages[cp].beats) )
self._insertPage( id, after )
after = id
new[cp] = id
@@ -186,23 +195,23 @@ class NoteDB:
l.notifyPageMove( sorted, low, high )
#-- private --------------------------------------------
- def _newPage( self, beats ):
- id = self._genId()
- self.pages[id] = Page( beats )
- self.noteD[id] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ]
- self.noteS[id] = [ [] for i in range(Config.NUMBER_OF_TRACKS) ]
- self.parasiteD[id] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ]
- self.parasiteS[id] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ]
+ def _newPage( self, pid, page ):
+ if pid == -1 : pid = self._genId()
+ self.pages[pid] = page
+ self.noteD[pid] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ]
+ self.noteS[pid] = [ [] for i in range(Config.NUMBER_OF_TRACKS) ]
+ self.parasiteD[pid] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ]
+ self.parasiteS[pid] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ]
for i in range(Config.NUMBER_OF_TRACKS):
for par in self.parasiteList.keys():
- self.parasiteD[id][i][par] = {}
- self.parasiteS[id][i][par] = []
- return id
+ self.parasiteD[pid][i][par] = {}
+ self.parasiteS[pid][i][par] = []
+ return pid
- def _insertPage( self, id, after ):
+ def _insertPage( self, pid, after ):
if not after: at = 0
else: at = self.tune.index(after)+1
- self.tune.insert( at, id )
+ self.tune.insert( at, pid )
return at
@@ -218,9 +227,9 @@ class NoteDB:
#=======================================================
# Note Functions
- def addNote( self, page, track, cs, hint = False ):
- id = self.pages[page].genId()
- n = self.noteD[page][track][id] = Note( page, track, id, cs )
+ def addNote( self, nid, page, track, cs, hint = False ):
+ if nid == -1: nid = self.pages[page].genId()
+ n = self.noteD[page][track][nid] = Note( page, track, nid, cs )
if not hint: at = 0
else: at = hint[0]
@@ -242,15 +251,15 @@ class NoteDB:
for par in self.parasiteList.keys():
parasite = self.parasiteList[par]( self, par, n )
- self.parasiteD[page][track][par][id] = parasite.attach() # give parasites the option of return something other than themselves
+ self.parasiteD[page][track][par][nid] = parasite.attach() # give parasites the option of return something other than themselves
self.parasiteS[page][track][par].insert( at, parasite.attach() )
if hint: hint[0] = at + 1 # assume the next note will fall after this one
for l in self.noteListeners:
- l.notifyNoteAdd( page, track, id )
+ l.notifyNoteAdd( page, track, nid )
- return id
+ return nid
# stream format:
# page id
@@ -270,7 +279,7 @@ class NoteDB:
N = self._readstream(stream,i)
hint = [0]
for j in range(N):
- new[p][t].append( self.addNote( p, t, self._readstream(stream,i), hint ) )
+ new[p][t].append( self.addNote( -1, p, t, self._readstream(stream,i), hint ) )
p = self._readstream(stream,i)
return new
@@ -323,7 +332,7 @@ class NoteDB:
if cs.onset + cs.duration > ticks:
cs.duration = ticks - cs.onset
- return self.addNote( toPage, toTrack, cs )
+ return self.addNote( -1, toPage, toTrack, cs )
# stream format:
# page id
diff --git a/mini.py b/mini.py
index 5573502..ae18a01 100644
--- a/mini.py
+++ b/mini.py
@@ -1,123 +1,116 @@
-from Framework.CSound.CSoundClient import CSoundClient
-from Framework.NoteLooper import NoteLooper as NL
-
-import time
-import math
-
-lookahead = 0.2
-nl = NL( lookahead, 20.0, ['drum1kit'], [1.0], [1.0] )
-
-nl.notes = [
- #(0, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'drum1kick', 'filterType': 0, 'tied': False, 'onset': 0, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.92029484351363555, 'pitch': 24, 'duration': 1, 'noteID': 36, 'pan': 0.5}, ''),
- #( 0, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'flute', 'filterType': 0, 'tied': False, 'onset': 0, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.92525578482634807, 'pitch': 29, 'duration': 4, 'noteID': 1, 'pan': 0.5}, ''),
- #(0, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'koto', 'filterType': 0, 'tied': False, 'onset': 0, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.939434987358156, 'pitch': 38, 'duration': 5, 'noteID': 8, 'pan': 0.5}, ''),
- (0, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'gam', 'filterType': 0, 'tied': False, 'onset': 0, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.94494314329224738, 'pitch': 24, 'duration': 5, 'noteID': 22, 'pan': 0.5}, ''),
- #(0, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'guit', 'filterType': 0, 'tied': False, 'onset': 0, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.94691792606223446, 'pitch': 35, 'duration': 5, 'noteID': 29, 'pan': 0.5}, ''),
- #(6, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'gam', 'filterType': 0, 'tied': False, 'onset': 6, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.65465607337722664, 'pitch': 26, 'duration': 7, 'noteID': 23, 'pan': 0.5}, ''),
- #(6, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'gam', 'filterType': 0, 'tied': False, 'onset': 6, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.66382312969855872, 'pitch': 28, 'duration': 5, 'noteID': 16, 'pan': 0.5}, ''),
- #(6, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'koto', 'filterType': 0, 'tied': False, 'onset': 6, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.69112283960585841, 'pitch': 33, 'duration': 5, 'noteID': 9, 'pan': 0.5}, ''),
- #(6, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'drum1hatshoulder', 'filterType': 0, 'tied': False, 'onset': 6, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.70739520974639736, 'pitch': 46, 'duration': 1, 'noteID': 46, 'pan': 0.5}, ''),
- #(6, {'fullDuration': False, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'flute', 'filterType': 0, 'tied': False, 'onset': 6, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.72961742679087493, 'pitch': 31, 'duration': 5, 'noteID': 2, 'pan': 0.5}, '')
- (0, {'fullDuration': True, 'pageID': 0, 'decay': 0.098000000000000004, 'trackID': 0, 'instrumentFlag': 'gam', 'filterType': 0, 'tied': False, 'onset': 0, 'filterCutoff': 1000, 'attack': 0.002, 'reverbSend': 0.10000000000000001, 'overlap': False, 'amplitude': 0.98273554224748583, 'pitch': 33, 'duration': 6, 'noteID': 15, 'pan': 0.5}, '')
- ]
-
-
-cmdEvent = [
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5019 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5020 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5021 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5028 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5017 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5011 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5012 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5013 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5777 0.0 0.001 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5012 0.002 0.004 0 1000.000000')"
- ]
-cmdStraight = [
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5019 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5020 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5021 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5028 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5017 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5011 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5012 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5013 0.002 0.004 0 1000.000000')",
- "perf.InputMessage('i 5003.5 %f 0.050000 1.000000 0.100000 0.773 0.500000 5012 0.002 0.004 0 1000.000000')"
- ]
-
-CSoundClient.initialize()
-CSoundClient.setMasterVolume(100.0)
-
-
-
-i = 0
-t1 = time.time()
-m = v = vv = 0.0
-
-loopsleep = 0.05
-loopdelay = 0.1
-
-
-time0 = time.time()
-nl.setDuration(12)
-nl.setTick(0)
-CSoundClient.startTime()
-
-while True :
-
- t0 = t1
- i = i + 1
- now = time.time()
- if True:
- next = nl.next()
- for n in next:
- CSoundClient.sendText(n)
-
- elif True:
- j = i % 4
- if j == 0:
- CSoundClient.sendText( cmdEvent[1] % ( (now - time0) + loopdelay, ))
- CSoundClient.sendText( cmdEvent[3] % ( (now - time0) + loopdelay, ) )
- pass
- elif j == 1:
- CSoundClient.sendText( cmdEvent[4] % ( (now - time0) + loopdelay, ) )
- CSoundClient.sendText( cmdEvent[5] % ( (now - time0) + loopdelay, ) )
- pass
- elif j == 2:
- CSoundClient.sendText( cmdEvent[2] % ( (now - time0) + loopdelay, ) )
- CSoundClient.sendText( cmdEvent[5] % ( (now - time0) + loopdelay, ) )
- pass
- else:
- CSoundClient.sendText( cmdEvent[1] % ( (now - time0) + loopdelay, ))
- CSoundClient.sendText( cmdEvent[4] % ( (now - time0) + loopdelay, ) )
- elif True:
- j = i % 4
- if j == 0:
- CSoundClient.sendText( cmdStraight[1] % (loopdelay, ))
- CSoundClient.sendText( cmdStraight[3] % (loopdelay, ) )
- pass
- elif j == 1:
- CSoundClient.sendText( cmdStraight[4] % (loopdelay, ) )
- CSoundClient.sendText( cmdStraight[5] % (loopdelay, ) )
- pass
- elif j == 2:
- CSoundClient.sendText( cmdStraight[2] % (loopdelay, ) )
- CSoundClient.sendText( cmdStraight[5] % (loopdelay, ) )
- pass
- else:
- CSoundClient.sendText( cmdStraight[1] % (loopdelay, ))
- CSoundClient.sendText( cmdStraight[4] % (loopdelay, ) )
- time.sleep(loopsleep)
- t1 = time.time()
+from Util.CSoundClient import new_csound_client
+from Util.NoteDB import PARAMETER, NoteDB
+from Util.ControlStream import *
+
+
+class MiniPlayer:
+ def __init__(self, ndb):
+ self.csnd = new_csound_client()
+ self.csnd.connect(True)
+ self.csnd.loopSetNumTicks(50)
+ self.csnd.loopStart()
+ self.csnd.setMasterVolume(1.0)
+ self.csnd.setTrackVolume(1.0, 1)
- r = 1.0 / i
- d = t1 - t0
- m = r * d + (1.0 - r) * m
- v = r * d * d + (1.0 - r) * v
- vv = r * d * d * d + (1.0 - r) * v
+ self.noteDB = ndb
+ ndb.addListener( self, page=True, note=True )
- #print m, math.sqrt(v - m * m ), math.pow( vv - m*m*m, 0.333333)
- break
-CSoundClient.initialize(False)
+ def recompose( self, algo, params, genOrVar):
+
+ newtracks = set(range(Config.NUMBER_OF_TRACKS))
+ newpages = self.tuneInterface.getSelectedIds()
+
+ if genOrVar == 0:
+ dict = {}
+ for t in newtracks:
+ dict[t] = {}
+ for p in newpages:
+ dict[t][p] = self.noteDB.getCSNotesByTrack( p, t )
+ else:
+ dict = {}
+ for t in newtracks:
+ dict[t] = {}
+ dict[t][1] = self.noteDB.getCSNotesByTrack( 1, t )
+
+ beatsOfPages = {}
+ for pageId in newpages:
+ beatsOfPages[pageId] = self.noteDB.pages[pageId].beats
+
+ algo(
+ params,
+ self._data['track_volume'][:],
+ [ i.name for i in self.trackInstrument ],
+ self._data['tempo'],
+ beatsOfPages,
+ newtracks,
+ newpages,
+ dict)
+
+ # filter & fix input ...WTF!?
+ for track in dict:
+ for page in dict[track]:
+ for note in dict[track][page]:
+ intdur = int(note.duration)
+ note.duration = intdur
+ note.pageId = page
+ note.trackId = track
+
+ # prepare the new notes
+ newnotes = []
+ for tid in dict:
+ for pid in dict[tid]:
+ newnotes += dict[tid][pid]
+
+ # delete the notes and add the new
+ self.noteDB.deleteNotesByTrack( newpages, newtracks )
+
+ stream = []
+ for page in newpages:
+ for track in newtracks:
+ stream += [ page, track, len(dict[track][page]) ]
+ stream += dict[track][page]
+ stream += [-1]
+ self.noteDB.addNotes( stream )
+ def notifyPageAdd( self, id, at ):
+ return
+
+ def notifyPageDelete( self, which, safe ):
+ pass
+
+ def notifyPageDuplicate( self, new, at ):
+ return
+
+ def notifyPageMove( self, which, low, high ):
+ return
+
+ def notifyNoteAdd( self, page, track, id ):
+ print 'INFO: adding note to loop', page, track, id
+ n = self.noteDB.getNote(page, track, id)
+ self.csnd.loopPlay(n,0)
+ onset = n.cs.onset + 0 #self.page_onset[n.page]
+ self.csnd.loopUpdate(n, PARAMETER.ONSET, onset, 1) #set onset + activate
+
+ def notifyNoteDelete( self, page, track, id ):
+ print 'INFO: deleting note from loop', page, track, id
+ self.csnd.loopDelete1(page,id)
+ def notifyNoteUpdate( self, page, track, id, parameter, value ):
+ print 'INFO: updating note ', page, id, parameter, value
+ note = self.noteDB.getNote(page, track, id)
+ self.csnd.loopUpdate(note, parameter, value, -1)
+
+if __name__ == '__main__':
+ ndb = NoteDB()
+ mplayer = MiniPlayer(ndb)
+
+ ttt = TamTamTable(ndb)
+ table = ttt.parseTable()
+ while True:
+ l = sys.stdin.readline()
+ if l == '\n': break
+ cmdlist = l.split()
+ if cmdlist[0] not in table:
+ print 'ERROR: command %s not defined by parse table' % cmdlist[0]
+ else:
+ table[cmdlist[0]](cmdlist[1:])
diff --git a/miniTamTam/MiniSequencer.py b/miniTamTam/MiniSequencer.py
index 8a99606..45ed16b 100644
--- a/miniTamTam/MiniSequencer.py
+++ b/miniTamTam/MiniSequencer.py
@@ -15,11 +15,13 @@ class MiniSequencer:
self.pitchs = []
self.beat = 4
self.tempo = Config.PLAYER_TEMPO
- self.tick = 15
+ self.checkOk = 0
+ self.tick = 0
self.id = 1000
self.csnd = new_csound_client()
self.startLooking = 0
self.recordState = 0
+ self.startPoint = 0
self.recordButtonState = recordButtonState
self.playbackTimeout = None
self.playState = 0
@@ -61,6 +63,13 @@ class MiniSequencer:
self.playState = 0
def recording( self, note ):
+ if self.startLooking:
+ self.sequencer = []
+ self.pitchs = []
+ self.recordState = 1
+ self.startLooking = 0
+ self.recordButtonState(True)
+ self.startPoint = self.csnd.loopGetTick()
if self.recordState:
self.pitchs.append( note.pitch )
self.sequencer.append( note )
@@ -91,7 +100,8 @@ class MiniSequencer:
self.pitchs.remove( pitch )
def handleClock( self ):
- t = self.csnd.loopGetTick() / 3
+ currentTick = self.csnd.loopGetTick()
+ t = currentTick / 3
if self.tick != t:
self.tick = t
if self.startLooking:
@@ -99,16 +109,14 @@ class MiniSequencer:
self.recordButtonState(True)
if self.tick in self.upBeats:
self.recordButtonState(False)
- if self.tick == 0:
- self.sequencer = []
- self.pitchs = []
- self.recordState = 1
- self.startLooking = 0
- if self.tick >= (4 * self.beat - 1):
- if self.recordState:
- self.recordState = 0
- self.recordButtonState(False)
+ if self.recordState:
+ if currentTick < self.startPoint:
+ self.checkOk = 1
+ if currentTick >= self.startPoint and self.checkOk:
+ self.checkOk = 0
+ self.recordState = 0
+ self.recordButtonState(False)
return True
diff --git a/miniTamTam/miniTamTamMain.py b/miniTamTam/miniTamTamMain.py
index 82dce2a..b8bf123 100644
--- a/miniTamTam/miniTamTamMain.py
+++ b/miniTamTam/miniTamTamMain.py
@@ -406,12 +406,19 @@ class miniTamTamMain(SubActivity):
cleanInstrumentList.sort(lambda g,l: cmp(Config.INSTRUMENTS[g].category, Config.INSTRUMENTS[l].category) )
return cleanInstrumentList + ['drum1kit', 'drum2kit', 'drum3kit']
+ def onActivate( self ):
+ self.csnd.loopPause()
+ self.csnd.loopClear()
+
def onDeactivate( self ):
SubActivity.onDeactivate( self )
self.releaseInstrumentPanel()
+ self.csnd.loopPause()
+ self.csnd.loopClear()
def onDestroy( self ):
#this gets called when the whole app is being destroyed
+ #QUESTION is this called before or after onDeactivate()
pass
def scale(self, input,input_min,input_max,output_min,output_max):
diff --git a/scripts/pull_aclient.sh b/scripts/pull_aclient.sh
new file mode 100644
index 0000000..c6edac1
--- /dev/null
+++ b/scripts/pull_aclient.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+wget http://www-etud.iro.umontreal.ca/~bergstrj/tamtam/aclient.so -o Util/Clooper/aclient.so