Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fixes and new features #10

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
92 changes: 84 additions & 8 deletions LO2ClipSlotComponent.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,35 @@ def wrap(self, *a, **kw):
def __init__(self, tid, sid, *a, **k):
self._track_id = tid
self._scene_id = sid

self._has_clip = 0

super(LO2ClipSlotComponent, self).__init__(*a, **k)

self.set_default('_track_id', '_scene_id')

callbacks = {'color': 'color', 'name': 'name', 'warping': 'warping', 'looping': 'looping', 'loopstart': 'loop_start', 'loopend': 'loop_end', 'start': 'start_marker', 'end': 'end_marker', 'loopjump': 'loop_jump'}
callbacks = {
'color': 'color',
'name': 'name',
'warping': 'warping',
'looping': 'looping',
'loopstart': 'loop_start',
'loopend': 'loop_end',
'start': 'start_marker',
'end': 'end_marker',
'loopjump': 'loop_jump',
'muted': 'muted'
}

for n,p in callbacks.iteritems():
self.add_simple_callback('/live/clip/'+n, '_clip_slot.clip', p, self._is_clip, getattr(self, '_on_clip_'+n+'_changed'))

self.add_callback('/live/clip/play', self._fire)
self.add_callback('/live/clip/stop', self._stop)
self.add_callback('/live/clip/pitch', self._pitch)
self.add_callback('/live/clip/select', self._view)
self.add_callback('/live/clip/notes', self._notes)
self.add_callback('/live/clip/notes/add', self._notes_add)
self.add_callback('/live/clip/notes/remove', self._notes_remove)



Expand All @@ -50,6 +65,7 @@ def _lo2__update_clip_property_slots(self):
self._on_clip_start_changed.subject = clip
self._on_clip_end_changed.subject = clip
self._on_clip_gain_changed.subject = clip
self._on_clip_muted_changed.subject = clip


def _is_clip(self, msg):
Expand Down Expand Up @@ -107,18 +123,31 @@ def _lo2__on_clip_playing_state_changed(self):
def _send_state(self):
if self._scene_id == -1:
return

state = int(self._clip_slot.has_clip) if self._clip_slot is not None else 0


state = LO2ClipSlotComponent.compute_state(self._clip_slot)
name = ''

if self._clip_slot.has_clip:
c = self._clip_slot.clip
name = self._clip_slot.clip.name

self.send('/live/clip/state', self._track_id, self._scene_id, state)

if self._clip_slot.has_clip != self._has_clip:
self._has_clip = self._clip_slot.has_clip
self.send_default('/live/clip/name', name)

@staticmethod
def compute_state(clip_slot):
state = int(clip_slot.has_clip) if clip_slot is not None else 0

if clip_slot.has_clip:
c = clip_slot.clip
if c.is_playing:
state = 2
if c.is_triggered:
state = 3

self.send('/live/clip/state', self._track_id, self._scene_id, state)

return state


def _lo2__on_clip_color_changed(self):
Expand Down Expand Up @@ -161,6 +190,10 @@ def _on_clip_end_changed(self):
@subject_slot('gain')
def _on_clip_gain_changed(self):
self.send_default('/live/clip/gain', self._clip_slot.clip.gain)

@subject_slot('muted')
def _on_clip_muted_changed(self):
self.send_default('/live/clip/muted', self._clip_slot.clip.muted)



Expand Down Expand Up @@ -202,3 +235,46 @@ def _view(self, msg, src):
self.song().view.selected_scene = self.song().scenes[self._scene_id]


def _notes(self, msg, src):
if self._is_clip(msg):
c = self._clip_slot.clip

if c.is_midi_clip and len(msg) == 8:
notes = c.get_notes(msg[4], msg[5], msg[6], msg[7])
data = []
data.append(self._track_id)
data.append(self._scene_id)

for n in notes:
for p in n:
data.append(p)
self.send('/live/clip/notes', data)


def _notes_add(self, msg, src):
if self._is_clip(msg):
c = self._clip_slot.clip
if c.is_midi_clip and len(msg) >= 9:
param_count = len(msg) - 4
if param_count % 5 != 0:
return # bad param count/format

note_count = param_count / 5

notes_to_add = []
for x in range(note_count):
notes_to_add.append(
(msg[x * 5 + 4], msg[x * 5 + 5], msg[x * 5 + 6], msg[x * 5 + 7], msg[x * 5 + 8])
)

c.set_notes(tuple(notes_to_add))


def _notes_remove(self, msg, src):
if self._is_clip(msg):
c = self._clip_slot.clip
if c.is_midi_clip and len(msg) == 8:
c.remove_notes(msg[4], msg[5], msg[6], msg[7])



6 changes: 2 additions & 4 deletions LO2MixerComponent.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,8 @@ def _lo2__on_return_tracks_changed(self):

# Callbacks
def _lo2_on_track_list_changed(self):
if len(self.song().tracks) != self._track_count:
self.log_message('/live/tracks:' + str(len(self.song().tracks)))
self.send('/live/tracks', len(self.song().tracks))
self._track_count = len(self.song().tracks)
self._track_count = len(self.song().tracks)
self.send('/live/tracks', self._track_count)


def _lo2_on_selected_track_changed(self):
Expand Down
3 changes: 3 additions & 0 deletions LO2OSC.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def error(self):

def send(self, address, msg):
oscmsg = OSC.OSCMessage(address, msg)
if len(oscmsg.error) > 0:
self.log_message('OSCMessage Error: ' + ''.join(oscmsg.error))

self._socket.sendto(oscmsg.getBinary(), self._remote_addr)

def send_message(self, message):
Expand Down
46 changes: 33 additions & 13 deletions LO2SessionComponent.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from LO2SceneComponent import LO2SceneComponent
from LO2Mixin import LO2Mixin, wrap_init
from LO2ClipSlotComponent import LO2ClipSlotComponent

class LO2SessionComponent(SessionComponent, LO2Mixin):

Expand All @@ -22,6 +23,7 @@ def __init__(self, *args, **kwargs):

self.add_callback('/live/scene/name/block', self._scene_name_block)
self.add_callback('/live/clip/name/block', self._clip_name_block)
self.add_callback('/live/clip/state/block', self._clip_state_block)

self.add_function_callback('/live/scenes', self._lo2_on_scene_list_changed)

Expand Down Expand Up @@ -59,9 +61,7 @@ def _reassign_scenes(self):

# Listeners
def _lo2_on_scene_list_changed(self):
if len(self.song().scenes) != self._scenes_count:
self.send('/live/scenes', len(self.song().scenes))
self._scenes_count = len(self.song().scenes)
self.send('/live/scenes', len(self.song().scenes))


def _lo2_on_selected_scene_changed(self):
Expand Down Expand Up @@ -104,19 +104,39 @@ def _clip_name_block(self, msg, src):
""" Gets a block of clip names
"""
b = []
for i in range(msg[2], msg[2]+msg[3]):
for i in range(msg[3], msg[3]+msg[5]):
if i < len(self._scenes):
s = self.scene[i]
for j in range(msg[4], msg[4]+msg[5]):
s = self.scene(i)
for j in range(msg[2], msg[2]+msg[4]):
if j < len(s._clip_slots):
c = s.clip_slots(j)
b.append(i, j, c.clip_name)
c = s.clip_slot(j)
b.append(str(c.clip_name))
else:
b.append(i, j, '')
b.append('')
else:
b.append(i, j, '')
b.append('')

self.send('/live/clip/name/block', b)





def _clip_state_block(self, msg, src):
""" Gets a block of clip states
"""
b = []

for i in range(msg[3], msg[3]+msg[5]):
if i < len(self._scenes):
s = self.scene(i)
for j in range(msg[2], msg[2]+msg[4]):
if j < len(s._clip_slots):
c = s.clip_slot(j)
b.append(LO2ClipSlotComponent.compute_state(c._clip_slot))
else:
b.append(-1)
else:
b.append(-1)

self.send('/live/clip/state/block', b)


28 changes: 27 additions & 1 deletion LO2TransportComponent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self, *a, **kw):
self._on_signature_numerator_changed.subject = s
self._on_signature_denominator_changed.subject = s
self._on_tempo_changed.subject = s
self._on_playing_changed.subject = s

self.add_default_callback('/live/tempo', s, 'tempo', float)
self.add_default_callback('/live/time', s, 'current_song_time', float)
Expand All @@ -22,6 +23,10 @@ def __init__(self, *a, **kw):

self.add_function_callback('/live/cue/next', s.jump_to_next_cue)
self.add_function_callback('/live/cue/prev', s.jump_to_prev_cue)
self.add_callback('/live/cue', self._cue)
self.add_callback('/live/cue/jump', self._cue_jump)

self.add_callback('/live/jump', self._jump)

self.add_function_callback('/live/play', s.start_playing)
self.add_function_callback('/live/play/continue', s.continue_playing)
Expand Down Expand Up @@ -123,4 +128,25 @@ def _del_clip(self, msg, src):
if msg[3] < len(self.song().scenes):
c = tr.clip_slots[msg[3]]
if c.has_clip:
c.delete_clip(msg[4])
c.delete_clip(msg[4])

def _cue(self, msg, src):
data = []
for c in sorted(self.song().cue_points, key = lambda cue: cue.time):
data.append(c.name)
data.append(c.time)

self.send('/live/cue', data)

def _cue_jump(self, msg, src):
self.log_message('jump to: ' + msg[2])
for c in sorted(self.song().cue_points, key = lambda cue: cue.time):
if c.name == msg[2]:
c.jump()
return

def _jump(self, msg, src):
if len(msg) == 3:
self.song().jump_by(msg[2])


10 changes: 9 additions & 1 deletion LiveOSC2.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def __init__(self, c_instance):
self._session = LO2SessionComponent(1,1)
self._session.set_mixer(self._mixer)
self._transport = LO2TransportComponent()

self._mixin = LO2Mixin()
self._c_instance = c_instance
self._mixin.add_callback('/live/selection', self._live_selection)

self.parse()

Expand All @@ -41,4 +45,8 @@ def disconnect(self):

def parse(self):
self.osc_handler.process()
self.schedule_message(1, self.parse)
self.schedule_message(1, self.parse)


def _live_selection(self, msg, src):
self._c_instance.set_session_highlight(msg[2], msg[3], msg[4], msg[5], 0)
14 changes: 11 additions & 3 deletions OSC.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,25 @@ def __init__(self, address='', msg=()):
self.address = address
self.typetags = ","
self.message = ""
self.error = []

if type(msg) in (str, int, float, bool, unicode):
self.append(msg)
elif type(msg) in (list,tuple):
for m in msg:
if type(m) not in (str,int,float, bool, unicode):
#log("don't know how to encode message element " + str(m) + " " + str(type(m)))
if type(m) in (list,tuple):
for me in m:
if type(me) not in (str, int, float, bool, unicode):
self.error.append("don't know how to encode message element element " + str(m) + " " + str(type(m)))
return
self.append(me)
continue
if type(m) not in (str, int, float, bool, unicode):
self.error.append("don't know how to encode message element " + str(m) + " " + str(type(m)))
return
self.append(m)
else:
#log("don't know how to encode message " + str(m) + " " + str(type(m)))
self.error.append ("don't know how to encode message " + str(m) + " " + str(type(m)))
return

def append(self, argument, typehint = None):
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ groove
cue points
set / delete cue

/live/cue
Gets all the cue points in the song, sorted by time in the format (float beat_time, string name)

/live/cue/jump (string name)
Jumps to the first occurrence of a cue point with the provided name

/live/cue/next
/live/cue/prev
Jumps to the next and previous cue points in arrangement view
Expand All @@ -61,6 +67,8 @@ set / delete cue
/live/play/select
Starts playing the current selection in arrangement view

/live/jump (float beats)
Instantly jumps ahead n beats

/live/undo
/live/redo
Expand Down Expand Up @@ -192,6 +200,8 @@ Clips

/live/clip/state (int track_id, int scene_id)

/live/clip/state/block (int track_id, int scene_id, int width, int height)

/live/clip/play (int track_id, int scene_id)

/live/clip/stop (int track_id, int scene_id)
Expand All @@ -204,6 +214,26 @@ Clips

/live/clip/color (int track_id, int scene_id)

/live/clip/muted (int track_id, int scene_id, int muted)

/live/clip/notes (int track_id, int scene_id, float start_time, int start_pitch, float time_length, int pitch_length)

/live/clip/notes/add (int track_id, int scene_id, int pitch, float time, float duration, int velocity, int mute)
```
pitch is from 0 to 127
time is in beats (0 is beat 1, 1 is beat 2, 0.5 is halfway between the two)
duration is in beats (0.25 is a quarter beat, 0.5 half beat, 1 whole beat, ...)
velocity is 0 127
mutes is 0 or 1
```

Example adding multiple notes

```
/live/clip/notes/add 0 0 0 0 0.5 100 0 12 0 0.5 100 0 24 0 0.5 100 0
```

/live/clip/notes/remove (int track_id, int scene_id, float start_time, int start_pitch, float time_length, int pitch_length)

/live/clip/looping (int track_id, int scene_id)
/live/clip/loopstart (int track_id, int scene_id)
Expand Down