Skip to content

Commit

Permalink
implemented virtual sequencer to complement the virtual synthesizer.
Browse files Browse the repository at this point in the history
  • Loading branch information
robertoostenveld committed Nov 26, 2015
1 parent 875bf0c commit ea6fe3d
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 14 deletions.
3 changes: 2 additions & 1 deletion module/keyboard/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
while True:
for msg in port.iter_pending():
if hasattr(msg,"note"):
print(msg)
if config.get('output','action')=="release" and msg.velocity>0:
pass
elif config.get('output','action')=="press" and msg.velocity==0:
Expand All @@ -30,7 +31,7 @@
key = "{}.channel{:0>2d}.note".format(config.get('output','prefix'),msg.channel)
val = msg.note
r.set(key,val)
# send it as trigger: prefix.channel000.note=velocity
# send it as trigger: prefix.channel000.note=note
key = "{}.channel{:0>2d}.note".format(config.get('output','prefix'),msg.channel)
val = msg.velocity
r.publish(key,val)
Expand Down
5 changes: 4 additions & 1 deletion module/launchcontrol/launchcontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
r = redis.StrictRedis(host=config.get('redis','hostname'), port=config.getint('redis','port'), db=0)

# this is only for debugging
mido.get_input_names()
print('-------------------------')
for port in mido.get_input_names():
print(port)
print('-------------------------')

port = mido.open_input(config.get('midi','device'))

Expand Down
32 changes: 32 additions & 0 deletions module/sequencer/sequencer.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[general]
name="Sequencer"

[redis]
hostname=localhost
port=6379

[sequence]
pattern000= 0 4 0 7
pattern001= 0 4 7 9 10 9 7 4
pattern002= 5 10 13 10
pattern003= 5 13 12 10
pattern004= 4 4 4 4
pattern005= 5 5 5 5
pattern006= 6 6 6 6
pattern007= 7 7 7 7
pattern008= 8 8 8 8
pattern009= 9 9 9 9
pattern010= 10 10 10 10

[default]
pattern=1 ; see above for the sequences
rate=120 ; beats per minute
offset=48

[input]
pattern=launchcontrol.channel00.control053
rate=launchcontrol.channel00.control054
offset=keyboard.channel00.note

[output]
prefix=sequencer
63 changes: 63 additions & 0 deletions module/sequencer/sequencer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python

import math
import ConfigParser # this is version 2.x specific,on version 3.x it is called "configparser" and has a different API
import redis
import time

config = ConfigParser.ConfigParser()
config.read('sequencer.ini')

r = redis.StrictRedis(host=config.get('redis','hostname'),port=config.getint('redis','port'),db=0)

pattern = r.get(config.get('input','pattern'))
if pattern:
pattern = int(pattern)
else:
pattern = config.getint('default','pattern')
previous = pattern

# get the corresponding pattern
sequence = config.get('sequence',"pattern{:0>3d}".format(pattern))

while True:

for note in sequence.split():
# this will return empty if not available
new = int(r.get(config.get('input','pattern')))
if pattern!=new:
pattern = new
try:
sequence = config.get('sequence',"pattern{:0>3d}".format(pattern))
except:
pass
# immediately start playing the new sequence
break

rate = r.get(config.get('input','rate'))
if rate:
rate = float(rate)
else:
rate = config.getfloat('default','rate')
# assume that value is between 0 and 127, map exponentially from 20 to 2000
# it should not get too low, otherwise the code with the sleep below becomes unresponsive
rate = 20*math.exp(rate*math.log(100)/127)

offset = r.get(config.get('input','offset'))
if offset:
offset = int(offset)
else:
offset = config.getint('default','offset')

note = int(note)

print(pattern, int(rate), offset, note)

key = "{}.note".format(config.get('output','prefix'))
val = note+offset
# send it as control value: prefix.channel000.note=note
r.set(key,val)
# send it as trigger: prefix.channel000.note=note
r.publish(key,val)

time.sleep(60/rate)
8 changes: 2 additions & 6 deletions module/synthesizer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@ The LFO shapes the audio envelope. The LFO has two input controls for the freque

The VCA shapes the audio envelope. The VCA has a single input controls for the attenuation.

** NOT YET IMPLEMENTED: ADSR - attack, decay, sustain, release **
** ADSR - attack, decay, sustain, release **

The ADSR takes a trigger as input and generates a continuous envelope as output. It has four input controls.

** NOT YET IMPLEMENTED: VCF - voltage controlled filter **

The filter can be toggled between lowpass, highpass and bandpass filtering of the audio signal. It has two input controls for the frequency and bandwidth.
The ADSR takes a trigger as input and generates a continuous envelope as output. It has input controls for A, D, S and R and for the trigger.

** Requirements **

Expand Down
14 changes: 8 additions & 6 deletions module/synthesizer/synthesizer.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,25 @@ pitch=64
lfo_frequency=64
lfo_depth=64
vca=64
attack=16
decay=16
sustain=64
release=32
adsr_attack=16
adsr_decay=16
adsr_sustain=64
adsr_release=32

[input]
sin=launchcontrol.channel00.control077
tri=launchcontrol.channel00.control078
saw=launchcontrol.channel00.control079
sqr=launchcontrol.channel00.control080
pitch=launchcontrol.channel00.control081
; pitch=launchcontrol.channel00.control081
; pitch=keyboard.channel00.note
pitch=sequencer.note
lfo_frequency=launchcontrol.channel00.control082
lfo_depth=launchcontrol.channel00.control083
vca=launchcontrol.channel00.control084
adsr_gate=launchcontrol.channel00.note
; adsr_gate=launchcontrol.channel00.note
; adsr_gate=keyboard.channel00.note
adsr_gate=sequencer.note
adsr_attack=launchcontrol.channel00.control049
adsr_decay=launchcontrol.channel00.control050
adsr_sustain=launchcontrol.channel00.control051
Expand Down

0 comments on commit ea6fe3d

Please sign in to comment.