[SCM] python-pyo/master: Added a setKeepLast method to TableRead object (will hold last value).
tiago at users.alioth.debian.org
tiago at users.alioth.debian.org
Wed May 10 00:22:33 UTC 2017
The following commit has been merged in the master branch:
commit 9a0f3432cc56956edc9cb46b05404668b7b192e1
Author: Tiago Bortoletto Vaz <tiago at debian.org>
Date: Sun Mar 12 17:42:22 2017 -0400
Added a setKeepLast method to TableRead object (will hold last value).
diff --git a/ChangeLog b/ChangeLog
index 8ee28a6..6a5832f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2017-03-11 belangeo <belangeo at gmail.com>
+
+ * Added a setKeepLast method to TableRead object (will hold last value).
+
+2017-03-10 belangeo <belangeo at gmail.com>
+
+ * Added a setMode method to Selector object to switch between
+ equal power mode and linear fade.
+
+2017-03-06 belangeo <belangeo at gmail.com>
+
+ * Fixed SfMarkerLooper and SfMarkerShuffler markers not accurate
+ when soundfile sampling rate is not the same as the server's
+ sampling rate.
+
+2017-03-04 belangeo <belangeo at gmail.com>
+
+ * Added a "title" argument to Server.gui() method.
+
+2017-03-03 belangeo <belangeo at gmail.com>
+
+ * MidiDispatcher can send sysex message with sendx() method.
+
+2017-03-03 belangeo <belangeo at gmail.com>
+
+ * Midi input refactoring. Events are now spreaded over the buffer size
+ according to the event's timestamp.
+
+2017-02-19 belangeo <belangeo at gmail.com>
+
+ * Added examples about multicore audio programming with pyo.
+
2017-02-13 belangeo <belangeo at gmail.com>
* Final revision for version 0.8.3.
diff --git a/TODO.md b/TODO.md
index c5d2807..6a0fe34 100644
--- a/TODO.md
+++ b/TODO.md
@@ -45,6 +45,8 @@ MIDI
- Create a MidiLinseg object that act like MidiAdsr but with a breakpoints
function as the envelope. The sustain point should settable by the user.
+- sysex support in MidiListener.
+
GUI
---
@@ -58,7 +60,7 @@ GUI
- Keyboard, a virtual MIDI keyboard (adapted from Zyne's one).
-- Save item in ctrl() and DataTable graph() windows.
+- Save menu item in ctrl() and DataTable graph() windows.
Tables
------
diff --git a/debian/changelog b/debian/changelog
index 0760fae..58547a2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-pyo (0.8.3+git20170311.01-1) unstable; urgency=medium
+
+ * Added a setKeepLast method to TableRead object (will hold last value).
+
+ -- Tiago Bortoletto Vaz <tiago at debian.org> Sun, 12 Mar 2017 17:36:32 -0400
+
python-pyo (0.8.3-1) unstable; urgency=medium
* New upstream release.
diff --git a/doc-sphinx/source/index.rst b/doc-sphinx/source/index.rst
index 1053c53..7cb051b 100644
--- a/doc-sphinx/source/index.rst
+++ b/doc-sphinx/source/index.rst
@@ -41,6 +41,7 @@ Examples
Playing with soundfiles <examples/04-soundfiles/index>
Amplitude envelopes <examples/05-envelopes/index>
Filtering <examples/06-filters/index>
+ Multicore audio programming <examples/18-multicore/index>
Much more to come... Stay tuned!
diff --git a/examples/18-multicore/01-processes-spawning.py b/examples/18-multicore/01-processes-spawning.py
new file mode 100644
index 0000000..dcd5f9a
--- /dev/null
+++ b/examples/18-multicore/01-processes-spawning.py
@@ -0,0 +1,37 @@
+"""
+01-processes-spawning.py - Simple processes spawning, no synchronization.
+
+Need at least 4 cores to be really effective.
+
+Usage:
+ python3 -i 01-processes-spawning.py
+
+"""
+import time, random, multiprocessing
+from pyo import *
+
+class Proc(multiprocessing.Process):
+ def __init__(self, pitch):
+ super(Proc, self).__init__()
+ self.daemon = True
+ self.pitch = pitch
+
+ def run(self):
+ self.server = Server(audio="jack")
+ self.server.deactivateMidi()
+ self.server.boot().start()
+
+ # 200 randomized band-limited square wave oscillators.
+ self.amp = Fader(fadein=5, mul=0.01).play()
+ lo, hi = midiToHz((self.pitch - 0.1, self.pitch + 0.1))
+ self.fr = Randi(lo, hi, [random.uniform(.2, .4) for i in range(200)])
+ self.sh = Randi(0.1, 0.9, [random.uniform(.2, .4) for i in range(200)])
+ self.osc = LFO(self.fr, sharp=self.sh, type=2, mul=self.amp).out()
+
+ time.sleep(30) # Play for 30 seconds.
+ self.server.stop()
+
+if __name__ == '__main__':
+ # C major chord (one note per process).
+ p1, p2, p3, p4 = Proc(48), Proc(52), Proc(55), Proc(60)
+ p1.start(); p2.start(); p3.start(); p4.start()
diff --git a/examples/18-multicore/02-sharing-audio.py b/examples/18-multicore/02-sharing-audio.py
new file mode 100644
index 0000000..faef36c
--- /dev/null
+++ b/examples/18-multicore/02-sharing-audio.py
@@ -0,0 +1,48 @@
+"""
+02-sharing-audio.py - Sharing audio signals between processes.
+
+Usage:
+ python3 -i 02-sharing-audio.py
+
+"""
+import time, random, multiprocessing
+from pyo import *
+
+class Proc(multiprocessing.Process):
+ def __init__(self, create):
+ super(Proc, self).__init__()
+ self.daemon = True
+ self.create = create
+
+ def run(self):
+ self.server = Server(audio="jack")
+ self.server.deactivateMidi()
+ self.server.boot().start()
+ bufsize = self.server.getBufferSize()
+
+ nbands = 50
+ names = ["/f%02d" % i for i in range(nbands)]
+
+ if self.create: # 50 bands frequency splitter.
+ freq = [20 * 1.1487 ** i for i in range(nbands)]
+ amp = [pow(10, (i*-1)*0.05) * 8 for i in range(nbands)]
+ self.input = SfPlayer(SNDS_PATH+"/transparent.aif", loop=True)
+ self.filts = IRWinSinc(self.input, freq, freq, 3, 128, amp)
+ self.table = SharedTable(names, create=True, size=bufsize)
+ self.recrd = TableFill(self.filts, self.table)
+ else: # Independent transposition per band.
+ self.table = SharedTable(names, create=False, size=bufsize)
+ self.tscan = TableScan(self.table)
+ transpofac = [random.uniform(0.98,1.02) for i in range(nbands)]
+ self.pvana = PVAnal(self.tscan, size=1024, overlaps=4)
+ self.pvtra = PVTranspose(self.pvana, transpo=transpofac)
+ self.pvsyn = PVSynth(self.pvtra).out()
+
+ time.sleep(30)
+ self.server.stop()
+
+if __name__ == '__main__':
+ analysis = Proc(create=True)
+ synthesis = Proc(create=False)
+ analysis.start()
+ synthesis.start()
diff --git a/examples/18-multicore/03-synchronization.py b/examples/18-multicore/03-synchronization.py
new file mode 100644
index 0000000..0033dd2
--- /dev/null
+++ b/examples/18-multicore/03-synchronization.py
@@ -0,0 +1,74 @@
+"""
+03-synchronization.py - Synchronizing multiple processes.
+
+Usage:
+ python3 -i 03-synchronization.py
+
+"""
+import time, random, multiprocessing
+from pyo import *
+
+RECORD = False
+
+class Main(multiprocessing.Process):
+ def __init__(self):
+ super(Main, self).__init__()
+ self.daemon = True
+
+ def run(self):
+ self.server = Server(audio="jack")
+ self.server.deactivateMidi()
+ self.server.boot().start()
+ bufsize = self.server.getBufferSize()
+ if RECORD:
+ self.server.recstart("synchronization.wav")
+
+ self.tab1 = SharedTable("/audio-1", create=False, size=bufsize)
+ self.tab2 = SharedTable("/audio-2", create=False, size=bufsize)
+ self.out1 = TableScan(self.tab1).out()
+ self.out2 = TableScan(self.tab2).out(1)
+
+ time.sleep(30)
+ self.server.stop()
+
+class Proc(multiprocessing.Process):
+ def __init__(self, voice, conn):
+ super(Proc, self).__init__()
+ self.voice = voice
+ self.connection = conn
+ self.daemon = True
+
+ def run(self):
+ self.server = Server(audio="jack")
+ self.server.deactivateMidi()
+ self.server.boot()
+ bufsize = self.server.getBufferSize()
+
+ name = "/audio-%d" % self.voice
+ self.audiotable = SharedTable(name, create=True, size=bufsize)
+
+ onsets = random.sample([5,6,7,8,9], 2)
+ self.tab = CosTable([(0,0), (32,1), (512,0.5), (4096,0.5), (8191,0)])
+ self.ryt = Euclide(time=.125, taps=16, onsets=onsets, poly=1).play()
+ self.mid = TrigXnoiseMidi(self.ryt, dist=12, mrange=(60, 96))
+ self.frq = Snap(self.mid, choice=[0,2,3,5,7,8,10], scale=1)
+ self.amp = TrigEnv(self.ryt, table=self.tab, dur=self.ryt['dur'],
+ mul=self.ryt['amp'])
+ self.sig = SineLoop(freq=self.frq, feedback=0.08, mul=self.amp*0.3)
+ self.fil = TableFill(self.sig, self.audiotable)
+
+ # Wait for an incoming signal before starting the server.
+ while not self.connection.poll():
+ pass
+ self.server.start()
+
+ time.sleep(30)
+ self.server.stop()
+
+if __name__ == '__main__':
+ signal, child = multiprocessing.Pipe()
+ p1, p2 = Proc(1, child), Proc(2, child)
+ main = Main()
+ p1.start(); p2.start(); main.start()
+ time.sleep(.05)
+ signal.send(1)
diff --git a/examples/18-multicore/04-data-control.py b/examples/18-multicore/04-data-control.py
new file mode 100644
index 0000000..e35d884
--- /dev/null
+++ b/examples/18-multicore/04-data-control.py
@@ -0,0 +1,73 @@
+"""
+04-data-control.py - Multicore midi synthesizer.
+
+Need at least 4 cores to be really effective.
+
+Usage:
+ python3 -i 04-data-control.py
+
+"""
+import time, multiprocessing
+from random import uniform
+from pyo import *
+
+VOICES_PER_CORE = 4
+
+class Proc(multiprocessing.Process):
+ def __init__(self, pipe):
+ super(Proc, self).__init__()
+ self.daemon = True
+ self.pipe = pipe
+
+ def run(self):
+ self.server = Server(audio="jack")
+ self.server.deactivateMidi()
+ self.server.boot().start()
+
+ self.mid = Notein(poly=VOICES_PER_CORE, scale=1, first=0, last=127)
+ self.amp = MidiAdsr(self.mid['velocity'], 0.005, .1, .7, 0.5, mul=.1)
+ self.pit = self.mid['pitch'] * [uniform(.99, 1.01) for i in range(40)]
+ self.rc1 = RCOsc(self.pit, sharp=0.8, mul=self.amp).mix(1)
+ self.rc2 = RCOsc(self.pit*0.99, sharp=0.8, mul=self.amp).mix(1)
+ self.mix = Mix([self.rc1, self.rc2], voices=2)
+ self.rev = STRev(Denorm(self.mix), [.1, .9], 2, bal=0.30).out()
+
+ while True:
+ if self.pipe.poll():
+ data = self.pipe.recv()
+ self.server.addMidiEvent(*data)
+ time.sleep(0.001)
+
+ self.server.stop()
+
+if __name__ == '__main__':
+ main1, child1 = multiprocessing.Pipe()
+ main2, child2 = multiprocessing.Pipe()
+ main3, child3 = multiprocessing.Pipe()
+ main4, child4 = multiprocessing.Pipe()
+ mains = [main1, main2, main3, main4]
+ p1, p2, p3, p4 = Proc(child1), Proc(child2), Proc(child3), Proc(child4)
+ p1.start(); p2.start(); p3.start(); p4.start()
+
+ playing = {0: [], 1: [], 2: [], 3: []}
+ currentcore = 0
+ def callback(status, data1, data2):
+ global currentcore
+ if status == 0x80 or status == 0x90 and data2 == 0:
+ for i in range(4):
+ if data1 in playing[i]:
+ playing[i].remove(data1)
+ mains[i].send([status, data1, data2])
+ break
+ elif status == 0x90:
+ for i in range(4):
+ currentcore = (currentcore + 1) % 4
+ if len(playing[currentcore]) < VOICES_PER_CORE:
+ playing[currentcore].append(data1)
+ mains[currentcore].send([status, data1, data2])
+ break
+
+ s = Server()
+ s.setMidiInputDevice(99) # Open all devices.
+ s.boot().start()
+ raw = RawMidi(callback)
diff --git a/include/md_portmidi.h b/include/md_portmidi.h
index 0248a14..7788d6c 100644
--- a/include/md_portmidi.h
+++ b/include/md_portmidi.h
@@ -41,6 +41,7 @@ void pm_programout(Server *self, int value, int chan, long timestamp);
void pm_pressout(Server *self, int value, int chan, long timestamp);
void pm_bendout(Server *self, int value, int chan, long timestamp);
void pm_sysexout(Server *self, unsigned char *msg, long timestamp);
+long pm_get_current_time();
/* Queries. */
PyObject * portmidi_count_devices();
diff --git a/include/servermodule.h b/include/servermodule.h
index 4b16f33..6a9c0ad 100644
--- a/include/servermodule.h
+++ b/include/servermodule.h
@@ -80,6 +80,7 @@ typedef struct {
int midiin_count;
int midiout_count;
int midi_count;
+ long midi_time_offset;
double samplingRate;
int nchnls;
int ichnls;
@@ -160,6 +161,8 @@ extern PyObject * Server_removeStream(Server *self, int sid);
extern MYFLT * Server_getInputBuffer(Server *self);
extern PyoMidiEvent * Server_getMidiEventBuffer(Server *self);
extern int Server_getMidiEventCount(Server *self);
+extern long Server_getMidiTimeOffset(Server *self);
+extern unsigned long Server_getElapsedTime(Server *self);
extern int Server_getCurrentResamplingFactor(Server *self);
extern int Server_getLastResamplingFactor(Server *self);
extern int Server_generateSeed(Server *self, int oid);
diff --git a/pyolib/_widgets.py b/pyolib/_widgets.py
index a0b9dad..6f2f348 100644
--- a/pyolib/_widgets.py
+++ b/pyolib/_widgets.py
@@ -387,21 +387,23 @@ def createExprEditorWindow(object, title, wxnoserver=False):
EXPREDITORWINDOWS.append([object, title])
def createServerGUI(nchnls, start, stop, recstart, recstop, setAmp, started,
- locals, shutdown, meter, timer, amp, exit):
+ locals, shutdown, meter, timer, amp, exit, title):
"Creates the server's GUI."
global X, Y, MAX_X, NEXT_Y
+ if title is None:
+ title = "Pyo Server"
if not PYO_USE_WX:
createRootWindow()
win = tkCreateToplevelWindow()
f = ServerGUI(win, nchnls, start, stop, recstart, recstop, setAmp,
started, locals, shutdown, meter, timer, amp)
- f.master.title("pyo server")
+ f.master.title(title)
f.focus_set()
else:
win = createRootWindow()
f = ServerGUI(None, nchnls, start, stop, recstart, recstop, setAmp,
started, locals, shutdown, meter, timer, amp, exit)
- f.SetTitle("pyo server")
+ f.SetTitle(title)
f.SetPosition((30, 30))
f.Show()
X, Y = (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X) - 50,
diff --git a/pyolib/_wxwidgets.py b/pyolib/_wxwidgets.py
index 68d55c8..e9ecb04 100644
--- a/pyolib/_wxwidgets.py
+++ b/pyolib/_wxwidgets.py
@@ -74,9 +74,6 @@ def GetRoundBitmap( w, h, r ):
b.SetMaskColour(maskColor)
return b
-def GetRoundShape( w, h, r ):
- return wx.RegionFromBitmap( GetRoundBitmap(w,h,r) )
-
class ControlSlider(wx.Panel):
def __init__(self, parent, minvalue, maxvalue, init=None, pos=(0,0), size=(200,16), log=False,
@@ -2793,8 +2790,6 @@ class ServerGUI(wx.Frame):
exit=True):
wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
- self.SetTitle("pyo server")
-
self.menubar = wx.MenuBar()
self.menu = wx.Menu()
self.menu.Append(22999, 'Start/Stop\tCtrl+R', kind=wx.ITEM_NORMAL)
diff --git a/pyolib/listener.py b/pyolib/listener.py
index 3fe6d32..c952aee 100644
--- a/pyolib/listener.py
+++ b/pyolib/listener.py
@@ -113,6 +113,8 @@ class MidiDispatcher(threading.Thread):
Use the `send` method to send midi event to connected devices.
+ Use the `sendx` method to send sysex event to connected devices.
+
:Parent: threading.Thread
:Args:
@@ -184,6 +186,31 @@ class MidiDispatcher(threading.Thread):
status, data1, data2, timestamp, device, lmax = convertArgsToLists(status, data1, data2, timestamp, device)
[self._dispatcher.send(wrap(status,i), wrap(data1,i), wrap(data2,i), wrap(timestamp,i), wrap(device,i)) for i in range(lmax)]
+ def sendx(self, msg, timestamp=0, device=-1):
+ """
+ Send a MIDI system exclusive message to the selected midi output device.
+
+ Arguments can be list of values to generate multiple events
+ in one call.
+
+ :Args:
+
+ msg: str
+ A valid system exclusive message as a string. The first byte
+ must be 0xf0 and the last one must be 0xf7.
+ timestamp: int, optional
+ The delay time, in milliseconds, before the note
+ is sent on the portmidi stream. A value of 0 means
+ to play the note now. Defaults to 0.
+ device: int, optional
+ The index of the device to which the message will
+ be sent. The default (-1) means all devices. See
+ `getDeviceInfos()` to retrieve device indexes.
+
+ """
+ msg, timestamp, device, lmax = convertArgsToLists(msg, timestamp, device)
+ [self._dispatcher.sendx(wrap(msg,i), wrap(timestamp,i), wrap(device,i)) for i in range(lmax)]
+
def getDeviceInfos(self):
"""
Returns infos about connected midi devices.
diff --git a/pyolib/midi.py b/pyolib/midi.py
index be9e7c3..9112535 100644
--- a/pyolib/midi.py
+++ b/pyolib/midi.py
@@ -161,17 +161,10 @@ class Midictl(PyoObject):
def setInterpolation(self, x):
"""
- Activate/Deactivate interpolation. Off by default.
-
- :Args:
-
- x: boolean
- True activates the interpolation, False deactivates it.
+ Deprecated method. If needed, use Port or SigTo to interpolate between values.
"""
- pyoArgsAssert(self, "b", x)
- x, lmax = convertArgsToLists(x)
- [obj.setInterpolation(wrap(x,i)) for i, obj in enumerate(self._base_objs)]
+ print("setInterpolation() is deprecated. If needed, use Port or SigTo to interpolate between values.")
def ctrl(self, map_list=None, title=None, wxnoserver=False):
self._map_list = [SLMap(0, 127, 'lin', 'ctlnumber', self._ctlnumber, res="int", dataOnly=True),
@@ -796,17 +789,10 @@ class Bendin(PyoObject):
def setInterpolation(self, x):
"""
- Activate/Deactivate interpolation. Off by default.
-
- :Args:
-
- x: boolean
- True activates the interpolation, False deactivates it.
+ Deprecated method. If needed, use Port or SigTo to interpolate between values.
"""
- pyoArgsAssert(self, "b", x)
- x, lmax = convertArgsToLists(x)
- [obj.setInterpolation(wrap(x,i)) for i, obj in enumerate(self._base_objs)]
+ print("setInterpolation() is deprecated. If needed, use Port or SigTo to interpolate between values.")
def ctrl(self, map_list=None, title=None, wxnoserver=False):
self._map_list = [SLMap(0.0, 12.0, 'lin', 'brange', self._brange, dataOnly=True),
@@ -931,17 +917,10 @@ class Touchin(PyoObject):
def setInterpolation(self, x):
"""
- Activate/Deactivate interpolation. Off by default.
-
- :Args:
-
- x: boolean
- True activates the interpolation, False deactivates it.
+ Deprecated method. If needed, use Port or SigTo to interpolate between values.
"""
- pyoArgsAssert(self, "b", x)
- x, lmax = convertArgsToLists(x)
- [obj.setInterpolation(wrap(x,i)) for i, obj in enumerate(self._base_objs)]
+ print("setInterpolation() is deprecated. If needed, use Port or SigTo to interpolate between values.")
def ctrl(self, map_list=None, title=None, wxnoserver=False):
self._map_list = [SLMap(-1.0, 0.0, 'lin', 'minscale', self._minscale, dataOnly=True),
diff --git a/pyolib/pan.py b/pyolib/pan.py
index 683b4ef..ab68e2d 100644
--- a/pyolib/pan.py
+++ b/pyolib/pan.py
@@ -362,6 +362,7 @@ class Selector(PyoObject):
PyoObject.__init__(self, mul, add)
self._inputs = inputs
self._voice = voice
+ self._mode = 0
voice, mul, add, self._lmax = convertArgsToLists(voice, mul, add)
self._length = 1
for obj in self._inputs:
@@ -419,6 +420,24 @@ class Selector(PyoObject):
for i, obj in enumerate(self._base_objs):
obj.setVoice(wrap(x, i // self._length))
+ def setMode(self, x):
+ """
+ Change the algorithm used to interpolate between inputs.
+
+ if inputs are phase correlated you should use a linear fade.
+
+ :Args:
+
+ x: int {0, 1}
+ If 0 (the default) the equal power law is used to
+ interpolate bewtween sources. If 1, linear fade is
+ used instead.
+
+ """
+ pyoArgsAssert(self, "i", x)
+ self._mode = x
+ [obj.setMode(x) for obj in self._base_objs]
+
def ctrl(self, map_list=None, title=None, wxnoserver=False):
self._map_list = [SLMap(0, len(self._inputs)-1, "lin", "voice", self._voice), SLMapMul(self._mul)]
PyoObject.ctrl(self, map_list, title, wxnoserver)
diff --git a/pyolib/server.py b/pyolib/server.py
index 63ea0e5..35891d7 100644
--- a/pyolib/server.py
+++ b/pyolib/server.py
@@ -180,7 +180,7 @@ class Server(object):
if callable(callback):
self._server.setCallback(callback)
- def gui(self, locals=None, meter=True, timer=True, exit=True):
+ def gui(self, locals=None, meter=True, timer=True, exit=True, title=None):
"""
Show the server's user interface.
@@ -199,12 +199,15 @@ class Server(object):
If True, the python interpreter will exit when the 'Quit' button is pressed,
Otherwise, the GUI will be closed leaving the interpreter alive.
Defaults to True.
+ title: str, optional
+ Alternate title for the server window. If None (default), generic
+ title, "Pyo Server" is used.
"""
self._gui_frame, win = createServerGUI(self._nchnls, self.start, self.stop,
self.recstart, self.recstop, self.setAmp,
self.getIsStarted(), locals, self.shutdown,
- meter, timer, self._amp, exit)
+ meter, timer, self._amp, exit, title)
if meter:
self._server.setAmpCallable(self._gui_frame)
if timer:
@@ -938,12 +941,18 @@ class Server(object):
"""
Add a MIDI event in the server processing loop.
- This method can be used to programatically simulate incomming
+ This method can be used to programmatically simulate incoming
MIDI events. In an embedded framework (ie. pyo inside puredata,
openframeworks, etc.), this is useful to control a MIDI-driven
script from the host program. Arguments can be list of values to
generate multiple events in one call.
+ The MIDI event buffer is emptied at the end of each processing
+ block. So, for events to be processed, addMidiEvent should be
+ called at the beginning of the block. If you use audio objects
+ to generate MIDI events, they should be created before the rest
+ of the processing chain.
+
:Args:
status: int
diff --git a/pyolib/tableprocess.py b/pyolib/tableprocess.py
index a897006..844ffd1 100644
--- a/pyolib/tableprocess.py
+++ b/pyolib/tableprocess.py
@@ -817,6 +817,7 @@ class TableRead(PyoObject):
self._freq = freq
self._loop = loop
self._interp = interp
+ self._keeplast = 0
table, freq, loop, interp, mul, add, lmax = convertArgsToLists(table, freq, loop, interp, mul, add)
self._base_objs = [TableRead_base(wrap(table,i), wrap(freq,i), wrap(loop,i), wrap(interp,i), wrap(mul,i), wrap(add,i)) for i in range(lmax)]
self._trig_objs = Dummy([TriggerDummy_base(obj) for obj in self._base_objs])
@@ -881,6 +882,22 @@ class TableRead(PyoObject):
x, lmax = convertArgsToLists(x)
[obj.setInterp(wrap(x,i)) for i, obj in enumerate(self._base_objs)]
+ def setKeepLast(self, x):
+ """
+ If anything but zero, the object will hold the last value when stopped.
+
+ :Args:
+
+ x: bool
+ If 0, the object will be reset to 0 when stopped, otherwise
+ it will hold its last value.
+
+ """
+ pyoArgsAssert(self, "b", x)
+ self._keeplast = x
+ x, lmax = convertArgsToLists(x)
+ [obj.setKeepLast(wrap(x,i)) for i, obj in enumerate(self._base_objs)]
+
def reset(self):
"""
Resets current phase to 0.
diff --git a/src/engine/ad_portaudio.c b/src/engine/ad_portaudio.c
index 5e92703..5d3dc89 100644
--- a/src/engine/ad_portaudio.c
+++ b/src/engine/ad_portaudio.c
@@ -264,7 +264,7 @@ Server_pa_deinit(Server *self)
PaError err;
PyoPaBackendData *be_data = (PyoPaBackendData *) self->audio_be_data;
- if (Pa_IsStreamActive(be_data->stream) || ! Pa_IsStreamStopped(be_data->stream)) {
+ if (!Pa_IsStreamStopped(be_data->stream)) {
self->server_started = 0;
err = Pa_AbortStream(be_data->stream);
portaudio_assert(err, "Pa_AbortStream");
@@ -286,7 +286,7 @@ Server_pa_start(Server *self)
PaError err;
PyoPaBackendData *be_data = (PyoPaBackendData *) self->audio_be_data;
- if (Pa_IsStreamActive(be_data->stream) || ! Pa_IsStreamStopped(be_data->stream)) {
+ if (!Pa_IsStreamStopped(be_data->stream)) {
err = Pa_AbortStream(be_data->stream);
portaudio_assert(err, "Pa_AbortStream");
}
@@ -300,7 +300,7 @@ Server_pa_stop(Server *self)
{
PyoPaBackendData *be_data = (PyoPaBackendData *) self->audio_be_data;
- if (Pa_IsStreamActive(be_data->stream) || ! Pa_IsStreamStopped(be_data->stream)) {
+ if (!Pa_IsStreamStopped(be_data->stream)) {
#ifndef _OSX_
PaError err = Pa_AbortStream(be_data->stream);
portaudio_assert(err, "Pa_AbortStream");
diff --git a/src/engine/md_portmidi.c b/src/engine/md_portmidi.c
index 35a9d36..92cae50 100644
--- a/src/engine/md_portmidi.c
+++ b/src/engine/md_portmidi.c
@@ -89,6 +89,7 @@ Server_pm_init(Server *self)
const PmDeviceInfo *info = Pm_GetDeviceInfo(self->midi_input);
if (info != NULL) {
if (info->input) {
+ Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
pmerr = Pm_OpenInput(&be_data->midiin[0], self->midi_input, NULL, 100, NULL, NULL);
if (pmerr) {
Server_warning(self,
@@ -114,6 +115,7 @@ Server_pm_init(Server *self)
else if (self->midi_input >= num_devices) {
Server_debug(self, "Midi input device : all!\n");
self->midiin_count = 0;
+ Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
for (i=0; i<num_devices; i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
if (info != NULL) {
@@ -146,7 +148,8 @@ Server_pm_init(Server *self)
const PmDeviceInfo *outinfo = Pm_GetDeviceInfo(self->midi_output);
if (outinfo != NULL) {
if (outinfo->output) {
- Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
+ if (!Pt_Started())
+ Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
pmerr = Pm_OpenOutput(&be_data->midiout[0], self->midi_output, NULL, 100, NULL, NULL, 1);
if (pmerr) {
Server_warning(self,
@@ -174,7 +177,8 @@ Server_pm_init(Server *self)
else if (self->midi_output >= num_devices) {
Server_debug(self, "Midi output device : all!\n");
self->midiout_count = 0;
- Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
+ if (!Pt_Started())
+ Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
for (i=0; i<num_devices; i++) {
const PmDeviceInfo *outinfo = Pm_GetDeviceInfo(i);
if (outinfo != NULL) {
@@ -193,8 +197,6 @@ Server_pm_init(Server *self)
}
}
if (self->midiout_count == 0) {
- if (Pt_Started())
- Pt_Stop();
self->withPortMidiOut = 0;
}
}
@@ -204,6 +206,8 @@ Server_pm_init(Server *self)
}
if (self->withPortMidi == 0 && self->withPortMidiOut == 0) {
+ if (Pt_Started())
+ Pt_Stop();
Pm_Terminate();
Server_warning(self, "Portmidi closed.\n");
ret = -1;
@@ -391,6 +395,15 @@ pm_sysexout(Server *self, unsigned char *msg, long timestamp)
}
}
+long
+pm_get_current_time()
+{
+ if (Pt_Started())
+ return Pt_Time();
+ else
+ return 0;
+}
+
/* Query functions. */
PyObject *
diff --git a/src/engine/midilistenermodule.c b/src/engine/midilistenermodule.c
index 6b1a3f1..3c9deac 100644
--- a/src/engine/midilistenermodule.c
+++ b/src/engine/midilistenermodule.c
@@ -77,7 +77,6 @@ void process_midi(PtTimestamp timestamp, void *userData)
} while (result);
PyGILState_Release(s);
- Py_XDECREF(tup);
}
static int
@@ -262,7 +261,7 @@ MidiListener_getDeviceInfos(MidiListener *self) {
PyObject *lst = PyList_New(0);
for (i = 0; i < self->midicount; i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(self->ids[i]);
- str = PyBytes_FromFormat("id: %d, name: %s, interface: %s\n", self->ids[i], info->name, info->interf);
+ str = PyUnicode_FromFormat("id: %d, name: %s, interface: %s\n", self->ids[i], info->name, info->interf);
PyList_Append(lst, str);
}
return lst;
@@ -473,7 +472,8 @@ static PyObject * MidiDispatcher_stop(MidiDispatcher *self) {
PyObject *
MidiDispatcher_send(MidiDispatcher *self, PyObject *args)
{
- int i, status, data1, data2, device, curtime;
+ int i, status, data1, data2, device;
+ long curtime;
PmTimestamp timestamp;
PmEvent buffer[1];
@@ -506,13 +506,47 @@ MidiDispatcher_send(MidiDispatcher *self, PyObject *args)
}
PyObject *
+MidiDispatcher_sendx(MidiDispatcher *self, PyObject *args)
+{
+ unsigned char *msg;
+ int i, size, device;
+ long curtime;
+ PmTimestamp timestamp;
+
+ if (! PyArg_ParseTuple(args, "s#li", &msg, &size, ×tamp, &device))
+ return PyInt_FromLong(-1);
+
+ curtime = Pt_Time();
+ if (device == -1 && self->midicount > 1) {
+ for (i=0; i<self->midicount; i++) {
+ Pm_WriteSysEx(self->midiout[i], curtime + timestamp, msg);
+ }
+ }
+ else if (self->midicount == 1)
+ Pm_WriteSysEx(self->midiout[0], curtime + timestamp, msg);
+ else {
+ for (i=0; i<self->midicount; i++) {
+ if (self->ids[i] == device) {
+ device = i;
+ break;
+ }
+ }
+ if (device < 0 || device >= self->midicount)
+ device = 0;
+ Pm_WriteSysEx(self->midiout[device], curtime + timestamp, msg);
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject *
MidiDispatcher_getDeviceInfos(MidiDispatcher *self) {
int i;
PyObject *str;
PyObject *lst = PyList_New(0);
for (i = 0; i < self->midicount; i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(self->ids[i]);
- str = PyBytes_FromFormat("id: %d, name: %s, interface: %s\n", self->ids[i], info->name, info->interf);
+ str = PyUnicode_FromFormat("id: %d, name: %s, interface: %s\n", self->ids[i], info->name, info->interf);
PyList_Append(lst, str);
}
return lst;
@@ -527,6 +561,7 @@ static PyMethodDef MidiDispatcher_methods[] = {
{"stop", (PyCFunction)MidiDispatcher_stop, METH_NOARGS, "Stops computing."},
{"getDeviceInfos", (PyCFunction)MidiDispatcher_getDeviceInfos, METH_NOARGS, "Returns a list of device infos."},
{"send", (PyCFunction)MidiDispatcher_send, METH_VARARGS, "Send a raw midi event."},
+ {"sendx", (PyCFunction)MidiDispatcher_sendx, METH_VARARGS, "Send a sysex midi event."},
{NULL} /* Sentinel */
};
diff --git a/src/engine/servermodule.c b/src/engine/servermodule.c
index 82a00f7..7823c76 100644
--- a/src/engine/servermodule.c
+++ b/src/engine/servermodule.c
@@ -73,6 +73,7 @@ void pm_programout(Server *self, int value, int chan, long timestamp) {};
void pm_pressout(Server *self, int value, int chan, long timestamp) {};
void pm_bendout(Server *self, int value, int chan, long timestamp) {};
void pm_sysexout(Server *self, unsigned char *msg, long timestamp) {};
+long pm_get_current_time() { return 0; };
#endif
/** Array of Server objects. **/
@@ -350,6 +351,9 @@ Server_process_buffers(Server *server)
*/
PyGILState_STATE s = PyGILState_Ensure();
+ if (server->elapsedSamples == 0)
+ server->midi_time_offset = pm_get_current_time();
+
if (server->CALLBACK != NULL)
PyObject_Call((PyObject *)server->CALLBACK, PyTuple_New(0), NULL);
@@ -583,6 +587,7 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->midi_input = -1;
self->midi_output = -1;
self->midiActive = 1;
+ self->midi_time_offset = 0;
self->amp = self->resetAmp = 1.;
self->currentAmp = self->lastAmp = 1.; // If set to 0, there is a 5ms fadein at server start.
self->withGUI = 0;
@@ -1696,6 +1701,15 @@ Server_getMidiEventCount(Server *self) {
return self->midi_count;
}
+long
+Server_getMidiTimeOffset(Server *self) {
+ return self->midi_time_offset;
+}
+
+unsigned long Server_getElapsedTime(Server *self) {
+ return self->elapsedSamples;
+}
+
static PyObject *
Server_addMidiEvent(Server *self, PyObject *args)
{
diff --git a/src/objects/midimodule.c b/src/objects/midimodule.c
index f5d4f98..be090eb 100644
--- a/src/objects/midimodule.c
+++ b/src/objects/midimodule.c
@@ -423,17 +423,31 @@ PyTypeObject CtlScan2Type = {
CtlScan2_new, /* tp_new */
};
+int
+getPosToWrite(long timestamp, Server *server, double sr, int bufsize)
+{
+ int offset = 0;
+ long realtimestamp, elapsed, ms;
+ realtimestamp = timestamp - Server_getMidiTimeOffset(server);
+ if (realtimestamp < 0)
+ return 0;
+ elapsed = (long)(Server_getElapsedTime(server) / sr * 1000);
+ ms = realtimestamp - (elapsed - (long)(bufsize / sr * 1000));
+ offset = (int)(ms * 0.001 * sr);
+ if (offset < 0)
+ offset = 0;
+ else if (offset >= bufsize)
+ offset = bufsize - 1;
+ return offset;
+}
typedef struct {
pyo_audio_HEAD
int ctlnumber;
int channel;
- int interp;
MYFLT minscale;
MYFLT maxscale;
MYFLT value;
- MYFLT oldValue;
- MYFLT sampleToSec;
int modebuffer[2];
} Midictl;
@@ -484,58 +498,63 @@ Midictl_setProcMode(Midictl *self)
}
}
-// Take MIDI events and translate them...
-void translateMidi(Midictl *self, PyoMidiEvent *buffer, int count)
+int
+Midictl_translateMidi(Midictl *self, PyoMidiEvent *buffer, int j)
{
- int i, ok;
- for (i=0; i<count; i++) {
- int status = PyoMidi_MessageStatus(buffer[i].message); // Temp note event holders
- int number = PyoMidi_MessageData1(buffer[i].message);
- int value = PyoMidi_MessageData2(buffer[i].message);
+ int ok, posto = -1;
+ int status = PyoMidi_MessageStatus(buffer[j].message); // Temp note event holders
+ int number = PyoMidi_MessageData1(buffer[j].message);
+ int value = PyoMidi_MessageData2(buffer[j].message);
- if (self->channel == 0) {
- if ((status & 0xF0) == 0xB0)
- ok = 1;
- else
- ok = 0;
- }
- else {
- if (status == (0xB0 | (self->channel - 1)))
- ok = 1;
- else
- ok = 0;
- }
+ if (self->channel == 0) {
+ if ((status & 0xF0) == 0xB0)
+ ok = 1;
+ else
+ ok = 0;
+ }
+ else {
+ if (status == (0xB0 | (self->channel - 1)))
+ ok = 1;
+ else
+ ok = 0;
+ }
- if (ok == 1 && number == self->ctlnumber) {
- self->value = (value / 127.) * (self->maxscale - self->minscale) + self->minscale;
- break;
- }
+ if (ok == 1 && number == self->ctlnumber) {
+ self->value = (value / 127.) * (self->maxscale - self->minscale) + self->minscale;
+ posto = getPosToWrite(buffer[j].timestamp, (Server *)self->server, self->sr, self->bufsize);
}
- self->oldValue = self->value;
+
+ return posto;
}
static void
Midictl_compute_next_data_frame(Midictl *self)
{
PyoMidiEvent *tmp;
- int i, count;
- MYFLT step;
+ int i, j, count, posto, oldpos = 0;
+ MYFLT oldval = 0.0;
tmp = Server_getMidiEventBuffer((Server *)self->server);
count = Server_getMidiEventCount((Server *)self->server);
- if (count > 0)
- translateMidi((Midictl *)self, tmp, count);
-
- if (self->interp == 0) {
+ if (count == 0) {
for (i=0; i<self->bufsize; i++) {
self->data[i] = self->value;
}
}
else {
- step = (self->value - self->oldValue) / self->bufsize;
- for (i=0; i<self->bufsize; i++) {
- self->data[i] = self->oldValue + step;
+ for (j=0; j<count; j++) {
+ oldval = self->value;
+ posto = Midictl_translateMidi((Midictl *)self, tmp, j);
+ if (posto == -1)
+ continue;
+ for (i=oldpos; i<posto; i++) {
+ self->data[i] = oldval;
+ }
+ oldpos = posto;
+ }
+ for (i=oldpos; i<self->bufsize; i++) {
+ self->data[i] = self->value;
}
}
(*self->muladd_func_ptr)(self);
@@ -573,10 +592,8 @@ Midictl_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->channel = 0;
self->value = 0.;
- self->oldValue = 0.;
self->minscale = 0.;
self->maxscale = 1.;
- self->interp = 0;
self->modebuffer[0] = 0;
self->modebuffer[1] = 0;
@@ -586,7 +603,7 @@ Midictl_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"ctlnumber", "minscale", "maxscale", "init", "channel", "mul", "add", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE_I_FFFIOO, kwlist, &self->ctlnumber, &self->minscale, &self->maxscale, &self->oldValue, &self->channel, &multmp, &addtmp))
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE_I_FFFIOO, kwlist, &self->ctlnumber, &self->minscale, &self->maxscale, &self->value, &self->channel, &multmp, &addtmp))
Py_RETURN_NONE;
if (multmp) {
@@ -599,8 +616,6 @@ Midictl_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyObject_CallMethod(self->server, "addStream", "O", self->stream);
- self->value = self->oldValue;
-
(*self->mode_func_ptr)(self);
return (PyObject *)self;
@@ -626,38 +641,14 @@ static PyObject * Midictl_div(Midictl *self, PyObject *arg) { DIV };
static PyObject * Midictl_inplace_div(Midictl *self, PyObject *arg) { INPLACE_DIV };
static PyObject *
-Midictl_setInterpolation(Midictl *self, PyObject *arg)
-{
- int tmp;
-
- ASSERT_ARG_NOT_NULL
-
- int isNum = PyInt_Check(arg);
-
- if (isNum == 1) {
- tmp = PyInt_AsLong(arg);
- if (tmp == 0)
- self->interp = 0;
- else
- self->interp = 1;
- }
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyObject *
Midictl_setValue(Midictl *self, PyObject *arg)
{
- int tmp;
-
ASSERT_ARG_NOT_NULL
int isNum = PyNumber_Check(arg);
if (isNum == 1) {
- tmp = PyFloat_AsDouble(arg);
- self->oldValue = self->value = tmp;
+ self->value = PyFloat_AsDouble(arg);
}
Py_INCREF(Py_None);
@@ -667,15 +658,12 @@ Midictl_setValue(Midictl *self, PyObject *arg)
static PyObject *
Midictl_setMinScale(Midictl *self, PyObject *arg)
{
- int tmp;
-
ASSERT_ARG_NOT_NULL
int isNum = PyNumber_Check(arg);
if (isNum == 1) {
- tmp = PyFloat_AsDouble(arg);
- self->minscale = tmp;
+ self->minscale = PyFloat_AsDouble(arg);
}
Py_INCREF(Py_None);
@@ -685,15 +673,12 @@ Midictl_setMinScale(Midictl *self, PyObject *arg)
static PyObject *
Midictl_setMaxScale(Midictl *self, PyObject *arg)
{
- int tmp;
-
ASSERT_ARG_NOT_NULL
int isNum = PyNumber_Check(arg);
if (isNum == 1) {
- tmp = PyFloat_AsDouble(arg);
- self->maxscale = tmp;
+ self->maxscale = PyFloat_AsDouble(arg);
}
Py_INCREF(Py_None);
@@ -751,7 +736,6 @@ static PyMethodDef Midictl_methods[] = {
{"_getStream", (PyCFunction)Midictl_getStream, METH_NOARGS, "Returns stream object."},
{"play", (PyCFunction)Midictl_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
{"stop", (PyCFunction)Midictl_stop, METH_NOARGS, "Stops computing."},
- {"setInterpolation", (PyCFunction)Midictl_setInterpolation, METH_O, "Activate/Deactivate interpolation."},
{"setValue", (PyCFunction)Midictl_setValue, METH_O, "Resets audio stream to value in argument."},
{"setMinScale", (PyCFunction)Midictl_setMinScale, METH_O, "Sets the minimum value of scaling."},
{"setMaxScale", (PyCFunction)Midictl_setMaxScale, METH_O, "Sets the maximum value of scaling."},
@@ -851,11 +835,8 @@ typedef struct {
pyo_audio_HEAD
int channel;
int scale; /* 0 = midi, 1 = transpo */
- int interp;
MYFLT range;
MYFLT value;
- MYFLT oldValue;
- MYFLT sampleToSec;
int modebuffer[2];
} Bendin;
@@ -906,61 +887,69 @@ Bendin_setProcMode(Bendin *self)
}
}
-// Take MIDI events and translate them...
-void Bendin_translateMidi(Bendin *self, PyoMidiEvent *buffer, int count)
+int
+Bendin_translateMidi(Bendin *self, PyoMidiEvent *buffer, int j)
{
- int i, ok;
+ int ok, posto = -1;
MYFLT val;
- for (i=0; i<count; i++) {
- int status = PyoMidi_MessageStatus(buffer[i].message); // Temp note event holders
- int number = PyoMidi_MessageData1(buffer[i].message);
- int value = PyoMidi_MessageData2(buffer[i].message);
- if (self->channel == 0) {
- if ((status & 0xF0) == 0xe0)
- ok = 1;
- else
- ok = 0;
- }
- else {
- if (status == (0xe0 | (self->channel - 1)))
- ok = 1;
- else
- ok = 0;
- }
+ int status = PyoMidi_MessageStatus(buffer[j].message); // Temp note event holders
+ int number = PyoMidi_MessageData1(buffer[j].message);
+ int value = PyoMidi_MessageData2(buffer[j].message);
- if (ok == 1) {
- val = (number + (value << 7) - 8192) / 8192.0 * self->range;
- if (self->scale == 0)
- self->value = val;
- else
- self->value = MYPOW(1.0594630943593, val);
- break;
- }
+ if (self->channel == 0) {
+ if ((status & 0xF0) == 0xe0)
+ ok = 1;
+ else
+ ok = 0;
}
- self->oldValue = self->value;
+ else {
+ if (status == (0xe0 | (self->channel - 1)))
+ ok = 1;
+ else
+ ok = 0;
+ }
+
+ if (ok == 1) {
+ val = (number + (value << 7) - 8192) / 8192.0 * self->range;
+ if (self->scale == 0)
+ self->value = val;
+ else
+ self->value = MYPOW(1.0594630943593, val);
+ posto = getPosToWrite(buffer[j].timestamp, (Server *)self->server, self->sr, self->bufsize);
+ }
+
+ return posto;
}
static void
Bendin_compute_next_data_frame(Bendin *self)
{
PyoMidiEvent *tmp;
- int i, count;
+ int i, j, count, posto, oldpos = 0;
+ MYFLT oldval = 0.0;
tmp = Server_getMidiEventBuffer((Server *)self->server);
count = Server_getMidiEventCount((Server *)self->server);
- if (count > 0)
- Bendin_translateMidi((Bendin *)self, tmp, count);
- if (self->interp == 0) {
+ if (count == 0) {
for (i=0; i<self->bufsize; i++) {
self->data[i] = self->value;
}
}
else {
- MYFLT step = (self->value - self->oldValue) / self->bufsize;
- for (i=0; i<self->bufsize; i++) {
- self->data[i] = self->oldValue + step;
+ for (j=0; j<count; j++) {
+ oldval = self->value;
+ posto = Bendin_translateMidi((Bendin *)self, tmp, j);
+ if (posto == -1)
+ continue;
+ for (i=oldpos; i<posto; i++) {
+ self->data[i] = oldval;
+ }
+ oldpos = posto;
+ }
+ for (i=oldpos; i<self->bufsize; i++) {
+ self->data[i] = self->value;
}
}
@@ -999,9 +988,7 @@ Bendin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->channel = 0;
self->scale = 0;
- self->interp = 0;
self->value = 0.;
- self->oldValue = 0.;
self->range = 2.;
self->modebuffer[0] = 0;
self->modebuffer[1] = 0;
@@ -1025,10 +1012,8 @@ Bendin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyObject_CallMethod(self->server, "addStream", "O", self->stream);
- if (self->scale == 0)
- self->oldValue = self->value = 0.;
- else
- self->oldValue = self->value = 1.;
+ if (self->scale == 1)
+ self->value = 1.0;
(*self->mode_func_ptr)(self);
@@ -1113,27 +1098,6 @@ Bendin_setScale(Bendin *self, PyObject *arg)
return Py_None;
}
-static PyObject *
-Bendin_setInterpolation(Bendin *self, PyObject *arg)
-{
- int tmp;
-
- ASSERT_ARG_NOT_NULL
-
- int isNum = PyInt_Check(arg);
-
- if (isNum == 1) {
- tmp = PyInt_AsLong(arg);
- if (tmp == 0)
- self->interp = 0;
- else
- self->interp = 1;
- }
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
static PyMemberDef Bendin_members[] = {
{"server", T_OBJECT_EX, offsetof(Bendin, server), 0, "Pyo server."},
{"stream", T_OBJECT_EX, offsetof(Bendin, stream), 0, "Stream object."},
@@ -1150,7 +1114,6 @@ static PyMethodDef Bendin_methods[] = {
{"setBrange", (PyCFunction)Bendin_setBrange, METH_O, "Sets the bending bipolar range."},
{"setScale", (PyCFunction)Bendin_setScale, METH_O, "Sets the output type, midi vs transpo."},
{"setChannel", (PyCFunction)Bendin_setChannel, METH_O, "Sets the midi channel."},
- {"setInterpolation", (PyCFunction)Bendin_setInterpolation, METH_O, "Activate/Deactivate interpolation."},
{"setMul", (PyCFunction)Bendin_setMul, METH_O, "Sets oscillator mul factor."},
{"setAdd", (PyCFunction)Bendin_setAdd, METH_O, "Sets oscillator add factor."},
{"setSub", (PyCFunction)Bendin_setSub, METH_O, "Sets inverse add factor."},
@@ -1246,10 +1209,7 @@ typedef struct {
int channel;
MYFLT minscale;
MYFLT maxscale;
- int interp;
MYFLT value;
- MYFLT oldValue;
- MYFLT sampleToSec;
int modebuffer[2];
} Touchin;
@@ -1300,56 +1260,62 @@ Touchin_setProcMode(Touchin *self)
}
}
-// Take MIDI events and translate them...
-void Touchin_translateMidi(Touchin *self, PyoMidiEvent *buffer, int count)
+int
+Touchin_translateMidi(Touchin *self, PyoMidiEvent *buffer, int j)
{
- int i, ok;
- for (i=0; i<count; i++) {
- int status = PyoMidi_MessageStatus(buffer[i].message); // Temp note event holders
- int number = PyoMidi_MessageData1(buffer[i].message);
+ int ok, posto = -1;
+ int status = PyoMidi_MessageStatus(buffer[j].message); // Temp note event holders
+ int number = PyoMidi_MessageData1(buffer[j].message);
- if (self->channel == 0) {
- if ((status & 0xF0) == 0xd0)
- ok = 1;
- else
- ok = 0;
- }
- else {
- if (status == (0xd0 | (self->channel - 1)))
- ok = 1;
- else
- ok = 0;
- }
+ if (self->channel == 0) {
+ if ((status & 0xF0) == 0xd0)
+ ok = 1;
+ else
+ ok = 0;
+ }
+ else {
+ if (status == (0xd0 | (self->channel - 1)))
+ ok = 1;
+ else
+ ok = 0;
+ }
- if (ok == 1) {
- self->value = (number / 127.) * (self->maxscale - self->minscale) + self->minscale;
- break;
- }
+ if (ok == 1) {
+ self->value = (number / 127.) * (self->maxscale - self->minscale) + self->minscale;
+ posto = getPosToWrite(buffer[j].timestamp, (Server *)self->server, self->sr, self->bufsize);
}
- self->oldValue = self->value;
+
+ return posto;
}
static void
Touchin_compute_next_data_frame(Touchin *self)
{
PyoMidiEvent *tmp;
- int i, count;
+ int i, j, count, posto, oldpos = 0;
+ MYFLT oldval = 0.0;
tmp = Server_getMidiEventBuffer((Server *)self->server);
count = Server_getMidiEventCount((Server *)self->server);
- if (count > 0)
- Touchin_translateMidi((Touchin *)self, tmp, count);
-
- if (self->interp == 0) {
+ if (count == 0) {
for (i=0; i<self->bufsize; i++) {
self->data[i] = self->value;
}
}
else {
- MYFLT step = (self->value - self->oldValue) / self->bufsize;
- for (i=0; i<self->bufsize; i++) {
- self->data[i] = self->oldValue + step;
+ for (j=0; j<count; j++) {
+ oldval = self->value;
+ posto = Touchin_translateMidi((Touchin *)self, tmp, j);
+ if (posto == -1)
+ continue;
+ for (i=oldpos; i<posto; i++) {
+ self->data[i] = oldval;
+ }
+ oldpos = posto;
+ }
+ for (i=oldpos; i<self->bufsize; i++) {
+ self->data[i] = self->value;
}
}
@@ -1388,10 +1354,8 @@ Touchin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->channel = 0;
self->value = 0.;
- self->oldValue = 0.;
self->minscale = 0.;
self->maxscale = 1.;
- self->interp = 0;
self->modebuffer[0] = 0;
self->modebuffer[1] = 0;
@@ -1401,7 +1365,7 @@ Touchin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"minscale", "maxscale", "init", "channel", "mul", "add", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE__FFFIOO, kwlist, &self->minscale, &self->maxscale, &self->oldValue, &self->channel, &multmp, &addtmp))
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE__FFFIOO, kwlist, &self->minscale, &self->maxscale, &self->value, &self->channel, &multmp, &addtmp))
Py_RETURN_NONE;
if (multmp) {
@@ -1414,8 +1378,6 @@ Touchin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyObject_CallMethod(self->server, "addStream", "O", self->stream);
- self->value = self->oldValue;
-
(*self->mode_func_ptr)(self);
return (PyObject *)self;
@@ -1443,15 +1405,10 @@ static PyObject * Touchin_inplace_div(Touchin *self, PyObject *arg) { INPLACE_DI
static PyObject *
Touchin_setMinScale(Touchin *self, PyObject *arg)
{
- int tmp;
-
ASSERT_ARG_NOT_NULL
- int isNum = PyNumber_Check(arg);
-
- if (isNum == 1) {
- tmp = PyFloat_AsDouble(arg);
- self->minscale = tmp;
+ if (PyNumber_Check(arg) == 1) {
+ self->minscale = PyFloat_AsDouble(arg);
}
Py_INCREF(Py_None);
@@ -1461,15 +1418,10 @@ Touchin_setMinScale(Touchin *self, PyObject *arg)
static PyObject *
Touchin_setMaxScale(Touchin *self, PyObject *arg)
{
- int tmp;
-
ASSERT_ARG_NOT_NULL
- int isNum = PyNumber_Check(arg);
-
- if (isNum == 1) {
- tmp = PyFloat_AsDouble(arg);
- self->maxscale = tmp;
+ if (PyNumber_Check(arg) == 1) {
+ self->maxscale = PyFloat_AsDouble(arg);
}
Py_INCREF(Py_None);
@@ -1483,9 +1435,7 @@ Touchin_setChannel(Touchin *self, PyObject *arg)
ASSERT_ARG_NOT_NULL
- int isInt = PyInt_Check(arg);
-
- if (isInt == 1) {
+ if (PyInt_Check(arg) == 1) {
tmp = PyInt_AsLong(arg);
if (tmp >= 0 && tmp < 128)
self->channel = tmp;
@@ -1495,27 +1445,6 @@ Touchin_setChannel(Touchin *self, PyObject *arg)
return Py_None;
}
-static PyObject *
-Touchin_setInterpolation(Touchin *self, PyObject *arg)
-{
- int tmp;
-
- ASSERT_ARG_NOT_NULL
-
- int isNum = PyInt_Check(arg);
-
- if (isNum == 1) {
- tmp = PyInt_AsLong(arg);
- if (tmp == 0)
- self->interp = 0;
- else
- self->interp = 1;
- }
-
- Py_INCREF(Py_None);
- return Py_None;
-}
-
static PyMemberDef Touchin_members[] = {
{"server", T_OBJECT_EX, offsetof(Touchin, server), 0, "Pyo server."},
{"stream", T_OBJECT_EX, offsetof(Touchin, stream), 0, "Stream object."},
@@ -1532,7 +1461,6 @@ static PyMethodDef Touchin_methods[] = {
{"setMinScale", (PyCFunction)Touchin_setMinScale, METH_O, "Sets the minimum value of scaling."},
{"setMaxScale", (PyCFunction)Touchin_setMaxScale, METH_O, "Sets the maximum value of scaling."},
{"setChannel", (PyCFunction)Touchin_setChannel, METH_O, "Sets the midi channel."},
- {"setInterpolation", (PyCFunction)Touchin_setInterpolation, METH_O, "Activate/Deactivate interpolation."},
{"setMul", (PyCFunction)Touchin_setMul, METH_O, "Sets oscillator mul factor."},
{"setAdd", (PyCFunction)Touchin_setAdd, METH_O, "Sets oscillator add factor."},
{"setSub", (PyCFunction)Touchin_setSub, METH_O, "Sets inverse add factor."},
@@ -1928,7 +1856,7 @@ PyTypeObject PrograminType = {
typedef struct {
pyo_audio_HEAD
- int *notebuf; /* pitch, velocity, ... */
+ int *notebuf; /* pitch, velocity, posToWrite */
int voices;
int vcount;
int scale; /* 0 = midi, 1 = hertz, 2 = transpo */
@@ -1948,7 +1876,7 @@ pitchIsIn(int *buf, int pitch, int len) {
int i;
int isIn = 0;
for (i=0; i<len; i++) {
- if (buf[i*2] == pitch) {
+ if (buf[i*3] == pitch) {
isIn = 1;
break;
}
@@ -1961,7 +1889,7 @@ int firstEmpty(int *buf, int len) {
int i;
int voice = -1;
for (i=0; i<len; i++) {
- if (buf[i*2+1] == 0) {
+ if (buf[i*3+1] == 0) {
voice = i;
break;
}
@@ -1974,7 +1902,7 @@ int nextEmptyVoice(int *buf, int voice, int len) {
int next = -1;
for (i=1; i<=len; i++) {
tmp = (i + voice) % len;
- if (buf[tmp*2+1] == 0) {
+ if (buf[tmp*3+1] == 0) {
next = tmp;
break;
}
@@ -1986,7 +1914,7 @@ int whichVoice(int *buf, int pitch, int len) {
int i;
int voice = 0;
for (i=0; i<len; i++) {
- if (buf[i*2] == pitch) {
+ if (buf[i*3] == pitch) {
voice = i;
break;
}
@@ -1997,7 +1925,7 @@ int whichVoice(int *buf, int pitch, int len) {
// Take MIDI events and keep track of notes
void grabMidiNotes(MidiNote *self, PyoMidiEvent *buffer, int count)
{
- int i, ok, voice, kind;
+ int i, ok, voice, kind, samp = 0;
for (i=0; i<count; i++) {
int status = PyoMidi_MessageStatus(buffer[i].message); // Temp note event holders
@@ -2018,6 +1946,8 @@ void grabMidiNotes(MidiNote *self, PyoMidiEvent *buffer, int count)
}
if (ok == 1) {
+ samp = getPosToWrite(buffer[i].timestamp, (Server *)self->server, self->sr, self->bufsize);
+
if ((status & 0xF0) == 0x80)
kind = 0;
else if ((status & 0xF0) == 0x90 && velocity == 0)
@@ -2031,24 +1961,27 @@ void grabMidiNotes(MidiNote *self, PyoMidiEvent *buffer, int count)
voice = nextEmptyVoice(self->notebuf, self->vcount, self->voices);
if (voice != -1) {
self->vcount = voice;
- self->notebuf[voice*2] = pitch;
- self->notebuf[voice*2+1] = velocity;
- self->trigger_streams[self->bufsize*(self->vcount*2)] = 1.0;
+ self->notebuf[voice*3] = pitch;
+ self->notebuf[voice*3+1] = velocity;
+ self->notebuf[voice*3+2] = samp;
+ self->trigger_streams[self->bufsize*(self->vcount*2)+samp] = 1.0;
}
}
else {
self->vcount = (self->vcount + 1) % self->voices;
- self->notebuf[self->vcount*2] = pitch;
- self->notebuf[self->vcount*2+1] = velocity;
- self->trigger_streams[self->bufsize*(self->vcount*2)] = 1.0;
+ self->notebuf[self->vcount*3] = pitch;
+ self->notebuf[self->vcount*3+1] = velocity;
+ self->notebuf[self->vcount*3+2] = samp;
+ self->trigger_streams[self->bufsize*(self->vcount*2)+samp] = 1.0;
}
}
else if (pitchIsIn(self->notebuf, pitch, self->voices) == 1 && kind == 0 && pitch >= self->first && pitch <= self->last) {
//PySys_WriteStdout("%i, %i, %i\n", status, pitch, velocity);
voice = whichVoice(self->notebuf, pitch, self->voices);
- self->notebuf[voice*2] = -1;
- self->notebuf[voice*2+1] = 0.;
- self->trigger_streams[self->bufsize*(voice*2+1)] = 1.0;
+ self->notebuf[voice*3] = -1;
+ self->notebuf[voice*3+1] = 0;
+ self->notebuf[voice*3+2] = samp;
+ self->trigger_streams[self->bufsize*(voice*2+1)+samp] = 1.0;
}
}
}
@@ -2127,7 +2060,7 @@ MidiNote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyObject_CallMethod(self->server, "addStream", "O", self->stream);
- self->notebuf = (int *)realloc(self->notebuf, self->voices * 2 * sizeof(int));
+ self->notebuf = (int *)realloc(self->notebuf, self->voices * 3 * sizeof(int));
self->trigger_streams = (MYFLT *)realloc(self->trigger_streams, self->bufsize * self->voices * 2 * sizeof(MYFLT));
for (i=0; i<self->bufsize*self->voices*2; i++) {
@@ -2135,8 +2068,9 @@ MidiNote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}
for (i=0; i<self->voices; i++) {
- self->notebuf[i*2] = -1;
- self->notebuf[i*2+1] = 0;
+ self->notebuf[i*3] = -1;
+ self->notebuf[i*3+1] = 0;
+ self->notebuf[i*3+2] = 0;
}
self->centralkey = (self->first + self->last) / 2;
@@ -2146,10 +2080,11 @@ MidiNote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)self;
}
-MYFLT MidiNote_getValue(MidiNote *self, int voice, int which)
+MYFLT
+MidiNote_getValue(MidiNote *self, int voice, int which, int *posto)
{
MYFLT val = -1.0;
- int midival = self->notebuf[voice*2+which];
+ int midival = self->notebuf[voice*3+which];
if (which == 0 && midival != -1) {
if (self->scale == 0)
val = midival;
@@ -2162,6 +2097,9 @@ MYFLT MidiNote_getValue(MidiNote *self, int voice, int which)
val = (MYFLT)midival;
else if (which == 1)
val = (MYFLT)midival / 127.;
+
+ *posto = self->notebuf[voice*3+2];
+
return val;
}
@@ -2325,6 +2263,8 @@ typedef struct {
int modebuffer[2];
int voice;
int mode; /* 0 = pitch, 1 = velocity */
+ MYFLT lastval;
+ MYFLT lastpitch;
} Notein;
static void Notein_postprocessing_ii(Notein *self) { POST_PROCESSING_II };
@@ -2377,19 +2317,43 @@ Notein_setProcMode(Notein *self)
static void
Notein_compute_next_data_frame(Notein *self)
{
- int i;
- MYFLT tmp = MidiNote_getValue(self->handler, self->voice, self->mode);
+ int i, posto;
+ MYFLT tmp = MidiNote_getValue(self->handler, self->voice, self->mode, &posto);
- if (self->mode == 0 && tmp != -1) {
- for (i=0; i<self->bufsize; i++) {
- self->data[i] = tmp;
+ if (self->lastval == tmp) {
+ if (self->mode == 0 && tmp != -1) {
+ for (i=0; i<self->bufsize; i++) {
+ self->data[i] = tmp;
+ }
+ }
+ else if (self->mode == 1) {
+ for (i=0; i<self->bufsize; i++) {
+ self->data[i] = tmp;
+ }
+ (*self->muladd_func_ptr)(self);
}
}
- else if (self->mode == 1) {
- for (i=0; i<self->bufsize; i++) {
- self->data[i] = tmp;
+ else { /* There is a new note to compute. */
+ if (self->mode == 0 && tmp != -1) {
+ for (i=0; i<self->bufsize; i++) {
+ if (i < posto)
+ self->data[i] = self->lastpitch;
+ else
+ self->data[i] = tmp;
+ }
+ }
+ else if (self->mode == 1) {
+ for (i=0; i<self->bufsize; i++) {
+ if (i < posto)
+ self->data[i] = self->lastval;
+ else
+ self->data[i] = tmp;
+ }
+ (*self->muladd_func_ptr)(self);
}
- (*self->muladd_func_ptr)(self);
+ self->lastval = tmp;
+ if (tmp != -1)
+ self->lastpitch = tmp;
}
}
@@ -2427,6 +2391,8 @@ Notein_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->voice = 0;
self->mode = 0;
+ self->lastval = -1.0;
+ self->lastpitch = 0.0;
self->modebuffer[0] = 0;
self->modebuffer[1] = 0;
diff --git a/src/objects/oscilmodule.c b/src/objects/oscilmodule.c
index 3041548..8a3ab21 100644
--- a/src/objects/oscilmodule.c
+++ b/src/objects/oscilmodule.c
@@ -5809,6 +5809,8 @@ typedef struct {
int go;
int modebuffer[3];
double pointerPos;
+ MYFLT lastValue;
+ int keepLast;
MYFLT *trigsBuffer;
TriggerStream *trig_stream;
int init;
@@ -5848,10 +5850,14 @@ TableRead_readframes_i(TableRead *self) {
if (self->go == 1) {
ipart = (int)self->pointerPos;
fpart = self->pointerPos - ipart;
- self->data[i] = (*self->interp_func_ptr)(tablelist, ipart, fpart, size);
+ self->lastValue = self->data[i] = (*self->interp_func_ptr)(tablelist, ipart, fpart, size);
+ }
+ else {
+ if (self->keepLast == 0)
+ self->data[i] = 0.0;
+ else
+ self->data[i] = self->lastValue;
}
- else
- self->data[i] = 0.0;
self->pointerPos += inc;
}
@@ -5890,10 +5896,14 @@ TableRead_readframes_a(TableRead *self) {
if (self->go == 1) {
ipart = (int)self->pointerPos;
fpart = self->pointerPos - ipart;
- self->data[i] = (*self->interp_func_ptr)(tablelist, ipart, fpart, size);
+ self->lastValue = self->data[i] = (*self->interp_func_ptr)(tablelist, ipart, fpart, size);
+ }
+ else {
+ if (self->keepLast == 0)
+ self->data[i] = 0.0;
+ else
+ self->data[i] = self->lastValue;
}
- else
- self->data[i] = 0.0;
inc = fr[i] * sizeOnSr;
self->pointerPos += inc;
@@ -6006,6 +6016,8 @@ TableRead_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->freq = PyFloat_FromDouble(1);
self->loop = 0;
self->init = 1;
+ self->keepLast = 0;
+ self->lastValue = 0.0;
self->modebuffer[0] = 0;
self->modebuffer[1] = 0;
self->modebuffer[2] = 0;
@@ -6088,8 +6100,24 @@ static PyObject * TableRead_out(TableRead *self, PyObject *args, PyObject *kwds)
};
static PyObject * TableRead_stop(TableRead *self)
{
+ int i;
self->go = 0;
- STOP
+ Stream_setStreamActive(self->stream, 0);
+ Stream_setStreamChnl(self->stream, 0);
+ Stream_setStreamToDac(self->stream, 0);
+ if (self->keepLast == 0) {
+ for (i=0; i<self->bufsize; i++) {
+ self->data[i] = 0;
+ }
+ }
+ else {
+ for (i=0; i<self->bufsize; i++) {
+ self->data[i] = self->lastValue;
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
};
static PyObject * TableRead_multiply(TableRead *self, PyObject *arg) { MULTIPLY };
@@ -6183,6 +6211,17 @@ TableRead_setInterp(TableRead *self, PyObject *arg)
}
static PyObject *
+TableRead_setKeepLast(TableRead *self, PyObject *arg)
+{
+ ASSERT_ARG_NOT_NULL
+
+ self->keepLast = PyInt_AsLong(arg);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
TableRead_reset(TableRead *self)
{
self->pointerPos = 0.0;
@@ -6213,6 +6252,7 @@ static PyMethodDef TableRead_methods[] = {
{"setFreq", (PyCFunction)TableRead_setFreq, METH_O, "Sets oscillator frequency in cycle per second."},
{"setLoop", (PyCFunction)TableRead_setLoop, METH_O, "Sets the looping mode."},
{"setInterp", (PyCFunction)TableRead_setInterp, METH_O, "Sets reader interpolation mode."},
+{"setKeepLast", (PyCFunction)TableRead_setKeepLast, METH_O, "Sets keepLast mode."},
{"reset", (PyCFunction)TableRead_reset, METH_NOARGS, "Resets pointer position to 0."},
{"setMul", (PyCFunction)TableRead_setMul, METH_O, "Sets oscillator mul factor."},
{"setAdd", (PyCFunction)TableRead_setAdd, METH_O, "Sets oscillator add factor."},
diff --git a/src/objects/panmodule.c b/src/objects/panmodule.c
index df741b3..4ace883 100644
--- a/src/objects/panmodule.c
+++ b/src/objects/panmodule.c
@@ -2797,6 +2797,7 @@ typedef struct {
PyObject *voice;
Stream *voice_stream;
int chSize;
+ int mode;
int modebuffer[3]; // need at least 2 slots for mul & add
} Selector;
@@ -2836,6 +2837,27 @@ Selector_generate_i(Selector *self) {
}
static void
+Selector_generate_lin_i(Selector *self) {
+ int j1, j, i;
+ MYFLT voice = Selector_clip_voice(self, PyFloat_AS_DOUBLE(self->voice));
+
+ j1 = (int)voice;
+ j = j1 + 1;
+ if (j1 >= (self->chSize-1)) {
+ j1--; j--;
+ }
+
+ MYFLT *st1 = Stream_getData((Stream *)PyObject_CallMethod((PyObject *)PyList_GET_ITEM(self->inputs, j1), "_getStream", NULL));
+ MYFLT *st2 = Stream_getData((Stream *)PyObject_CallMethod((PyObject *)PyList_GET_ITEM(self->inputs, j), "_getStream", NULL));
+
+ voice = P_clip(voice - j1);
+
+ for (i=0; i<self->bufsize; i++) {
+ self->data[i] = st1[i] * (1.0 - voice) + st2[i] * voice;
+ }
+}
+
+static void
Selector_generate_a(Selector *self) {
int old_j1, old_j, j1, j, i;
MYFLT voice;
@@ -2870,6 +2892,41 @@ Selector_generate_a(Selector *self) {
}
}
+static void
+Selector_generate_lin_a(Selector *self) {
+ int old_j1, old_j, j1, j, i;
+ MYFLT voice;
+ MYFLT *st1, *st2;
+ MYFLT *vc = Stream_getData((Stream *)self->voice_stream);
+
+ old_j1 = 0;
+ old_j = 1;
+ st1 = Stream_getData((Stream *)PyObject_CallMethod((PyObject *)PyList_GET_ITEM(self->inputs, old_j1), "_getStream", NULL));
+ st2 = Stream_getData((Stream *)PyObject_CallMethod((PyObject *)PyList_GET_ITEM(self->inputs, old_j), "_getStream", NULL));
+
+ for (i=0; i<self->bufsize; i++) {
+ voice = Selector_clip_voice(self, vc[i]);
+
+ j1 = (int)voice;
+ j = j1 + 1;
+ if (j1 >= (self->chSize-1)) {
+ j1--; j--;
+ }
+ if (j1 != old_j1) {
+ st1 = Stream_getData((Stream *)PyObject_CallMethod((PyObject *)PyList_GET_ITEM(self->inputs, j1), "_getStream", NULL));
+ old_j1 = j1;
+ }
+ if (j != old_j) {
+ st2 = Stream_getData((Stream *)PyObject_CallMethod((PyObject *)PyList_GET_ITEM(self->inputs, j), "_getStream", NULL));
+ old_j = j;
+ }
+
+ voice = P_clip(voice - j1);
+
+ self->data[i] = st1[i] * (1.0 - voice) + st2[i] * voice;
+ }
+}
+
static void Selector_postprocessing_ii(Selector *self) { POST_PROCESSING_II };
static void Selector_postprocessing_ai(Selector *self) { POST_PROCESSING_AI };
static void Selector_postprocessing_ia(Selector *self) { POST_PROCESSING_IA };
@@ -2889,10 +2946,16 @@ Selector_setProcMode(Selector *self)
switch (procmode) {
case 0:
- self->proc_func_ptr = Selector_generate_i;
+ if (self->mode == 0)
+ self->proc_func_ptr = Selector_generate_i;
+ else
+ self->proc_func_ptr = Selector_generate_lin_i;
break;
case 1:
- self->proc_func_ptr = Selector_generate_a;
+ if (self->mode == 0)
+ self->proc_func_ptr = Selector_generate_a;
+ else
+ self->proc_func_ptr = Selector_generate_lin_a;
break;
}
switch (muladdmode) {
@@ -2970,6 +3033,7 @@ Selector_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self = (Selector *)type->tp_alloc(type, 0);
self->voice = PyFloat_FromDouble(0.);
+ self->mode = 0;
self->modebuffer[0] = 0;
self->modebuffer[1] = 0;
self->modebuffer[2] = 0;
@@ -3078,6 +3142,21 @@ Selector_setVoice(Selector *self, PyObject *arg)
return Py_None;
}
+static PyObject *
+Selector_setMode(Selector *self, PyObject *arg)
+{
+ ASSERT_ARG_NOT_NULL
+
+ if (PyInt_Check(arg) == 1) {
+ self->mode = PyLong_AsLong(arg);
+ }
+
+ (*self->mode_func_ptr)(self);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
static PyMemberDef Selector_members[] = {
{"server", T_OBJECT_EX, offsetof(Selector, server), 0, "Pyo server."},
{"stream", T_OBJECT_EX, offsetof(Selector, stream), 0, "Stream object."},
@@ -3096,6 +3175,7 @@ static PyMethodDef Selector_methods[] = {
{"stop", (PyCFunction)Selector_stop, METH_NOARGS, "Stops computing."},
{"setInputs", (PyCFunction)Selector_setInputs, METH_O, "Sets list of input streams."},
{"setVoice", (PyCFunction)Selector_setVoice, METH_O, "Sets voice position pointer."},
+{"setMode", (PyCFunction)Selector_setMode, METH_O, "Sets interpolation algorithm."},
{"setMul", (PyCFunction)Selector_setMul, METH_O, "Sets mul factor."},
{"setAdd", (PyCFunction)Selector_setAdd, METH_O, "Sets add factor."},
{"setSub", (PyCFunction)Selector_setSub, METH_O, "Sets inverse add factor."},
diff --git a/src/objects/sfplayermodule.c b/src/objects/sfplayermodule.c
index 0dee321..1a14894 100644
--- a/src/objects/sfplayermodule.c
+++ b/src/objects/sfplayermodule.c
@@ -986,8 +986,8 @@ SfMarkerShuffler_chooseNewMark(SfMarkerShuffler *self, int dir)
if (dir == 1) {
if (self->startPos == -1) {
mark = (int)(self->markers_size * RANDOM_UNIFORM);
- self->startPos = self->markers[mark] * self->srScale;
- self->endPos = self->markers[mark+1] * self->srScale;
+ self->startPos = self->markers[mark];
+ self->endPos = self->markers[mark+1];
}
else {
self->startPos = self->nextStartPos;
@@ -995,14 +995,14 @@ SfMarkerShuffler_chooseNewMark(SfMarkerShuffler *self, int dir)
}
mark = (int)(self->markers_size * RANDOM_UNIFORM);
- self->nextStartPos = self->markers[mark] * self->srScale;
- self->nextEndPos = self->markers[mark+1] * self->srScale;
+ self->nextStartPos = self->markers[mark];
+ self->nextEndPos = self->markers[mark+1];
}
else {
if (self->startPos == -1) {
mark = self->markers_size - (int)(self->markers_size * RANDOM_UNIFORM);
- self->startPos = self->markers[mark] * self->srScale;
- self->endPos = self->markers[mark-1] * self->srScale;
+ self->startPos = self->markers[mark];
+ self->endPos = self->markers[mark-1];
}
else {
self->startPos = self->nextStartPos;
@@ -1010,8 +1010,8 @@ SfMarkerShuffler_chooseNewMark(SfMarkerShuffler *self, int dir)
}
mark = self->markers_size - (int)(self->markers_size * RANDOM_UNIFORM);
- self->nextStartPos = self->markers[mark] * self->srScale;
- self->nextEndPos = self->markers[mark-1] * self->srScale;
+ self->nextStartPos = self->markers[mark];
+ self->nextEndPos = self->markers[mark-1];
}
}
@@ -1723,28 +1723,28 @@ SfMarkerLooper_chooseNewMark(SfMarkerLooper *self, int dir)
if (dir == 1) {
if (self->startPos == -1) {
- self->startPos = self->markers[mark] * self->srScale;
- self->endPos = self->markers[mark+1] * self->srScale;
+ self->startPos = self->markers[mark];
+ self->endPos = self->markers[mark+1];
}
else {
self->startPos = self->nextStartPos;
self->endPos = self->nextEndPos;
}
- self->nextStartPos = self->markers[mark] * self->srScale;
- self->nextEndPos = self->markers[mark+1] * self->srScale;
+ self->nextStartPos = self->markers[mark];
+ self->nextEndPos = self->markers[mark+1];
}
else {
mark = self->markers_size - mark;
if (self->startPos == -1) {
- self->startPos = self->markers[mark] * self->srScale;
- self->endPos = self->markers[mark-1] * self->srScale;
+ self->startPos = self->markers[mark];
+ self->endPos = self->markers[mark-1];
}
else {
self->startPos = self->nextStartPos;
self->endPos = self->nextEndPos;
}
- self->nextStartPos = self->markers[mark] * self->srScale;
- self->nextEndPos = self->markers[mark-1] * self->srScale;
+ self->nextStartPos = self->markers[mark];
+ self->nextEndPos = self->markers[mark-1];
}
}
--
python-pyo packaging
More information about the pkg-multimedia-commits
mailing list