diff options
author | jaberg <james@localhost.localdomain> | 2007-01-08 11:18:20 (GMT) |
---|---|---|
committer | jaberg <james@localhost.localdomain> | 2007-01-08 11:18:20 (GMT) |
commit | 7478ebeda0b5cc2a101a3808e64323684ae8ef91 (patch) | |
tree | 1e6613a2c76ea2896622311a71138669ecaf8cd6 | |
parent | c2cd65e519cd77b4d8575ca130213c9cbdc439a6 (diff) |
new and improved notelooper
-rw-r--r-- | Generation/GenerationRythm.py | 2 | ||||
-rw-r--r-- | Generation/VariationRythm.py | 2 | ||||
-rw-r--r-- | Player/KeyboardStandAlone.py | 5 | ||||
-rw-r--r-- | Player/StandalonePlayer.py | 92 | ||||
-rw-r--r-- | Util/CSoundNote.py | 2 | ||||
-rw-r--r-- | Util/NoteLooper.py | 158 |
6 files changed, 121 insertions, 140 deletions
diff --git a/Generation/GenerationRythm.py b/Generation/GenerationRythm.py index fef9d42..b621718 100644 --- a/Generation/GenerationRythm.py +++ b/Generation/GenerationRythm.py @@ -104,7 +104,7 @@ class GenerationRythm: for beat in range( beatsPerPage ): beats.append( beat * Config.TICKS_PER_BEAT ) for i in range( len( beats ) ): - print ( beats[ GenerationConstants.PUNCH_ACCENTS[ beatsPerPage ][ i ] ], pow( float( len( beats ) - i) / len( beats ), 1.5 ) * 100.) + print 'INFO: GenerationRythm::drumRythmSequence', ( beats[ GenerationConstants.PUNCH_ACCENTS[ beatsPerPage ][ i ] ], pow( float( len( beats ) - i) / len( beats ), 1.5 ) * 100.) downBeats.append( ( beats[ GenerationConstants.PUNCH_ACCENTS[ beatsPerPage ][ i ] ], pow( float( len( beats ) - i) / len( beats ), 1.5 ) * 100.) ) for downBeat in downBeats: upBeats.append( ( downBeat[ 0 ] + Config.TICKS_PER_BEAT , downBeat[ 1 ] ) ) diff --git a/Generation/VariationRythm.py b/Generation/VariationRythm.py index 7506762..5ac727e 100644 --- a/Generation/VariationRythm.py +++ b/Generation/VariationRythm.py @@ -62,7 +62,7 @@ class RythmReverse( RythmShuffle ): self.newOnsetList.append( i ) self.newOnsetList.reverse() - print len( self.newOnsetList ), len( notesList ) + print 'INFO: RythmReverse: ', len( self.newOnsetList ), len( notesList ) RythmShuffle.getOldDuration( self, notesList ) RythmShuffle.getNewDuration( self, notesList ) diff --git a/Player/KeyboardStandAlone.py b/Player/KeyboardStandAlone.py index a8406ba..9f3d3b9 100644 --- a/Player/KeyboardStandAlone.py +++ b/Player/KeyboardStandAlone.py @@ -75,8 +75,7 @@ class KeyboardStandAlone: instrument = instrument, instrumentFlag = instrument, reverbSend = self.reverb) - #self.key_dict[key].play() - self.key_dict[key].play() + self.csnd.sendText( self.key_dict[key].getText(0.3,0)) #play self.onset_dict[key] = self.getCurrentTick() self.recording( CSoundNote( onset = 0, @@ -98,7 +97,7 @@ class KeyboardStandAlone: self.key_dict[key].duration = 1 self.key_dict[key].decay = 0.88 self.key_dict[key].amplitude = 1 - self.key_dict[key].play() + self.csnd.sendText( self.key_dict[key].getText(0.3,0)) #play self.adjustDuration(self.key_dict[key].pitch, self.onset_dict[key]) del self.key_dict[key] diff --git a/Player/StandalonePlayer.py b/Player/StandalonePlayer.py index af0047d..9f36a9f 100644 --- a/Player/StandalonePlayer.py +++ b/Player/StandalonePlayer.py @@ -4,6 +4,7 @@ import gtk import gobject import os import random +import time import Config @@ -28,6 +29,7 @@ class StandAlonePlayer( gtk.EventBox ): self.csnd = client self.instrument = self.getInstrumentList()[0] + self.timeout_ms = 50 self.reverb = 0. self.volume = 80 self.regularity = 0.75 @@ -35,14 +37,12 @@ class StandAlonePlayer( gtk.EventBox ): self.tempo = 130 self.rythmPlayer = RythmPlayer(self.csnd, self.recordStateButton) self.rythmInstrument = 'drum1kit' - self.noteLooper = NoteLooper( - 0.2, - Config.DEFAULT_TEMPO * 0.2, - #0.2 currently converts beats per second to seconds_per_tick - [Config.DRUM1KIT,Config.DRUM1KIT,Config.DRUM1KIT,Config.DRUM1KIT ], - [0.8, 0.8, 0.8, 0.8], - [1.0, 1.0, 1.0, 1.0]) - + self.noteLooper = NoteLooper( 0.2, self.tempo * 0.2 ) + self.notesList = [] + self.csnd.startTime() + self.noteLooper.startTime() + time.sleep(0.001) + self.playbackTimeout = None self.synthLabWindow1 = SynthLabWindow(self.csnd, 86) self.synthLabWindow2 = SynthLabWindow(self.csnd, 87) @@ -51,17 +51,6 @@ class StandAlonePlayer( gtk.EventBox ): self.csnd.setMasterVolume(self.volume) self.rythmPlayer.beat = self.beat - #self.rythmPlayer.notesList = generator( self.rythmInstrument, self.beat, self.regularity, self.reverb, self.csnd) - notesList = generator( self.rythmInstrument, self.beat, self.regularity, self.reverb, self.csnd) - def flatten(ll): - rval = [] - for l in ll: - rval += l - return rval - self.noteLooper.insert([(x.onset, x) for x in flatten(notesList)]) - self.noteLooper.setDuration( self.beat * Config.TICKS_PER_BEAT ) - self.noteLooper.setTick(0) #TODO: get playback head position - #self.noteLooper.setRate( round( self.tempoAdjustment.value, 0 ) * 0.2 ) self.tooltips = gtk.Tooltips() @@ -331,23 +320,34 @@ class StandAlonePlayer( gtk.EventBox ): else: return + def regenerate(self): + def flatten(ll): + rval = [] + for l in ll: + rval += l + return rval + self.notesList = flatten ( + generator(self.rythmInstrument, self.beat, self.regularity, self.reverb, self.csnd)) + self.noteLooper.clear() + self.noteLooper.setDuration( self.beat * Config.TICKS_PER_BEAT ) + self.noteLooper.insert([(x.onset, x) for x in self.notesList]) + def handleGenerationSlider(self, adj): img = int(adj.value * 7)+1 self.geneSliderBoxImgTop.set_from_file(Config.IMAGE_ROOT + 'complex' + str(img) + '.png') def handleGenerationSliderRelease(self, widget, event): self.regularity = widget.get_adjustment().value - self.rythmPlayer.notesList = generator( self.rythmInstrument, self.beat, self.regularity, self.reverb, self.csnd) - + self.regenerate() + def handleBeatSlider(self, adj): img = self.scale(int(adj.value),2,12,1,11) self.beatSliderBoxImgTop.set_from_file(Config.IMAGE_ROOT + 'beat' + str(img) + '.png') - print img def handleBeatSliderRelease(self, widget, event): self.beat = int(widget.get_adjustment().value) self.rythmPlayer.beat = self.beat - self.rythmPlayer.notesList = generator( self.rythmInstrument, self.beat, self.regularity, self.reverb, self.csnd) + self.regenerate() def handleVolumeSlider(self, adj): self.volume = int(adj.value) @@ -362,34 +362,41 @@ class StandAlonePlayer( gtk.EventBox ): self.keyboardStandAlone.setReverb(self.reverb) def handlePlayButton(self, widget, data = None): - if widget.get_active() == False: - self.rythmPlayer.stopPlayback() - gobject.source_remove( self.playbackTimeout ) - self.playbackTimeout = None - else: - self.rythmPlayer.startPlayback() - self.playbackTimeout = gobject.timeout_add( 50 , self.onTimeout ) - self.onTimeout() + if widget.get_active() == False: + #self.rythmPlayer.stopPlayback() + gobject.source_remove( self.playbackTimeout ) + self.playbackTimeout = None + else: + #self.rythmPlayer.startPlayback() + self.regenerate() + self.noteLooper.setTick(0) + self.playbackTimeout = gobject.timeout_add( self.timeout_ms, self.onTimeout ) + self.onTimeout() def onTimeout( self ): + if self.playbackTimeout == None : return False cmds = self.noteLooper.next() - #for c in cmds: self.csnd.sendText( c ) + for c in cmds: + self.csnd.sendText( c ) + #self.playStartupSound() return True def handleGenerationDrumBtn(self , widget , data): + #data is drum1kit, drum2kit, or drum3kit self.rythmInstrument = data - if self.rythmPlayer.notesList: - for seq in self.rythmPlayer.notesList: - for note in seq: - note.instrument = data + for n in self.notesList : + n.instrumentFlag = data + n.nchanges += 1 def handleGenerateBtn(self , widget , data=None): #self.rythmPlayer.beat = self.beat - #self.rythmPlayer.notesList = generator( self.rythmInstrument, self.beat, self.regularity, self.reverb, self.csnd) - self.rythmPlayer.startPlayback() - self.playStopButton.set_active(True) - self.playStartupSound() - + #self.rythmPlayer.startPlayback() + if self.playbackTimeout == None : + self.playStopButton.set_active(True) #this calls handlePlayButton + self.playStartupSound() + else: + self.regenerate() + def enableKeyboard( self ): self.keyboardStandAlone = KeyboardStandAlone( self.csnd, self.rythmPlayer.recording, self.rythmPlayer.adjustDuration, self.rythmPlayer.getCurrentTick ) self.add_events(gtk.gdk.BUTTON_PRESS_MASK) @@ -400,10 +407,11 @@ class StandAlonePlayer( gtk.EventBox ): def setTempo(self,adj): self.rythmPlayer.setTempo(int(adj.value)) + self.noteLooper.setRate( adj.value * 0.2 ) # 0.2 = 12 <ticks per beat> / 60 <beats per min > img = int((adj.value - 40) /26.)+1 self.tempoSliderBoxImgTop.set_from_file(Config.IMAGE_ROOT + 'tempo' + str(img) + '.png') - def playInstrumentNote(self , instrument, secs_per_tick = 0.02): + def playInstrumentNote(self , instrument, secs_per_tick = 0.025): note = CSoundNote( onset = 0, pitch = 36, amplitude = 1, diff --git a/Util/CSoundNote.py b/Util/CSoundNote.py index 2632b45..848d569 100644 --- a/Util/CSoundNote.py +++ b/Util/CSoundNote.py @@ -43,6 +43,7 @@ class CSoundNote : self.instrumentFlag = Config.DRUM1INSTRUMENTS[ self.pitch ] else: self.instrumentFlag = self.instrument + self.nchanges = 0 def __getstate__(self): return {'onset': self.onset, @@ -79,6 +80,7 @@ class CSoundNote : self.tied = dict['tied'] self.overlap = dict['overlap'] self.instrumentFlag = dict['instrumentFlag'] + self.nchanges = 0 def clone( self ): return CSoundNote( self.onset, self.pitch, self.amplitude, self.pan, diff --git a/Util/NoteLooper.py b/Util/NoteLooper.py index 3b31416..7ee264a 100644 --- a/Util/NoteLooper.py +++ b/Util/NoteLooper.py @@ -17,14 +17,7 @@ from Generation.GenerationConstants import GenerationConstants class NoteLooper: #PRIVATE - DRIFT = 0.01 - - def dirty_track(self, track): - for i in range(len(self.notes)): - (o,n,c) = self.notes[i] - if n.trackId == track: - self.notes[i] = (o,n,'') - + DRIFT = 0.01 #careful about changing this... coordinate with instrument 5777 #PUBLIC @@ -32,47 +25,55 @@ class NoteLooper: self.ticks_per_sec = ticks_per_sec # ticks last this long self.secs_per_tick = 1.0 / ticks_per_sec # precomputed inverse self.range_sec = range_sec # notes are checked-for, this many seconds in advance - self.range_tick = int( range_sec * ticks_per_sec ) # same, but in ticks self.duration = 0 # number of ticks in playback loop - self.prev_duration = 0 + self.loops = 0 # number of elapsed loops self.notes = [] # sorted list of (onset, noteptr, cache) - self.tick0 = 0 # the tick at which playback started - self.time0 = time.time() + 1000000 # the real time at which playback started + self.time0 = time.time() + 1000000 # the real time at which tick == 0 (sometimes retro-active) + #self.time_start # remember to call NoteLooper.startTime + # at the same time as you call + # CSoundClient.startTime() def setTick( self, tick ): - self.time0 = time.time() - self.tick0 = tick % self.duration - self.hIdx = bisect.bisect_left(self.notes, self.tick0) - self.prev_duration = 0 + time_time = time.time() + self.time0 = time_time - tick * self.secs_per_tick + self.loops = tick // self.duration + self.hIdx = bisect.bisect_left(self.notes, tick - self.duration * self.loops ) def setRate( self, ticks_per_sec): if ticks_per_sec != self.ticks_per_sec: - t = time.time() secs_per_tick = 1.0 / ticks_per_sec - if t > self.time0: - self.tick0 += int( (t - self.time0) * ticks_per_sec) - self.time0 = t + time_time = time.time() + curtick = self.getTick( 0.0, False, time_time ) + curticktime = curtick * self.secs_per_tick + self.time0 + self.ticks_per_sec = ticks_per_sec self.secs_per_tick = secs_per_tick - self.range_tick = ticks_per_sec * self.range_sec - self.notes = [ (o,n,'') for (o,n,c) in self.notes ] #clear cache - self.prev_duration = 0 + self.time0 = curticktime - curtick * secs_per_tick + self.notes = [ (o,n,'',z) for (o,n,c,z) in self.notes ] #clear cache + self.loops = 0 def setDuration( self, duration ): + self.time0 += self.loops * self.duration * self.secs_per_tick + self.loops = 0 self.duration = duration - def getCurrentTick(self, future , domod , t): #t is for time - if domod : return ( self.tick0 + int( ( t + future - self.time0 ) * self.ticks_per_sec ) ) % self.duration - else : return ( self.tick0 + int( ( t + future - self.time0 ) * self.ticks_per_sec ) ) + def getTick(self, future , domod , t): #t is for time + if domod : + return ( int( ( t + future - self.time0 ) * self.ticks_per_sec ) ) % self.duration + else : + return ( int( ( t + future - self.time0 ) * self.ticks_per_sec ) ) def next( self ) : time_time = time.time() - tickhorizon = self.getCurrentTick( self.range_sec, False, time_time ) #tick where we'll be after range_sec + #tickhorizon is tick where we'll be after range_sec + tickhorizon = self.getTick( self.range_sec, False, time_time ) + time0_time = self.time0 - self.time_start + self.DRIFT - if tickhorizon < self.tick0 : return [] + if tickhorizon < 0 : return [] + if len(self.notes) == 0 : return [] def cache_cmd(secs_per_tick, amplitude, pitch, inst, trackId, duration, tied, fullDuration, overlap, attack, decay, reverbSend, filterType, filterCutoff, pan ): if inst[0:4] == 'drum': @@ -118,75 +119,60 @@ class NoteLooper: return rval def getText(i, secs_per_tick, time_offset): - (onset,note,cache) = self.notes[i] - if cache == '' : - self.notes[i] = ( onset, note, - cache_cmd( secs_per_tick, - note.amplitude, # * track-level mixer rate - note.pitch, - note.instrumentFlag, - note.trackId, - note.duration * self.secs_per_tick, - note.tied, - note.fullDuration, - note.overlap, - note.attack, - note.decay, - note.reverbSend, - note.filterType, - note.filterCutoff, - note.pan)) + (onset,note,cache,z) = self.notes[i] + if cache == '' or note.nchanges != z : + self.notes[i] = \ + ( + onset, + note, + cache_cmd( + secs_per_tick, + note.amplitude, # * track-level mixer rate + note.pitch, + note.instrumentFlag, + note.trackId, + note.duration * self.secs_per_tick, + note.tied, + note.fullDuration, + note.overlap, + note.attack, + note.decay, + note.reverbSend, + note.filterType, + note.filterCutoff, + note.pan), + note.nchanges + ) rval = self.notes[i][2] % float(onset * self.secs_per_tick + time_offset) return rval - if self.tick0 != 0: - print self.tick0 - raise 'tick0 != 0' - - prev_secs = self.prev_duration * self.secs_per_tick + prev_secs = (self.loops * self.duration) * self.secs_per_tick rval = [] - while self.notes[self.hIdx][0] + self.prev_duration < tickhorizon: - rval.append ( getText(self.hIdx, self.secs_per_tick, prev_secs + self.DRIFT ) ) + while self.notes[self.hIdx][0] + self.loops * self.duration < tickhorizon: + rval.append ( getText(self.hIdx, self.secs_per_tick, prev_secs + time0_time ) ) self.hIdx += 1 if self.hIdx == len(self.notes): - self.hIdx = 0 - self.prev_duration += self.duration - prev_secs += self.secs_per_tick + self.hIdx = 0 + self.loops += 1 + prev_secs += self.duration * self.secs_per_tick - - if False: - if tickhorizon <= self.duration + self.prev_duration: - hIdx = self.hIdx - self.hIdx = hIdxMax = bisect.bisect_left(self.notes, (tickhorizon - self.prev_duration,)) - rlist = [(i, self.prev_duration * self.secs_per_tick) for i in range(hIdx, hIdxMax)] - else: - self.hIdx = hIdxMax = bisect.bisect_left(self.notes, (self.duration,)) - rlist = [(i, self.prev_duration * self.secs_per_tick) for i in range(self.hIdx, hIdxMax)] - - while tickhorizon > self.duration + self.prev_duration: #loop back to tick0 == 0 - self.prev_duration += self.duration - tickhorizon -= self.duration - self.hIdx = hIdxMax = bisect.bisect_left(self.notes, (min(tickhorizon - self.prev_duration, self.duration), 0)) - rlist += [(i,self.prev_duration * self.secs_per_tick) for i in range(hIdxMax)] - - rval = [ getText(i, self.secs_per_tick, looplag) for (i,looplag) in rlist ] return rval def insert( self, notes): def insertMany(): - self.notes += [ ( notes[i][0], notes[i][1], '' ) for i in xrange(len(notes)) ] + self.notes += [ ( notes[i][0], notes[i][1], '', 0 ) for i in xrange(len(notes)) ] self.notes.sort() def insertFew(): for i in xrange(len(notes)): - t = (notes[i][0], notes[i][1],'') + t = (notes[i][0], notes[i][1],'',0) l = bisect.bisect_left(self.notes, t ) self.notes.insert(l, t) - print 't',t if len(notes) > 6: insertMany() else: insertFew() + self.hIdx = bisect.bisect_left(self.notes, self.getTick(self.range_sec, True, time.time())) def remove(self, note): def removeFew(): @@ -204,24 +190,10 @@ class NoteLooper: removeMany() else: removeFew() + self.hIdx = bisect.bisect_left(self.notes, self.getTick(self.range_sec, True, time.time())) def clear(self): self.notes = [] - - def setVolume(self, track,vol): - raise 'dont call' - if self.tvol[track] != vol: - self.tvol[track] = vol - self.dirty_track(track) - def setInstrument(self, track, inst): - raise 'dont call' - if self.inst[track] != inst: - self.inst[track] = inst - self.dirty_track(track) - def setMute(self, track, m): - raise 'dont call' - if self.mute[track] != m: - self.mute[track] = m - self.dirty_track(track) - + def startTime(self): + self.time_start = time.time() |