[SCM] python-pyo/master: New release: 0.8.4.

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 abf21f925615014c94e9ffd3c61db46bf1f118be
Author: Tiago Bortoletto Vaz <tiago at debian.org>
Date:   Tue Mar 28 12:19:35 2017 -0400

    New release: 0.8.4.

diff --git a/ChangeLog b/ChangeLog
index 6a5832f..c55041e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2017-03-24 belangeo <belangeo at gmail.com>
+
+    * Upgraded version number to 0.8.4.
+
+2017-03-23 belangeo <belangeo at gmail.com>
+
+    * Added `id` and `object` attributes to wxgui's object events.
+
+2017-03-22 belangeo <belangeo at gmail.com>
+
+    * Added setJackInputPortNames and setJackOutputPortNames methods to the
+      Server object. This allow the user to rename jack input/output ports.
+
+2017-03-21 belangeo <belangeo at gmail.com>
+
+    * Updated portaudio interface to make it much more secure.
+
+2017-03-20 belangeo <belangeo at gmail.com>
+
+    * Added setIsJackTransportSlave method to Server object (it allows to
+      start/stop the Server from jack transport).
+
+2017-03-17 belangeo <belangeo at gmail.com>
+
+    * Fixed GIL conflicts with portaudio, portmidi and jack library calls.
+
 2017-03-11 belangeo <belangeo at gmail.com>
 
     * Added a setKeepLast method to TableRead object (will hold last value).
@@ -30,6 +56,11 @@
 
     * Added examples about multicore audio programming with pyo.
 
+2017-02-14 belangeo <belangeo at gmail.com>
+
+    * Fixed segfault in MidiListener callback function with python3.
+    * Fixed string versus unicode in MidiDispatcher's getDeviceInfos method.
+
 2017-02-13 belangeo <belangeo at gmail.com>
 
     * Final revision for version 0.8.3.
diff --git a/TODO.md b/TODO.md
index 6a0fe34..c0942fc 100644
--- a/TODO.md
+++ b/TODO.md
@@ -22,7 +22,6 @@ Server
 - Remove, if possible, PyGILState_Ensure/PyGILState_Release from 
   the process_buffers function.
 
-
 Examples
 --------
 
@@ -37,7 +36,9 @@ Objects
   Where `inputs` are a list of trigger objects and `values` (list of floats) 
   the corresponding values to output depending which trigger has been detected.
   A trigger from the second object will make the object output the second value
-  from the list. 
+  from the list.
+
+- Added random distribution in SfMarkerShuffler.
 
 MIDI
 ----
diff --git a/debian/changelog b/debian/changelog
index 58547a2..c8d1062 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-pyo (0.8.4-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Tiago Bortoletto Vaz <tiago at debian.org>  Tue, 28 Mar 2017 12:16:45 -0400
+
 python-pyo (0.8.3+git20170311.01-1) unstable; urgency=medium
 
   * Added a setKeepLast method to TableRead object (will hold last value).
diff --git a/doc-sphinx/source/index.rst b/doc-sphinx/source/index.rst
index 7cb051b..936df77 100644
--- a/doc-sphinx/source/index.rst
+++ b/doc-sphinx/source/index.rst
@@ -3,7 +3,7 @@
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
-Welcome to the pyo 0.8.3 documentation
+Welcome to the pyo 0.8.4 documentation
 ===================================================
 
 .. image:: E-PyoIcon.png
diff --git a/examples/algorithmic/03_melody_algo.py b/examples/algorithmic/03_melody_algo.py
index c989dc3..bdda065 100644
--- a/examples/algorithmic/03_melody_algo.py
+++ b/examples/algorithmic/03_melody_algo.py
@@ -7,7 +7,7 @@ The Melo class records 16 notes in a table and reads it at variable speed.
 from pyo import *
 import random
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 SCALES = [[0,2,5,7,9,11], [0,2,3,7,8,11], [0,3,5,7,8,10]]
 
diff --git a/examples/algorithmic/04_drum_machine.py b/examples/algorithmic/04_drum_machine.py
index bff2b6a..3b52f29 100644
--- a/examples/algorithmic/04_drum_machine.py
+++ b/examples/algorithmic/04_drum_machine.py
@@ -25,7 +25,7 @@ presets = [[[16, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0],
             [16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0],
             [16, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1]]]
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 tm = Xnoise(dist=9, freq=2.34, x1=.5, x2=3, mul=.0025, add=.12)
 b = Beat(time=tm, w1=[90,30,30,20], w2=[30,90,50,40], w3=[0,30,30,40])
diff --git a/examples/control/03_table_grapher.py b/examples/control/03_table_grapher.py
index 5e9b05c..f33128f 100644
--- a/examples/control/03_table_grapher.py
+++ b/examples/control/03_table_grapher.py
@@ -18,7 +18,7 @@ http://www.wxpython.org/
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 NOTE_DUR = 2
 
diff --git a/examples/control/04_set_example.py b/examples/control/04_set_example.py
index 8e86ab9..dd303f3 100644
--- a/examples/control/04_set_example.py
+++ b/examples/control/04_set_example.py
@@ -9,7 +9,7 @@ call reset() to get back initial values.
 from pyo import *
 from random import uniform
 
-s = Server(sr=44100, nchnls=2, buffersize=1024, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 a = FM(carrier=[uniform(197,203) for i in range(10)],
        ratio=[uniform(0.49,0.51) for i in range(10)],
diff --git a/examples/control/05_mixer.py b/examples/control/05_mixer.py
index 108ea2b..4cf4f52 100644
--- a/examples/control/05_mixer.py
+++ b/examples/control/05_mixer.py
@@ -7,7 +7,7 @@ Mixing multiple inputs to multiple outputs with fade time.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 # Inputs
 a = SfPlayer("../snds/ounkmaster.aif", loop=True, mul=.3)
diff --git a/examples/control/06_lfo_controls.py b/examples/control/06_lfo_controls.py
index 3a92ca2..893363c 100644
--- a/examples/control/06_lfo_controls.py
+++ b/examples/control/06_lfo_controls.py
@@ -6,7 +6,7 @@ Audio control with LFOs.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 # LFO (sine wave) +/- 5 (mul) around 10 (add), range = 5 -> 15.
 # Control the frequency of the square wave LFO.
diff --git a/examples/effects/01_flanger.py b/examples/effects/01_flanger.py
index 860f5d6..c77d411 100644
--- a/examples/effects/01_flanger.py
+++ b/examples/effects/01_flanger.py
@@ -6,7 +6,7 @@ Simple flanger.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 #-->
 src = BrownNoise(.1).mix(2).out()
diff --git a/examples/effects/02_chorus.py b/examples/effects/02_chorus.py
index 90fff48..cd592ed 100644
--- a/examples/effects/02_chorus.py
+++ b/examples/effects/02_chorus.py
@@ -6,7 +6,7 @@ Hand-written 8 delay lines chorus.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 #--> Start a sound
 sf = SfPlayer('../snds/baseballmajeur_m.aif', speed=1, loop=True, mul=.3)
diff --git a/examples/effects/03_detuned_waveguides.py b/examples/effects/03_detuned_waveguides.py
index 2a73e27..ec294c3 100644
--- a/examples/effects/03_detuned_waveguides.py
+++ b/examples/effects/03_detuned_waveguides.py
@@ -7,7 +7,7 @@ Detuned waveguide bank.
 from pyo import *
 import random
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 src = SfPlayer("../snds/ounkmaster.aif", loop=True, mul=.1)
 
diff --git a/examples/effects/04_harmonizer.py b/examples/effects/04_harmonizer.py
index 43676fc..3082d67 100644
--- a/examples/effects/04_harmonizer.py
+++ b/examples/effects/04_harmonizer.py
@@ -6,7 +6,7 @@ Hand-written harmonizer.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 sf = SfPlayer('../snds/flute.aif', speed=1, loop=True, mul=.5).out()
 
diff --git a/examples/fft/07_fft_stretch.py b/examples/fft/07_fft_stretch.py
index 5bcfbad..8b3e9c7 100644
--- a/examples/fft/07_fft_stretch.py
+++ b/examples/fft/07_fft_stretch.py
@@ -7,7 +7,7 @@ Time stretching using FFT/IFFT.
 from __future__ import division
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 # Settings #
 snd = '../snds/flute.aif'
diff --git a/examples/matrix/01_wave_terrain.py b/examples/matrix/01_wave_terrain.py
index fbe0d24..d9fee03 100644
--- a/examples/matrix/01_wave_terrain.py
+++ b/examples/matrix/01_wave_terrain.py
@@ -7,7 +7,7 @@ Simple wave terrain synthesis. The terrain is generated with sin functions.
 from pyo import *
 import random, math
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 def terrain(size=256, freq=1, phase=16):
     l = []
diff --git a/examples/matrix/02_matrix_record.py b/examples/matrix/02_matrix_record.py
index 5c2c6c8..7acd679 100644
--- a/examples/matrix/02_matrix_record.py
+++ b/examples/matrix/02_matrix_record.py
@@ -7,7 +7,7 @@ Wave terrain synthesis of a live recording of FM synthesis in the matrix.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 SIZE = 256
 mm = NewMatrix(SIZE, SIZE)
diff --git a/examples/matrix/03_matrix_algo.py b/examples/matrix/03_matrix_algo.py
index 5b9d1cc..6877382 100644
--- a/examples/matrix/03_matrix_algo.py
+++ b/examples/matrix/03_matrix_algo.py
@@ -7,7 +7,7 @@ algorithmic generation of notes.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 mat = [[36,41,43,48], [48,51,53,57], [60,62,67,68], [70,72,74,77]]
 
diff --git a/examples/sampling/01_sound_cloud.py b/examples/sampling/01_sound_cloud.py
index 99bc296..0ce20c3 100644
--- a/examples/sampling/01_sound_cloud.py
+++ b/examples/sampling/01_sound_cloud.py
@@ -7,7 +7,7 @@ Exponential cloud of sounds...
 
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 snds = ['../snds/snd_%d.aif' % i for i in range(1,7)]
 num = len(snds)
diff --git a/examples/sampling/02_live_looper.py b/examples/sampling/02_live_looper.py
index 0d38569..ff73058 100644
--- a/examples/sampling/02_live_looper.py
+++ b/examples/sampling/02_live_looper.py
@@ -9,7 +9,7 @@ The buffer is looped with some funny parameters...
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=1).boot()
+s = Server(duplex=1).boot()
 
 tab = NewTable(4)
 rec = TableRec(Input(), tab)
diff --git a/examples/sampling/03_rec_and_loop.py b/examples/sampling/03_rec_and_loop.py
index 5264d19..61999fd 100644
--- a/examples/sampling/03_rec_and_loop.py
+++ b/examples/sampling/03_rec_and_loop.py
@@ -8,7 +8,7 @@ Call r.play() (as many times as you want) to record a buffer from the input mic.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=1).boot()
+s = Server(duplex=1).boot()
 
 buffer_length = 1 # seconds
 
diff --git a/examples/sampling/04_live_convolution.py b/examples/sampling/04_live_convolution.py
index 7022ddc..6f5f0f5 100644
--- a/examples/sampling/04_live_convolution.py
+++ b/examples/sampling/04_live_convolution.py
@@ -14,7 +14,7 @@ Circular convolution is very expensive, so keep TLEN (in samples) small.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=1).boot()
+s = Server(duplex=1).boot()
 
 sf = Noise(.5)
 
diff --git a/examples/sequencing/01_starttime_duration.py b/examples/sequencing/01_starttime_duration.py
index 307fec2..cd13f98 100644
--- a/examples/sequencing/01_starttime_duration.py
+++ b/examples/sequencing/01_starttime_duration.py
@@ -7,7 +7,7 @@ methods to sequence events over time.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 num = 70
 freqs = [random.uniform(100,1000) for i in range(num)]
diff --git a/examples/sequencing/02_random_score.py b/examples/sequencing/02_random_score.py
index bd829b2..60c3026 100644
--- a/examples/sequencing/02_random_score.py
+++ b/examples/sequencing/02_random_score.py
@@ -7,7 +7,7 @@ Calling Python function from an audio stream with the Score object.
 from pyo import *
 import random
 
-s = Server(sr=44100, nchnls=2, buffersize=256, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 # Frequency of event generation in Hz
 GEN_FREQ = .25
diff --git a/examples/synthesis/01_pulsar_synthesis.py b/examples/synthesis/01_pulsar_synthesis.py
index 690b676..eb7a2bb 100644
--- a/examples/synthesis/01_pulsar_synthesis.py
+++ b/examples/synthesis/01_pulsar_synthesis.py
@@ -7,7 +7,7 @@ Hand-written pulsar synthesis.
 from pyo import *
 import random
 
-s = Server(sr=48000, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 # simple pulsar waveform
 t = HarmTable([1,0,.3,0,.2,0,0,.1], size=32768)
diff --git a/examples/synthesis/02_FM3.py b/examples/synthesis/02_FM3.py
index 9c8fbdf..02b2bc2 100644
--- a/examples/synthesis/02_FM3.py
+++ b/examples/synthesis/02_FM3.py
@@ -7,7 +7,7 @@
 from pyo import *
 import math
 
-s = Server(sr=48000, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 t = HarmTable([1,0.1])
 class FM3:
diff --git a/examples/synthesis/07_split_sideband_synthesis.py b/examples/synthesis/07_split_sideband_synthesis.py
index 80eeee2..0ca80ee 100644
--- a/examples/synthesis/07_split_sideband_synthesis.py
+++ b/examples/synthesis/07_split_sideband_synthesis.py
@@ -12,7 +12,7 @@ Ann Arbor, MPublishing, University of Michigan Library, August 2008
 from pyo import *
 import math
 
-s = Server(sr=48000, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 twopi = 2 * math.pi
 oneOverTwoPi = 1.0 / twopi
diff --git a/examples/synthesis/08_bucket_brigade_device.py b/examples/synthesis/08_bucket_brigade_device.py
index c78d799..a1534ee 100644
--- a/examples/synthesis/08_bucket_brigade_device.py
+++ b/examples/synthesis/08_bucket_brigade_device.py
@@ -8,7 +8,7 @@ filter inside each delay line. The delay lines are feeded with a sine wave.
 """
 from pyo import *
 
-s = Server(sr=48000, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 t = HarmTable(size=32768)
 src = Osc(t, 100)
diff --git a/examples/tables/01_curve_table.py b/examples/tables/01_curve_table.py
index 1acdd44..e351c6c 100644
--- a/examples/tables/01_curve_table.py
+++ b/examples/tables/01_curve_table.py
@@ -6,7 +6,7 @@ Curve table variations used as an amplitude envelope.
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 t = CurveTable([(0,0),(2048,.5),(4096, .2),(6144,.5),(8192,0)], tension=0, bias=20).normalize()
 t.view(title="Waveform at initialization")
diff --git a/examples/tables/02_scrubbing.py b/examples/tables/02_scrubbing.py
index f0c2fb1..13bb1ef 100644
--- a/examples/tables/02_scrubbing.py
+++ b/examples/tables/02_scrubbing.py
@@ -9,7 +9,7 @@ Give the focus to the Scrubbing window then click and move the mouse...
 """
 
 from pyo import *
-s = Server(buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 def mouse(mpos):
     print("X = %.2f, Y = %.2f" % tuple(mpos))
diff --git a/examples/tables/03_granulation.py b/examples/tables/03_granulation.py
index 8a5728b..8681cb3 100644
--- a/examples/tables/03_granulation.py
+++ b/examples/tables/03_granulation.py
@@ -8,7 +8,7 @@ Classical granulation stretching...
 
 from pyo import *
 
-s = Server(buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 snd = SndTable('../snds/baseballmajeur_m.aif')
 snd.view()
diff --git a/examples/tables/05_table_maker.py b/examples/tables/05_table_maker.py
index dd24b93..f93bfff 100644
--- a/examples/tables/05_table_maker.py
+++ b/examples/tables/05_table_maker.py
@@ -7,7 +7,7 @@ Creates a new sound table from random chunks of a soundfile.
 from pyo import *
 import random, os
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 path = "../snds/baseballmajeur_m.aif"
 dur = sndinfo(path)[1]
diff --git a/examples/utilities/01_get_example.py b/examples/utilities/01_get_example.py
index 49bbe3f..d049da2 100644
--- a/examples/utilities/01_get_example.py
+++ b/examples/utilities/01_get_example.py
@@ -8,7 +8,7 @@ The PyoObject.get() method can be used to convert audio stream to usable python
 """
 from pyo import *
 
-s = Server(sr=44100, nchnls=2, buffersize=512, duplex=0).boot()
+s = Server(duplex=0).boot()
 
 lfos = Sine(freq=[.1,.2,.4,.3], mul=100, add=500)
 synth = SineLoop(freq=lfos, feedback=.07, mul=.05).out()
diff --git a/include/ad_jack.h b/include/ad_jack.h
index afac2de..c41b874 100644
--- a/include/ad_jack.h
+++ b/include/ad_jack.h
@@ -35,6 +35,8 @@ int jack_srate_cb(jack_nframes_t nframes, void *arg);
 int jack_bufsize_cb(jack_nframes_t nframes, void *arg);
 void jack_error_cb(const char *desc);
 void jack_shutdown_cb(void *arg);
+int jack_input_port_set_names(Server *self);
+int jack_output_port_set_names(Server *self);
 void Server_jack_autoconnect(Server *self);
 int Server_jack_init(Server *self);
 int Server_jack_deinit(Server *self);
diff --git a/include/pyomodule.h b/include/pyomodule.h
index ab23153..9e3cb24 100644
--- a/include/pyomodule.h
+++ b/include/pyomodule.h
@@ -21,7 +21,7 @@
 #include "Python.h"
 #include <math.h>
 
-#define PYO_VERSION "0.8.3"
+#define PYO_VERSION "0.8.4"
 
 #ifndef __MYFLT_DEF
 #define __MYFLT_DEF
diff --git a/include/servermodule.h b/include/servermodule.h
index 6a9c0ad..46ae7da 100644
--- a/include/servermodule.h
+++ b/include/servermodule.h
@@ -76,6 +76,10 @@ typedef struct {
     int jackautoout; /* jack port auto-connection (on by default) */
     PyObject *jackAutoConnectInputPorts; /* list of lists of jack auto-connection ports to pyo inputs */
     PyObject *jackAutoConnectOutputPorts; /* list of lists of jack auto-connection ports from pyo outputs */
+    PyObject *jackInputPortNames; /* string or list of strings (input port short names for jack server */
+    PyObject *jackOutputPortNames; /* string or list of strings (output port short names for jack server */
+    int isJackTransportSlave;
+    int jack_transport_state; /* 0 = stopped, 1 = started */
     PyoMidiEvent midiEvents[200];
     int midiin_count;
     int midiout_count;
@@ -174,7 +178,9 @@ void Server_message(Server *self, char * format, ...);
 void Server_warning(Server *self, char * format, ...);
 void Server_debug(Server *self, char * format, ...);
 PyObject * Server_shutdown(Server *self);
-PyObject *Server_stop(Server *self);
+PyObject * Server_stop(Server *self);
+PyObject * Server_start(Server *self);
+PyObject * Server_boot(Server *self, PyObject *arg);
 void Server_process_gui(Server *server);
 void Server_process_time(Server *server);
 int Server_start_rec_internal(Server *self, char *filename);
diff --git a/installers/osx/PkgResources_x86_64_py2/ReadMe.rtf b/installers/osx/PkgResources_x86_64_py2/ReadMe.rtf
index 79476ad..b93fbc8 100755
--- a/installers/osx/PkgResources_x86_64_py2/ReadMe.rtf
+++ b/installers/osx/PkgResources_x86_64_py2/ReadMe.rtf
@@ -1,97 +1,82 @@
-{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400
-{\fonttbl\f0\fnil\fcharset0 LucidaGrande;\f1\froman\fcharset0 Times-Roman;}
-{\colortbl;\red255\green255\blue255;}
-{\info
-{\author Olivier }}\margl1440\margr1440\vieww10800\viewh8400\viewkind0
-\deftab720
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 Python-pyo (version 0.8.3) for python 2.7
-\f1\fs24 \
-
-\f0\fs26 \
-System requirements : macOS 10.6 to 10.12
-\f1\fs24 \
-
-\f0\fs26 \
-This package installs all the required components to run pyo inside your current Python installation. Python 2.7 (32/64 bit) must be already installed on your system.
-\f1\fs24 \
-
-\f0\fs26 \
-This package is divided into two separate installers. If you do not require one of them, please unselect the package in custom installation mode.
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\b \cf0 1. pyo extension:
-\f1\b0\fs24 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 The following components will be installed in the site-packages folder of the current Python Framework:
-\f1\fs24 \
-
-\f0\fs26 \
-_pyo.so
-\f1\fs24 \
-
-\f0\fs26 _pyo64.so
-\f1\fs24 \
-
-\f0\fs26 pyo.py
-\f1\fs24 \
-
-\f0\fs26 pyo64.py
-\f1\fs24 \
-
-\f0\fs26 pyolib (folder)
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\b \cf0 2. Support libraries (i386 and x86_64):
-\f1\b0\fs24 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 This component will install a number of dynamic libraries on which pyo depends. If you already have these, then you can skip this installation.
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\b \cf0 Warning:
-\b0  this installation will overwrite any previously installed libraries. These are the libraries that will be installed in your /usr/local/lib directory:
-\f1\fs24 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 \
-liblo.7.dylib
-\f1\fs24 \
-
-\f0\fs26 libportaudio.2.dylib
-\f1\fs24 \
-
-\f0\fs26 libportmidi.dylib
-\f1\fs24 \
-
-\f0\fs26 libsndfile.1.dylib
-\f1\fs24 \
-
-\f0\fs26 libFLAC.8.dylib
-\f1\fs24 \
-
-\f0\fs26 libvorbisenc.2.dylib
-\f1\fs24 \
-
-\f0\fs26 libvorbis.0.dylib
-\f1\fs24 \
-
-\f0\fs26 libogg.0.dylib
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\pardeftab720
-\cf0 Olivier B\'e9langer, 2016
-\f1\fs24 \
-}
\ No newline at end of file
+{\rtf1\ansi\deff3\adeflang1025
+{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f5\froman\fprq2\fcharset0 LucidaGrande;}{\f6\fnil\fprq2\fcharset0 WenQuanYi Micro Hei;}{\f7\fnil\fprq2\fcharset0 FreeSans;}{\f8\fswiss\fprq0\fcharset128 FreeSans;}}
+{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}
+{\stylesheet{\s0\snext0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105 Normal;}
+{\s15\sbasedon0\snext16\sb240\sa120\keepn\dbch\af6\dbch\af7\afs28\loch\f4\fs28 Heading;}
+{\s16\sbasedon0\snext16\sl288\slmult1\sb0\sa140 Text Body;}
+{\s17\sbasedon16\snext17\sl288\slmult1\sb0\sa140\dbch\af8 List;}
+{\s18\sbasedon0\snext18\sb120\sa120\noline\i\dbch\af8\afs24\ai\fs24 Caption;}
+{\s19\sbasedon0\snext19\noline\dbch\af8 Index;}
+}{\*\generator LibreOffice/5.2.5.1$Linux_X86_64 LibreOffice_project/20m0$Build-1}{\info{\author Olivier }{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr2017\mo3\dy23\hr20\min36}{\printim\yr0\mo0\dy0\hr0\min0}}{\*\userprops}\deftab720
+\viewscale120
+{\*\pgdsctbl
+{\pgdsc0\pgdscuse451\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\pgdscnxt0 Default Style;}}
+\formshade{\*\pgdscno0}\paperh15840\paperw12240\margl1440\margr1440\margt1440\margb1440\sectd\sbknone\sectunlocked1\pgndec\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc
+{\*\ftnsep}\pgndec\pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+Python-pyo (version 0.8.}{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+4}{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+) for python 2.7}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+System requirements : macOS 10.6 to 10.12}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+This package installs all the required components to run pyo inside your current Python installation. Python 2.7 (32/64 bit) must be already installed on your system.}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+This package is divided into two separate installers. If you do not require one of them, please unselect the package in custom installation mode.}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+1. pyo extension:}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+The following components will be installed in the site-packages folder of the current Python Framework:}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+_pyo.so}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+_pyo64.so}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+pyo.py}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+pyo64.py}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+pyolib (folder)}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+2. Support libraries (i386 and x86_64):}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+This component will install a number of dynamic libraries on which pyo depends. If you already have these, then you can skip this installation.}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+Warning:}{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+ this installation will overwrite any previously installed libraries. These are the libraries that will be installed in your /usr/local/lib directory:}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+liblo.7.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libportaudio.2.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libportmidi.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libsndfile.1.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libFLAC.8.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libvorbisenc.2.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libvorbis.0.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libogg.0.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+Olivier B\u233\'e9langer, 2016}
+\par }
\ No newline at end of file
diff --git a/installers/osx/PkgResources_x86_64_py3/ReadMe.rtf b/installers/osx/PkgResources_x86_64_py3/ReadMe.rtf
index 1a3f0e0..57d208c 100755
--- a/installers/osx/PkgResources_x86_64_py3/ReadMe.rtf
+++ b/installers/osx/PkgResources_x86_64_py3/ReadMe.rtf
@@ -1,97 +1,82 @@
-{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400
-{\fonttbl\f0\fnil\fcharset0 LucidaGrande;\f1\froman\fcharset0 Times-Roman;}
-{\colortbl;\red255\green255\blue255;}
-{\info
-{\author Olivier }}\margl1440\margr1440\vieww10800\viewh8400\viewkind0
-\deftab720
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 Python-pyo (version 0.8.3) for python 3.5
-\f1\fs24 \
-
-\f0\fs26 \
-System requirements : macOS 10.8 to 10.12
-\f1\fs24 \
-
-\f0\fs26 \
-This package installs all the required components to run pyo inside your current Python installation. Python 3.5 (32/64 bit) must be already installed on your system.
-\f1\fs24 \
-
-\f0\fs26 \
-This package is divided into two separate installers. If you do not require one of them, please unselect the package in custom installation mode.
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\b \cf0 1. pyo extension:
-\f1\b0\fs24 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 The following components will be installed in the site-packages folder of the current Python Framework:
-\f1\fs24 \
-
-\f0\fs26 \
-_pyo.cpython-35m-darwin.so
-\f1\fs24 \
-
-\f0\fs26 _pyo64.cpython-35m-darwin.so
-\f1\fs24 \
-
-\f0\fs26 pyo.py
-\f1\fs24 \
-
-\f0\fs26 pyo64.py
-\f1\fs24 \
-
-\f0\fs26 pyolib (folder)
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\b \cf0 2. Support libraries (i386 and x86_64):
-\f1\b0\fs24 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 This component will install a number of dynamic libraries on which pyo depends. If you already have these, then you can skip this installation.
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\b \cf0 Warning:
-\b0  this installation will overwrite any previously installed libraries. These are the libraries that will be installed in your /usr/local/lib directory:
-\f1\fs24 \
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720
-
-\f0\fs26 \cf0 \
-liblo.7.dylib
-\f1\fs24 \
-
-\f0\fs26 libportaudio.2.dylib
-\f1\fs24 \
-
-\f0\fs26 libportmidi.dylib
-\f1\fs24 \
-
-\f0\fs26 libsndfile.1.dylib
-\f1\fs24 \
-
-\f0\fs26 libFLAC.8.dylib
-\f1\fs24 \
-
-\f0\fs26 libvorbisenc.2.dylib
-\f1\fs24 \
-
-\f0\fs26 libvorbis.0.dylib
-\f1\fs24 \
-
-\f0\fs26 libogg.0.dylib
-\f1\fs24 \
-
-\f0\fs26 \
-\pard\pardeftab720
-\cf0 Olivier B\'e9langer, 2016
-\f1\fs24 \
-}
\ No newline at end of file
+{\rtf1\ansi\deff3\adeflang1025
+{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f5\froman\fprq2\fcharset0 LucidaGrande;}{\f6\fnil\fprq2\fcharset0 WenQuanYi Micro Hei;}{\f7\fnil\fprq2\fcharset0 FreeSans;}{\f8\fswiss\fprq0\fcharset128 FreeSans;}}
+{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}
+{\stylesheet{\s0\snext0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105 Normal;}
+{\s15\sbasedon0\snext16\sb240\sa120\keepn\dbch\af6\dbch\af7\afs28\loch\f4\fs28 Heading;}
+{\s16\sbasedon0\snext16\sl288\slmult1\sb0\sa140 Text Body;}
+{\s17\sbasedon16\snext17\sl288\slmult1\sb0\sa140\dbch\af8 List;}
+{\s18\sbasedon0\snext18\sb120\sa120\noline\i\dbch\af8\afs24\ai\fs24 Caption;}
+{\s19\sbasedon0\snext19\noline\dbch\af8 Index;}
+}{\*\generator LibreOffice/5.2.5.1$Linux_X86_64 LibreOffice_project/20m0$Build-1}{\info{\author Olivier }{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr2017\mo3\dy23\hr20\min36}{\printim\yr0\mo0\dy0\hr0\min0}}{\*\userprops}\deftab720
+\viewscale120
+{\*\pgdsctbl
+{\pgdsc0\pgdscuse451\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\pgdscnxt0 Default Style;}}
+\formshade{\*\pgdscno0}\paperh15840\paperw12240\margl1440\margr1440\margt1440\margb1440\sectd\sbknone\sectunlocked1\pgndec\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc
+{\*\ftnsep}\pgndec\pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+Python-pyo (version 0.8.}{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+4}{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+) for python 3.5}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+System requirements : macOS 10.8 to 10.12}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+This package installs all the required components to run pyo inside your current Python installation. Python 3.5 (32/64 bit) must be already installed on your system.}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+This package is divided into two separate installers. If you do not require one of them, please unselect the package in custom installation mode.}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+1. pyo extension:}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+The following components will be installed in the site-packages folder of the current Python Framework:}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+_pyo.cpython-35m-darwin.so}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+_pyo64.cpython-35m-darwin.so}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+pyo.py}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+pyo64.py}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+pyolib (folder)}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+2. Support libraries (i386 and x86_64):}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+This component will install a number of dynamic libraries on which pyo depends. If you already have these, then you can skip this installation.}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+Warning:}{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+ this installation will overwrite any previously installed libraries. These are the libraries that will be installed in your /usr/local/lib directory:}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+liblo.7.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libportaudio.2.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libportmidi.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libsndfile.1.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libFLAC.8.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libvorbisenc.2.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libvorbis.0.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+libogg.0.dylib}
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+
+\par \pard\plain \s0\nowidctlpar\hyphpar0\cf0\kerning1\dbch\af6\langfe2052\dbch\af7\afs24\alang1081\loch\f3\hich\af3\fs24\lang4105{\cf1\b0\rtlch \ltrch\loch\fs26\loch\f5\hich\af5
+Olivier B\u233\'e9langer, 2016}
+\par }
\ No newline at end of file
diff --git a/installers/osx/release_x86_64_py2.sh b/installers/osx/release_x86_64_py2.sh
index acf1b3f..e550ba9 100755
--- a/installers/osx/release_x86_64_py2.sh
+++ b/installers/osx/release_x86_64_py2.sh
@@ -8,9 +8,9 @@
 # 3. cd utils and build E-Pyo for python2
 # 4. cd installers/osx and build the release for python2
 
-export PACKAGE_NAME=pyo_0.8.3_x86_64_py2.pkg
-export DMG_DIR="pyo 0.8.3 py2 Universal"
-export DMG_NAME="pyo_0.8.3_OSX_py2-universal.dmg"
+export PACKAGE_NAME=pyo_0.8.4_x86_64_py2.pkg
+export DMG_DIR="pyo 0.8.4 py2 Universal"
+export DMG_NAME="pyo_0.8.4_OSX_py2-universal.dmg"
 export INSTALLER_DIR=`pwd`/installer
 export PYO_MODULE_DIR=$INSTALLER_DIR/PyoModule/Package_Contents/tmp
 export SUPPORT_LIBS_DIR=$INSTALLER_DIR/SupportLibs/Package_Contents/usr/local/lib
diff --git a/installers/osx/release_x86_64_py3.sh b/installers/osx/release_x86_64_py3.sh
index 4abc0a7..964d611 100755
--- a/installers/osx/release_x86_64_py3.sh
+++ b/installers/osx/release_x86_64_py3.sh
@@ -8,9 +8,9 @@
 # 3. cd utils and build E-Pyo for python3
 # 4. cd installers/osx and build the release for python3
 
-export PACKAGE_NAME=pyo_0.8.3_x86_64_py3.pkg
-export DMG_DIR="pyo 0.8.3 py3 Universal"
-export DMG_NAME="pyo_0.8.3_OSX_py3-universal.dmg"
+export PACKAGE_NAME=pyo_0.8.4_x86_64_py3.pkg
+export DMG_DIR="pyo 0.8.4 py3 Universal"
+export DMG_NAME="pyo_0.8.4_OSX_py3-universal.dmg"
 export INSTALLER_DIR=`pwd`/installer
 export PYO_MODULE_DIR=$INSTALLER_DIR/PyoModule/Package_Contents/tmp
 export SUPPORT_LIBS_DIR=$INSTALLER_DIR/SupportLibs/Package_Contents/usr/local/lib
diff --git a/installers/win/win_installer_py27.iss b/installers/win/win_installer_py27.iss
index a9eb776..c57e86e 100644
--- a/installers/win/win_installer_py27.iss
+++ b/installers/win/win_installer_py27.iss
@@ -3,7 +3,7 @@
 
 #define appName "pyo"
 #define pyVer "2.7"
-#define appVer "0.8.3"
+#define appVer "0.8.4"
 
 [Setup]
 ; NOTE: The value of AppId uniquely identifies this application.
diff --git a/installers/win/win_installer_py35.iss b/installers/win/win_installer_py35.iss
index d95583e..c326010 100644
--- a/installers/win/win_installer_py35.iss
+++ b/installers/win/win_installer_py35.iss
@@ -3,7 +3,7 @@
 
 #define appName "pyo"
 #define pyVer "3.5"
-#define appVer "0.8.3"
+#define appVer "0.8.4"
 
 [Setup]
 ; NOTE: The value of AppId uniquely identifies this application.
diff --git a/pyolib/_tkwidgets.py b/pyolib/_tkwidgets.py
index 854a2a7..4216428 100644
--- a/pyolib/_tkwidgets.py
+++ b/pyolib/_tkwidgets.py
@@ -457,3 +457,13 @@ class ServerGUI(tk.Frame):
                 self.vumeter.coords(self.green[i], 0, y, self.B1, y)
                 self.vumeter.coords(self.yellow[i], self.B1, y, self.B2, y)
                 self.vumeter.coords(self.red[i], self.B2, y, amp, y)
+
+    def setStartButtonState(self, state):
+        if state:
+            self._started = True
+            self.startStringVar.set('Stop')
+            self.quitButton.configure(state=tk.DISABLED)
+        else:
+            self._started = False
+            self.startStringVar.set('Start')
+            self.quitButton.configure(state=tk.NORMAL)
diff --git a/pyolib/_wxwidgets.py b/pyolib/_wxwidgets.py
index e9ecb04..8a1c71d 100644
--- a/pyolib/_wxwidgets.py
+++ b/pyolib/_wxwidgets.py
@@ -2960,6 +2960,18 @@ class ServerGUI(wx.Frame):
     def setRms(self, *args):
         self.meter.setRms(*args)
 
+    def setStartButtonState(self, state):
+        if state:
+            self._started = True
+            wx.CallAfter(self.startButton.SetLabel, 'Stop')
+            if self.exit:
+                wx.CallAfter(self.quitButton.Disable)
+        else:
+            self._started = False
+            wx.CallAfter(self.startButton.SetLabel, 'Start')
+            if self.exit:
+                wx.CallAfter(self.quitButton.Enable)
+
 def ensureNFD(unistr):
     if sys.platform == 'win32' or sys.platform.startswith('linux'):
         encodings = [sys.getdefaultencoding(), sys.getfilesystemencoding(),
diff --git a/pyolib/server.py b/pyolib/server.py
index 35891d7..278b047 100644
--- a/pyolib/server.py
+++ b/pyolib/server.py
@@ -102,7 +102,8 @@ class Server(object):
         - setDuplex(x): Set the duplex mode used by the server.
         - setVerbosity(x): Set the server's verbosity.
         - reinit(sr, nchnls, buffersize, duplex, audio, jackname): Reinit the server's settings.
-        - deactivateMidi(): Deactivate Midi callback. 
+        - deactivateMidi(): Deactivate Midi callback.
+        - setIsJackTransportSlave(x): Set if pyo's server is slave to jack transport or not.
 
     >>> # For an 8 channels server in duplex mode with
     >>> # a sampling rate of 48000 Hz and buffer size of 512
@@ -127,6 +128,9 @@ class Server(object):
         self._filename = None
         self._fileformat = 0
         self._sampletype = 0
+        self._globalseed = 0
+        self._resampling = 1
+        self._isJackTransportSlave = False
         self._server = Server_base(sr, nchnls, buffersize, duplex, audio, jackname, self._ichnls)
         self._server._setDefaultRecPath(os.path.join(os.path.expanduser("~"), "pyo_rec.wav"))
 
@@ -165,6 +169,7 @@ class Server(object):
         self._sampletype = 0
         self._globalseed = 0
         self._resampling = 1
+        self._isJackTransportSlave = False
         self._server.__init__(sr, nchnls, buffersize, duplex, audio, jackname, self._ichnls)
 
     def setCallback(self, callback):
@@ -539,6 +544,56 @@ class Server(object):
         ports, lmax = convertArgsToLists(ports)
         self._server.setJackAutoConnectOutputPorts(ports)
 
+    def setJackInputPortNames(self, name):
+        """
+        Change the short name of pyo's input ports for the jack server.
+
+        This method must be called after the server is booted.
+
+        :Args:
+
+            name: string or list of strings
+                New name of input ports for the jack server. If `name` is a string,
+                '_xxx' (where xxx is the channel number) will be added to it for
+                each input channel. If `name` is a list of strings, They will be
+                used as is and there must be one for each input channel.
+
+        """
+        self._server.setJackInputPortNames(name)
+
+    def setJackOutputPortNames(self, name):
+        """
+        Change the short name of pyo's output ports for the jack server.
+
+        This method must be called after the server is booted.
+
+        :Args:
+
+            name: string or list of strings
+                New name of output ports for the jack server. If `name` is a string,
+                '_xxx' (where xxx is the channel number) will be added to it for
+                each output channel. If `name` is a list of strings, They will be
+                used as is and there must be one for each output channel.
+
+        """
+        self._server.setJackOutputPortNames(name)
+
+    def setIsJackTransportSlave(self, x):
+        """
+        Set if pyo's server is slave to jack transport or not.
+
+        This method must be called before booting the server.
+
+        :Args:
+
+            x: boolean
+                If True, the server's start and stop command will be slave to
+                Jack transport. If False (the default) jack transport is ignored.
+
+        """
+        self._isJackTransportSlave = x
+        self._server.setIsJackTransportSlave(x)
+
     def setGlobalSeed(self, x):
         """
         Set the server's global seed used by random objects.
diff --git a/pyolib/tables.py b/pyolib/tables.py
index 6e223f6..fcad96a 100644
--- a/pyolib/tables.py
+++ b/pyolib/tables.py
@@ -1479,7 +1479,8 @@ class SndTable(PyoTableObject):
             Full path name of the sound. The defaults, None, creates an empty
             table.
         chnl: int, optional
-            Channel number to read in. Available at initialization time only.
+            Channel number to read in. The count starts at 0 (first channel is
+            is 0, second is 1 and so on). Available at initialization time only.
             The default (None) reads all channels.
         start: float, optional
             Begins reading at `start` seconds into the file. Available at
diff --git a/pyolib/wxgui.py b/pyolib/wxgui.py
index 6821400..2f4ff70 100644
--- a/pyolib/wxgui.py
+++ b/pyolib/wxgui.py
@@ -65,7 +65,9 @@ else:
             EVT_PYO_GUI_CONTROL_SLIDER
                 Sent after any change of the slider position. The current
                 value of the slider can be retrieve with the `value`
-                attribute of the generated event.
+                attribute of the generated event. The object itself can be
+                retrieve with the `object` attribute of the event and the
+                object's id with the `id` attrbute.
 
         :Args:
 
@@ -107,7 +109,7 @@ else:
                                                       orient)
 
         def _outFunction(self, value):
-            evt = PyoGuiControlSliderEvent(value=value)
+            evt = PyoGuiControlSliderEvent(value=value, id=self.GetId(), object=self)
             wx.QueueEvent(self, evt)
 
         def enable(self):
@@ -284,7 +286,9 @@ else:
             EVT_PYO_GUI_GRAPHER
                 Sent after any change of the grapher function. The current
                 list of points of the grapher can be retrieve with the `value`
-                attribute of the generated event.
+                attribute of the generated event. The object itself can be
+                retrieve with the `object` attribute of the event and the
+                object's id with the `id` attrbute.
 
         :Args:
 
@@ -336,7 +340,7 @@ else:
                                                 self._outFunction, pos, size, style)
 
         def _outFunction(self, value):
-            evt = PyoGuiGrapherEvent(value=value)
+            evt = PyoGuiGrapherEvent(value=value, id=self.GetId(), object=self)
             wx.QueueEvent(self, evt)
 
         def _refresh(self):
@@ -498,7 +502,9 @@ else:
             EVT_PYO_GUI_MULTI_SLIDER
                 Sent after any change of the multi-sliders values. The current
                 list of values of the multi-sliders can be retrieve with the
-                `value` attribute of the generated event.
+                `value` attribute of the generated event. The object itself can
+                be retrieve with the `object` attribute of the event and the
+                object's id with the `id` attrbute.
 
         :Args:
 
@@ -535,7 +541,7 @@ else:
                                                     size, style)
 
         def _outFunction(self, value):
-            evt = PyoGuiMultiSliderEvent(value=value)
+            evt = PyoGuiMultiSliderEvent(value=value, id=self.GetId(), object=self)
             wx.QueueEvent(self, evt)
 
         def reset(self):
@@ -858,7 +864,9 @@ else:
                 hold the normalized position of the mouse into the sound.
                 For X-axis value, 0.0 is the beginning of the sound and 1.0
                 is the end of the sound. For the Y-axis, 0.0 is the bottom
-                of the panel and 1.0 is the top.
+                of the panel and 1.0 is the top. The object itself can be
+                retrieve with the `object` attribute of the event and the
+                object's id with the `id` attrbute.
             EVT_PYO_GUI_SNDVIEW_SELECTION
                 Sent when a new region is selected on the panel. A new
                 selection is created with a Right-click and drag on the panel.
@@ -866,7 +874,9 @@ else:
                 drag. Ctrl+Right-click (Cmd on OSX) remove the selected region.
                 The `value` attribute of the event will hold the normalized
                 selection as a tuple (min, max). 0.0 means the beginning of
-                the sound and 1.0 means the end of the sound.
+                the sound and 1.0 means the end of the sound. The object itself
+                can be retrieve with the `object` attribute of the event and the
+                object's id with the `id` attrbute.
 
         :Args:
 
@@ -904,12 +914,12 @@ else:
             self.update()
 
         def _position_callback(self, pos):
-            evt = PyoGuiSndViewMousePositionEvent(value=pos)
+            evt = PyoGuiSndViewMousePositionEvent(value=pos, id=self.GetId(), object=self)
             wx.QueueEvent(self, evt)
 
         def _select_callback(self, selection):
             selection = (max(0.0, min(selection)), min(max(selection), 1.0))
-            evt = PyoGuiSndViewSelectionEvent(value=selection)
+            evt = PyoGuiSndViewSelectionEvent(value=selection, id=self.GetId(), object=self)
             wx.QueueEvent(self, evt)
 
         def __del__(self):
diff --git a/scripts/release_doc_src.sh b/scripts/release_doc_src.sh
index f908f09..4756d28 100755
--- a/scripts/release_doc_src.sh
+++ b/scripts/release_doc_src.sh
@@ -6,7 +6,7 @@
 # 3. Execute from pyo folder : ./scripts/release_doc_src.sh
 #
 
-version=0.8.3
+version=0.8.4
 replace=XXX
 
 doc_rep=pyo_XXX-doc
diff --git a/scripts/test_portaudio_functions.py b/scripts/test_portaudio_functions.py
new file mode 100644
index 0000000..eefaa2e
--- /dev/null
+++ b/scripts/test_portaudio_functions.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# encoding: utf-8
+from pyo import *
+
+print("------------- PA Version ---------------------")
+print('pa_get_version:', pa_get_version())
+print('pa_get_version_text:', pa_get_version_text())
+
+print("------------- Host API ---------------------")
+print('pa_count_host_apis:', pa_count_host_apis())
+print('pa_list_host_apis:')
+pa_list_host_apis()
+print('pa_get_default_host_api:', pa_get_default_host_api())
+
+print("--------------- Devices -------------------")
+print('pa_count_devices:', pa_count_devices())
+print('pa_list_devices:')
+pa_list_devices()
+print('pa_get_input_devices:')
+print(pa_get_input_devices())
+print('pa_get_default_input:', pa_get_default_input())
+print('pa_get_output_devices:')
+print(pa_get_output_devices())
+print('pa_get_default_output:', pa_get_default_output())
+
+print("--------------- Device Infos -------------------")
+print('pa_get_devices_infos:')
+inputs, outputs = pa_get_devices_infos()
+print('- Inputs:')
+for index in sorted(list(inputs.keys())):
+    print('  Device index:', index)
+    for key in ['name', 'host api index', 'default sr', 'latency']:
+        print('    %s:' % key, inputs[index][key])
+print('- Outputs:')
+for index in sorted(list(outputs.keys())):
+    print('  Device index:', index)
+    for key in ['name', 'host api index', 'default sr', 'latency']:
+        print('    %s:' % key, outputs[index][key])
+
+print("--------------- Channels -------------------")
+dev_list, dev_index =  pa_get_output_devices()
+for dev in dev_index:
+    print('Device index:', dev, 'Max outputs:', pa_get_output_max_channels(dev))
+
+dev_list, dev_index =  pa_get_input_devices()
+for dev in dev_index:
+    print('Device index:', dev, 'Max inputs:', pa_get_input_max_channels(dev))
diff --git a/setup.py b/setup.py
index 8bf6a36..a6b987a 100644
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ from distutils.sysconfig import get_python_lib
 from distutils.core import setup, Extension
 import os, sys, py_compile
 
-pyo_version = "0.8.3"
+pyo_version = "0.8.4"
 build_with_jack_support = False
 compile_externals = False
 
diff --git a/src/engine/ad_jack.c b/src/engine/ad_jack.c
index 69e7734..05fa6b0 100644
--- a/src/engine/ad_jack.c
+++ b/src/engine/ad_jack.c
@@ -72,48 +72,106 @@ jack_callback(jack_nframes_t nframes, void *arg) {
 }
 
 int
+jack_transport_cb(jack_transport_state_t state, jack_position_t *pos, void *arg) {
+    Server *server = (Server *) arg;
+
+    if (server->jack_transport_state == state)
+        return 0;
+
+    switch (state) {
+        case JackTransportStopped:
+            if (server->server_started) {
+                PyGILState_STATE s = PyGILState_Ensure();
+                Server_stop(server);
+                PyGILState_Release(s);
+            }
+            break;
+        case JackTransportRolling:
+            if (!server->server_started) {
+                PyGILState_STATE s = PyGILState_Ensure();
+                Server_start(server);
+                PyGILState_Release(s);
+            }
+            break;
+        case JackTransportStarting:
+            break;
+        case JackTransportLooping:
+            break;
+        case JackTransportNetStarting:
+            break;
+    }
+
+    server->jack_transport_state = state;
+
+    return 0;
+}
+
+int
 jack_srate_cb(jack_nframes_t nframes, void *arg) {
-    Server *s = (Server *) arg;
-    s->samplingRate = (double) nframes;
-    Server_debug(s, "The sample rate is now %lu.\n", (unsigned long) nframes);
+    Server *server = (Server *) arg;
+    server->samplingRate = (double) nframes;
+
+    PyGILState_STATE s = PyGILState_Ensure();
+    Server_debug(server, "The sample rate is now %lu.\n", (unsigned long) nframes);
+    PyGILState_Release(s);
+
     return 0;
 }
 
 int
 jack_bufsize_cb(jack_nframes_t nframes, void *arg) {
-    Server *s = (Server *) arg;
-    s->bufferSize = (int) nframes;
-    Server_debug(s, "The buffer size is now %lu.\n", (unsigned long) nframes);
+    Server *server = (Server *) arg;
+    server->bufferSize = (int) nframes;
+
+    PyGILState_STATE s = PyGILState_Ensure();
+    Server_debug(server, "The buffer size is now %lu.\n", (unsigned long) nframes);
+    PyGILState_Release(s);
+
     return 0;
 }
 
 void
 jack_error_cb(const char *desc) {
+    PyGILState_STATE s = PyGILState_Ensure();
     PySys_WriteStdout("JACK error: %s\n", desc);
+    PyGILState_Release(s);
 }
 
 void
 jack_shutdown_cb(void *arg) {
-    Server *s = (Server *) arg;
-    Server_shutdown(s);
-    Server_warning(s, "JACK server shutdown. Pyo Server shut down.\n");
+    Server *server = (Server *) arg;
+
+    PyGILState_STATE s = PyGILState_Ensure();
+    Server_shutdown(server);
+    Server_warning(server, "JACK server shutdown. Pyo Server shut down.\n");
+    PyGILState_Release(s);
 }
 
 void
 Server_jack_autoconnect(Server *self) {
     const char **ports;
     char *portname;
-    int i, j, num;
+    int i, j, num, err = 0;
     PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
 
     if (self->jackautoin) {
-        if ((ports = jack_get_ports(be_data->jack_client, "system", NULL, JackPortIsOutput)) == NULL) {
+
+        Py_BEGIN_ALLOW_THREADS
+        ports = jack_get_ports(be_data->jack_client, "system", NULL, JackPortIsOutput);
+        Py_END_ALLOW_THREADS
+
+        if (ports == NULL) {
             Server_error(self, "Jack: Cannot find any physical capture ports called 'system'\n");
         }
 
-        i=0;
+        i = 0;
         while(ports[i] != NULL && be_data->jack_in_ports[i] != NULL){
-            if (jack_connect(be_data->jack_client, ports[i], jack_port_name(be_data->jack_in_ports[i]))) {
+
+            Py_BEGIN_ALLOW_THREADS
+            err = jack_connect(be_data->jack_client, ports[i], jack_port_name(be_data->jack_in_ports[i]));
+            Py_END_ALLOW_THREADS
+
+            if (err) {
                 Server_error(self, "Jack: cannot connect 'system' to input ports\n");
             }
             i++;
@@ -122,13 +180,23 @@ Server_jack_autoconnect(Server *self) {
     }
 
     if (self->jackautoout) {
-        if ((ports = jack_get_ports(be_data->jack_client, "system", NULL, JackPortIsInput)) == NULL) {
+
+        Py_BEGIN_ALLOW_THREADS
+        ports = jack_get_ports(be_data->jack_client, "system", NULL, JackPortIsInput);
+        Py_END_ALLOW_THREADS
+
+        if (ports == NULL) {
             Server_error(self, "Jack: Cannot find any physical playback ports called 'system'\n");
         }
 
-        i=0;
+        i = 0;
         while(ports[i] != NULL && be_data->jack_out_ports[i] != NULL){
-            if (jack_connect(be_data->jack_client, jack_port_name (be_data->jack_out_ports[i]), ports[i])) {
+
+            Py_BEGIN_ALLOW_THREADS
+            jack_connect(be_data->jack_client, jack_port_name (be_data->jack_out_ports[i]), ports[i]);
+            Py_END_ALLOW_THREADS
+
+            if (err) {
                 Server_error(self, "Jack: cannot connect output ports to 'system'\n");
             }
             i++;
@@ -146,8 +214,14 @@ Server_jack_autoconnect(Server *self) {
                 num = PyList_Size(PyList_GetItem(self->jackAutoConnectInputPorts, j));
                 for (i=0; i<num; i++) {
                     portname = PY_STRING_AS_STRING(PyList_GetItem(PyList_GetItem(self->jackAutoConnectInputPorts, j), i));
+
                     if (jack_port_by_name(be_data->jack_client, portname) != NULL) {
-                        if (jack_connect(be_data->jack_client, portname, jack_port_name(be_data->jack_in_ports[j]))) {
+
+                        Py_BEGIN_ALLOW_THREADS
+                        err = jack_connect(be_data->jack_client, portname, jack_port_name(be_data->jack_in_ports[j]));
+                        Py_END_ALLOW_THREADS
+
+                        if (err) {
                             Server_error(self, "Jack: cannot connect '%s' to input port %d\n", portname, j);
                         }
                     }
@@ -169,7 +243,12 @@ Server_jack_autoconnect(Server *self) {
                 for (i=0; i<num; i++) {
                     portname = PY_STRING_AS_STRING(PyList_GetItem(PyList_GetItem(self->jackAutoConnectOutputPorts, j), i));
                     if (jack_port_by_name(be_data->jack_client, portname) != NULL) {
-                        if (jack_connect(be_data->jack_client, jack_port_name(be_data->jack_out_ports[j]), portname)) {
+
+                        Py_BEGIN_ALLOW_THREADS
+                        jack_connect(be_data->jack_client, jack_port_name(be_data->jack_out_ports[j]), portname);
+                        Py_END_ALLOW_THREADS
+
+                        if (err) {
                             Server_error(self, "Jack: cannot connect output port %d to '%s'\n", j, portname);
                         }
                     }
@@ -198,10 +277,14 @@ Server_jack_init(Server *self) {
     assert(self->audio_be_data == NULL);
     PyoJackBackendData *be_data = (PyoJackBackendData *) malloc(sizeof(PyoJackBackendData *));
     self->audio_be_data = (void *) be_data;
+    strncpy(client_name, self->serverName, 32);
+
+    Py_BEGIN_ALLOW_THREADS
     be_data->jack_in_ports = (jack_port_t **) calloc(self->ichnls + self->input_offset, sizeof(jack_port_t *));
     be_data->jack_out_ports = (jack_port_t **) calloc(self->nchnls + self->output_offset, sizeof(jack_port_t *));
-    strncpy(client_name,self->serverName, 32);
     be_data->jack_client = jack_client_open(client_name, options, &status, server_name);
+    Py_END_ALLOW_THREADS
+
     if (be_data->jack_client == NULL) {
         Server_error(self, "Jack error: Unable to create JACK client\n");
         if (status & JackServerFailed) {
@@ -218,7 +301,7 @@ Server_jack_init(Server *self) {
         Server_warning(self, "Jack name `%s' assigned\n", self->serverName);
     }
 
-    sampleRate = jack_get_sample_rate (be_data->jack_client);
+    sampleRate = jack_get_sample_rate(be_data->jack_client);
     if (sampleRate != self->samplingRate) {
         self->samplingRate = (double)sampleRate;
         Server_warning(self, "Sample rate set to Jack engine sample rate: %" PRIu32 "\n", sampleRate);
@@ -228,9 +311,14 @@ Server_jack_init(Server *self) {
     }
     if (sampleRate <= 0) {
         Server_error(self, "Invalid Jack engine sample rate.");
+
+        Py_BEGIN_ALLOW_THREADS
         jack_client_close(be_data->jack_client);
+        Py_END_ALLOW_THREADS
+
         return -1;
     }
+
     bufferSize = jack_get_buffer_size(be_data->jack_client);
     if (bufferSize != self->bufferSize) {
         self->bufferSize = bufferSize;
@@ -245,9 +333,14 @@ Server_jack_init(Server *self) {
         index = total_nchnls - nchnls - 1;
         ret = sprintf(name, "input_%i", index + 1);
         if (ret > 0) {
-            be_data->jack_in_ports[index]
-            = jack_port_register(be_data->jack_client, name,
-                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+
+            Py_BEGIN_ALLOW_THREADS
+            be_data->jack_in_ports[index] = jack_port_register(be_data->jack_client,
+                                                               name,
+                                                               JACK_DEFAULT_AUDIO_TYPE,
+                                                               JackPortIsInput, 0);
+            Py_END_ALLOW_THREADS
+
         }
 
         if ((be_data->jack_in_ports[index] == NULL)) {
@@ -261,9 +354,14 @@ Server_jack_init(Server *self) {
         index = total_nchnls - nchnls - 1;
         ret = sprintf(name, "output_%i", index + 1);
         if (ret > 0) {
-            be_data->jack_out_ports[index]
-            = jack_port_register(be_data->jack_client, name,
-                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+            Py_BEGIN_ALLOW_THREADS
+            be_data->jack_out_ports[index] = jack_port_register(be_data->jack_client,
+                                                                name,
+                                                                JACK_DEFAULT_AUDIO_TYPE,
+                                                                JackPortIsOutput, 0);
+            Py_END_ALLOW_THREADS
+
         }
         if ((be_data->jack_out_ports[index] == NULL)) {
             Server_error(self, "Jack: no more JACK output ports available\n");
@@ -276,9 +374,15 @@ Server_jack_init(Server *self) {
     jack_set_buffer_size_callback(be_data->jack_client, jack_bufsize_cb, (void *) self);
     jack_set_process_callback(be_data->jack_client, jack_callback, (void *) self);
 
-    if (jack_activate(be_data->jack_client)) {
+    if (self->isJackTransportSlave)
+        jack_set_sync_callback(be_data->jack_client, jack_transport_cb, (void *) self);
+
+    Py_BEGIN_ALLOW_THREADS
+    ret = jack_activate(be_data->jack_client);
+    Py_END_ALLOW_THREADS
+
+    if (ret) {
         Server_error(self, "Jack error: cannot activate jack client.\n");
-        //Server_shutdown(self);
         return -1;
     }
 
@@ -291,19 +395,107 @@ int
 Server_jack_deinit(Server *self) {
     int ret;
     PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+    Py_BEGIN_ALLOW_THREADS
     ret = jack_deactivate(be_data->jack_client);
+    Py_END_ALLOW_THREADS
+
     if (ret)
         Server_error(self, "Jack error: cannot deactivate jack client.\n");
+
+    Py_BEGIN_ALLOW_THREADS
     ret = jack_client_close(be_data->jack_client);
+    Py_END_ALLOW_THREADS
+
     if (ret)
         Server_error(self, "Jack error: cannot close client.\n");
+
     free(be_data->jack_in_ports);
     free(be_data->jack_out_ports);
     free(self->audio_be_data);
+
     return ret;
 }
 
 int
+jack_input_port_set_names(Server *self) {
+    int i, err, lsize;
+    char *name;
+    char result[128];
+    PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+    if (PyList_Check(self->jackInputPortNames)) {
+        lsize = PyList_Size(self->jackInputPortNames);
+        for (i=0; i<self->ichnls && i<lsize; i++) {
+            name = PY_STRING_AS_STRING(PyList_GetItem(self->jackInputPortNames, i));
+
+            Py_BEGIN_ALLOW_THREADS
+            err = jack_port_rename(be_data->jack_client, be_data->jack_in_ports[i], name);
+            Py_END_ALLOW_THREADS
+
+            if (err)
+                Server_error(self, "Jack error: cannot change port short name.\n");
+        }
+    }
+    else if (PY_STRING_CHECK(self->jackInputPortNames)) {
+        name = PY_STRING_AS_STRING(self->jackInputPortNames);
+        for (i=0; i<self->ichnls; i++) {
+            sprintf(result, "%s_%d", name, i);
+
+            Py_BEGIN_ALLOW_THREADS
+            err = jack_port_rename(be_data->jack_client, be_data->jack_in_ports[i], result);
+            Py_END_ALLOW_THREADS
+
+            if (err)
+                Server_error(self, "Jack error: cannot change port short name.\n");
+        }
+    }
+    else
+        Server_error(self, "Jack error: input port names must be a string or a list of strings.\n");
+
+    return 0;
+}
+
+int
+jack_output_port_set_names(Server *self) {
+    int i, err, lsize;
+    char *name;
+    char result[128];
+    PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+    if (PyList_Check(self->jackOutputPortNames)) {
+        lsize = PyList_Size(self->jackOutputPortNames);
+        for (i=0; i<self->nchnls && i<lsize; i++) {
+            name = PY_STRING_AS_STRING(PyList_GetItem(self->jackOutputPortNames, i));
+
+            Py_BEGIN_ALLOW_THREADS
+            err = jack_port_rename(be_data->jack_client, be_data->jack_out_ports[i], name);
+            Py_END_ALLOW_THREADS
+
+            if (err)
+                Server_error(self, "Jack error: cannot change port short name.\n");
+        }
+    }
+    else if (PY_STRING_CHECK(self->jackOutputPortNames)) {
+        name = PY_STRING_AS_STRING(self->jackOutputPortNames);
+        for (i=0; i<self->nchnls; i++) {
+            sprintf(result, "%s_%d", name, i);
+
+            Py_BEGIN_ALLOW_THREADS
+            err = jack_port_rename(be_data->jack_client, be_data->jack_out_ports[i], result);
+            Py_END_ALLOW_THREADS
+
+            if (err)
+                Server_error(self, "Jack error: cannot change port short name.\n");
+        }
+    }
+    else
+        Server_error(self, "Jack error: output port names must be a string or a list of strings.\n");
+
+    return 0;
+}
+
+int
 Server_jack_start(Server *self) {
     return 0;
 }
diff --git a/src/engine/ad_portaudio.c b/src/engine/ad_portaudio.c
index 5d3dc89..064210b 100644
--- a/src/engine/ad_portaudio.c
+++ b/src/engine/ad_portaudio.c
@@ -30,7 +30,14 @@ static void portaudio_assert(PaError ecode, const char* cmdName) {
             eText = "???";
         }
         PySys_WriteStdout("portaudio error in %s: %s\n", cmdName, eText);
-        Pa_Terminate();
+
+        if (strcmp(cmdName, "Pa_Initialize") != 0) {
+
+            Py_BEGIN_ALLOW_THREADS
+            Pa_Terminate();
+            Py_END_ALLOW_THREADS
+
+        }
     }
 }
 
@@ -146,7 +153,10 @@ Server_pa_init(Server *self)
     PaSampleFormat sampleFormat;
     PaStreamCallback *streamCallback;
 
+    Py_BEGIN_ALLOW_THREADS
     err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     portaudio_assert(err, "Pa_Initialize");
 
     n = Pa_GetDeviceCount();
@@ -211,7 +221,9 @@ Server_pa_init(Server *self)
     }
 
     if (self->input == -1 && self->output == -1) {
-        if (self->duplex == 1)
+        if (self->duplex == 1) {
+
+            Py_BEGIN_ALLOW_THREADS
             err = Pa_OpenDefaultStream(&be_data->stream,
                                        self->ichnls + self->input_offset,
                                        self->nchnls + self->output_offset,
@@ -220,7 +232,12 @@ Server_pa_init(Server *self)
                                        self->bufferSize,
                                        streamCallback,
                                        (void *) self);
-        else
+            Py_END_ALLOW_THREADS
+
+        }
+        else {
+
+            Py_BEGIN_ALLOW_THREADS
             err = Pa_OpenDefaultStream(&be_data->stream,
                                        0,
                                        self->nchnls + self->output_offset,
@@ -229,9 +246,14 @@ Server_pa_init(Server *self)
                                        self->bufferSize,
                                        streamCallback,
                                        (void *) self);
+            Py_END_ALLOW_THREADS
+
+        }
     }
     else {
-        if (self->duplex == 1)
+        if (self->duplex == 1) {
+
+            Py_BEGIN_ALLOW_THREADS
             err = Pa_OpenStream(&be_data->stream,
                                 &inputParameters,
                                 &outputParameters,
@@ -240,7 +262,12 @@ Server_pa_init(Server *self)
                                 paNoFlag,
                                 streamCallback,
                                 (void *) self);
-        else
+            Py_END_ALLOW_THREADS
+
+        }
+        else {
+
+            Py_BEGIN_ALLOW_THREADS
             err = Pa_OpenStream(&be_data->stream,
                                 (PaStreamParameters *) NULL,
                                 &outputParameters,
@@ -249,6 +276,9 @@ Server_pa_init(Server *self)
                                 paNoFlag,
                                 streamCallback,
                                 (void *) self);
+            Py_END_ALLOW_THREADS
+
+        }
     }
     portaudio_assert(err, "Pa_OpenStream");
     if (err < 0) {
@@ -264,17 +294,31 @@ Server_pa_deinit(Server *self)
     PaError err;
     PyoPaBackendData *be_data = (PyoPaBackendData *) self->audio_be_data;
 
-    if (!Pa_IsStreamStopped(be_data->stream)) {
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_IsStreamStopped(be_data->stream);
+    Py_END_ALLOW_THREADS
+
+    if (!err) {
         self->server_started = 0;
+
+        Py_BEGIN_ALLOW_THREADS
         err = Pa_AbortStream(be_data->stream);
-        portaudio_assert(err, "Pa_AbortStream");
+        Py_END_ALLOW_THREADS
+
+        portaudio_assert(err, "Pa_AbortStream (pa_deinit)");
     }
 
+    Py_BEGIN_ALLOW_THREADS
     err = Pa_CloseStream(be_data->stream);
-    portaudio_assert(err, "Pa_CloseStream");
+    Py_END_ALLOW_THREADS
 
+    portaudio_assert(err, "Pa_CloseStream (pa_deinit)");
+
+    Py_BEGIN_ALLOW_THREADS
     err = Pa_Terminate();
-    portaudio_assert(err, "Pa_Terminate");
+    Py_END_ALLOW_THREADS
+
+    portaudio_assert(err, "Pa_Terminate (pa_deinit)");
 
     free(self->audio_be_data);
     return err;
@@ -286,26 +330,49 @@ Server_pa_start(Server *self)
     PaError err;
     PyoPaBackendData *be_data = (PyoPaBackendData *) self->audio_be_data;
 
-    if (!Pa_IsStreamStopped(be_data->stream)) {
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_IsStreamStopped(be_data->stream);
+    Py_END_ALLOW_THREADS
+
+    if (!err) {
+
+        Py_BEGIN_ALLOW_THREADS
         err = Pa_AbortStream(be_data->stream);
-        portaudio_assert(err, "Pa_AbortStream");
+        Py_END_ALLOW_THREADS
+
+        portaudio_assert(err, "Pa_AbortStream (pa_start)");
     }
+
+    Py_BEGIN_ALLOW_THREADS
     err = Pa_StartStream(be_data->stream);
-    portaudio_assert(err, "Pa_StartStream");
+    Py_END_ALLOW_THREADS
+
+    portaudio_assert(err, "Pa_StartStream (pa_start)");
+
     return err;
 }
 
 int
 Server_pa_stop(Server *self)
 {
+    PaError err;
     PyoPaBackendData *be_data = (PyoPaBackendData *) self->audio_be_data;
 
-    if (!Pa_IsStreamStopped(be_data->stream)) {
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_IsStreamStopped(be_data->stream);
+    Py_END_ALLOW_THREADS
+
+    if (!err) {
 #ifndef _OSX_
-        PaError err = Pa_AbortStream(be_data->stream);
-        portaudio_assert(err, "Pa_AbortStream");
+
+        Py_BEGIN_ALLOW_THREADS
+        err = Pa_AbortStream(be_data->stream);
+        Py_END_ALLOW_THREADS
+
+        portaudio_assert(err, "Pa_AbortStream (pa_stop)");
 #endif
     }
+
     self->server_started = 0;
     self->server_stopped = 1;
     return 0;
@@ -328,7 +395,10 @@ portaudio_count_host_apis(){
     PaError err;
     PaHostApiIndex numApis;
 
+    Py_BEGIN_ALLOW_THREADS
     err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
@@ -337,6 +407,11 @@ portaudio_count_host_apis(){
         numApis = Pa_GetHostApiCount();
         if( numApis < 0 )
             portaudio_assert(numApis, "Pa_GetHostApiCount");
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
         return PyInt_FromLong(numApis);
     }
 }
@@ -346,7 +421,10 @@ portaudio_list_host_apis(){
     PaError err;
     PaHostApiIndex n, i;
 
+    Py_BEGIN_ALLOW_THREADS
     err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 	}
@@ -364,6 +442,11 @@ portaudio_list_host_apis(){
                                   (int)info->defaultOutputDevice);
             }
         }
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
     }
     Py_RETURN_NONE;
 }
@@ -373,13 +456,21 @@ portaudio_get_default_host_api(){
     PaError err;
     PaHostApiIndex i;
 
+    Py_BEGIN_ALLOW_THREADS
     err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
 	}
     else {
         i = Pa_GetDefaultHostApi();
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
         return PyInt_FromLong(i);
     }
 }
@@ -389,7 +480,10 @@ portaudio_count_devices(){
     PaError err;
     PaDeviceIndex numDevices;
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
@@ -398,6 +492,11 @@ portaudio_count_devices(){
         numDevices = Pa_GetDeviceCount();
         if( numDevices < 0 )
             portaudio_assert(numDevices, "Pa_GetDeviceCount");
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
         return PyInt_FromLong(numDevices);
     }
 
@@ -408,7 +507,10 @@ portaudio_list_devices(){
     PaError err;
     PaDeviceIndex n, i;
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
@@ -436,6 +538,11 @@ portaudio_list_devices(){
             }
             PySys_WriteStdout("\n");
         }
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
     }
     Py_RETURN_NONE;
 }
@@ -448,16 +555,17 @@ portaudio_get_devices_infos(){
     inDict = PyDict_New();
     outDict = PyDict_New();
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
-		Py_RETURN_NONE;
 	}
     else {
         n = Pa_GetDeviceCount();
         if (n < 0){
             portaudio_assert(n, "Pa_GetDeviceCount");
-            Py_RETURN_NONE;
         }
         else {
             for (i=0; i < n; ++i){
@@ -465,23 +573,34 @@ portaudio_get_devices_infos(){
                 assert(info);
                 tmpDict = PyDict_New();
                 if (info->maxInputChannels > 0) {
-                    PyDict_SetItemString(tmpDict, "name", PyUnicode_FromString(info->name));
+                    if (PyUnicode_FromString(info->name) == NULL)
+                        PyDict_SetItemString(tmpDict, "name", PyUnicode_FromString("???"));
+                    else
+                        PyDict_SetItemString(tmpDict, "name", PyUnicode_FromString(info->name));
                     PyDict_SetItemString(tmpDict, "host api index", PyInt_FromLong((int)info->hostApi));
                     PyDict_SetItemString(tmpDict, "default sr", PyInt_FromLong((int)info->defaultSampleRate));
                     PyDict_SetItemString(tmpDict, "latency", PyFloat_FromDouble((float)info->defaultLowInputLatency));
                     PyDict_SetItem(inDict, PyInt_FromLong(i), PyDict_Copy(tmpDict));
                 }
                 if (info->maxOutputChannels > 0) {
-                    PyDict_SetItemString(tmpDict, "name", PyUnicode_FromString(info->name));
+                    if (PyUnicode_FromString(info->name) == NULL)
+                        PyDict_SetItemString(tmpDict, "name", PyUnicode_FromString("???"));
+                    else
+                        PyDict_SetItemString(tmpDict, "name", PyUnicode_FromString(info->name));
                     PyDict_SetItemString(tmpDict, "host api index", PyInt_FromLong((int)info->hostApi));
                     PyDict_SetItemString(tmpDict, "default sr", PyInt_FromLong((int)info->defaultSampleRate));
                     PyDict_SetItemString(tmpDict, "latency", PyFloat_FromDouble((float)info->defaultLowOutputLatency));
                     PyDict_SetItem(outDict, PyInt_FromLong(i), PyDict_Copy(tmpDict));
                 }
             }
-            return Py_BuildValue("(OO)", inDict, outDict);
         }
+
+        Py_BEGIN_ALLOW_THREADS
+        err = Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
     }
+    return Py_BuildValue("(OO)", inDict, outDict);
 }
 
 PyObject *
@@ -493,29 +612,38 @@ portaudio_get_output_devices(){
     list = PyList_New(0);
     list_index = PyList_New(0);
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
-		Py_RETURN_NONE;
 	}
     else {
         n = Pa_GetDeviceCount();
         if (n < 0){
             portaudio_assert(n, "Pa_GetDeviceCount");
-            Py_RETURN_NONE;
         }
         else {
             for (i=0; i < n; ++i){
                 const PaDeviceInfo *info=Pa_GetDeviceInfo(i);
                 assert(info);
-                if (info->maxOutputChannels > 0){
-                    PyList_Append(list, PyUnicode_FromString(info->name));
+                if (info->maxOutputChannels > 0) {
                     PyList_Append(list_index, PyInt_FromLong(i));
+                    if (PyUnicode_FromString(info->name) == NULL)
+                        PyList_Append(list, PyUnicode_FromString("???"));
+                    else
+                        PyList_Append(list, PyUnicode_FromString(info->name));
                 }
             }
-            return Py_BuildValue("OO", list, list_index);
         }
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
     }
+    return Py_BuildValue("OO", list, list_index);
 }
 
 PyObject *
@@ -523,7 +651,10 @@ portaudio_get_output_max_channels(PyObject *self, PyObject *arg){
     PaError err;
     PaDeviceIndex n, i = PyInt_AsLong(arg);
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
@@ -536,6 +667,11 @@ portaudio_get_output_max_channels(PyObject *self, PyObject *arg){
         }
         else {
             const PaDeviceInfo *info=Pa_GetDeviceInfo(i);
+
+            Py_BEGIN_ALLOW_THREADS
+            Pa_Terminate();
+            Py_END_ALLOW_THREADS
+
             assert(info);
             return PyInt_FromLong(info->maxOutputChannels);
         }
@@ -547,7 +683,10 @@ portaudio_get_input_max_channels(PyObject *self, PyObject *arg){
     PaError err;
     PaDeviceIndex n, i = PyInt_AsLong(arg);
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
@@ -560,6 +699,11 @@ portaudio_get_input_max_channels(PyObject *self, PyObject *arg){
         }
         else {
             const PaDeviceInfo *info=Pa_GetDeviceInfo(i);
+
+            Py_BEGIN_ALLOW_THREADS
+            Pa_Terminate();
+            Py_END_ALLOW_THREADS
+
             assert(info);
             return PyInt_FromLong(info->maxInputChannels);
         }
@@ -575,29 +719,38 @@ portaudio_get_input_devices(){
     list = PyList_New(0);
     list_index = PyList_New(0);
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
-		Py_RETURN_NONE;
 	}
     else {
         n = Pa_GetDeviceCount();
         if (n < 0){
             portaudio_assert(n, "Pa_GetDeviceCount");
-            Py_RETURN_NONE;
         }
         else {
             for (i=0; i < n; ++i){
                 const PaDeviceInfo *info=Pa_GetDeviceInfo(i);
                 assert(info);
-                if (info->maxInputChannels > 0){
-                    PyList_Append(list, PyUnicode_FromString(info->name));
+                if (info->maxInputChannels > 0) {
                     PyList_Append(list_index, PyInt_FromLong(i));
+                    if (PyUnicode_FromString(info->name) == NULL)
+                        PyList_Append(list, PyUnicode_FromString("???"));
+                    else
+                        PyList_Append(list, PyUnicode_FromString(info->name));
                 }
             }
-            return Py_BuildValue("OO", list, list_index);
         }
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
     }
+    return Py_BuildValue("OO", list, list_index);
 }
 
 PyObject *
@@ -605,16 +758,23 @@ portaudio_get_default_input(){
     PaError err;
     PaDeviceIndex i;
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
 	}
     else {
         i = Pa_GetDefaultInputDevice();
+
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
         return PyInt_FromLong(i);
     }
-
 }
 
 PyObject *
@@ -622,14 +782,21 @@ portaudio_get_default_output(){
     PaError err;
     PaDeviceIndex i;
 
-	err = Pa_Initialize();
+    Py_BEGIN_ALLOW_THREADS
+    err = Pa_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (err != paNoError) {
         portaudio_assert(err, "Pa_Initialize");
 		Py_RETURN_NONE;
 	}
     else {
         i = Pa_GetDefaultOutputDevice();
-        return PyInt_FromLong(i);
 
+        Py_BEGIN_ALLOW_THREADS
+        Pa_Terminate();
+        Py_END_ALLOW_THREADS
+
+        return PyInt_FromLong(i);
     }
 }
diff --git a/src/engine/md_portmidi.c b/src/engine/md_portmidi.c
index 92cae50..9910305 100644
--- a/src/engine/md_portmidi.c
+++ b/src/engine/md_portmidi.c
@@ -61,7 +61,10 @@ Server_pm_init(Server *self)
         return 0;
     }
 
+    Py_BEGIN_ALLOW_THREADS
     pmerr = Pm_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (pmerr) {
         Server_warning(self, "Portmidi warning: could not initialize Portmidi: %s\n", Pm_GetErrorText(pmerr));
         self->withPortMidi = 0;
@@ -89,8 +92,12 @@ Server_pm_init(Server *self)
                 const PmDeviceInfo *info = Pm_GetDeviceInfo(self->midi_input);
                 if (info != NULL) {
                     if (info->input) {
+
+                        Py_BEGIN_ALLOW_THREADS
                         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);
+                        Py_END_ALLOW_THREADS
+
                         if (pmerr) {
                             Server_warning(self,
                                  "Portmidi warning: could not open midi input %d (%s): %s\n",
@@ -115,12 +122,20 @@ Server_pm_init(Server *self)
             else if (self->midi_input >= num_devices) {
                 Server_debug(self, "Midi input device : all!\n");
                 self->midiin_count = 0;
+
+                Py_BEGIN_ALLOW_THREADS
                 Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
+                Py_END_ALLOW_THREADS
+
                 for (i=0; i<num_devices; i++) {
                     const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
                     if (info != NULL) {
                         if (info->input) {
+
+                            Py_BEGIN_ALLOW_THREADS
                             pmerr = Pm_OpenInput(&be_data->midiin[self->midiin_count], i, NULL, 100, NULL, NULL);
+                            Py_END_ALLOW_THREADS
+
                             if (pmerr) {
                                 Server_warning(self,
                                      "Portmidi warning: could not open midi input %d (%s): %s\n",
@@ -148,16 +163,24 @@ Server_pm_init(Server *self)
                 const PmDeviceInfo *outinfo = Pm_GetDeviceInfo(self->midi_output);
                 if (outinfo != NULL) {
                     if (outinfo->output) {
+
+                        Py_BEGIN_ALLOW_THREADS
                         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);
+                        Py_END_ALLOW_THREADS
+
                         if (pmerr) {
                             Server_warning(self,
                                      "Portmidi warning: could not open midi output %d (%s): %s\n",
                                      self->midi_output, outinfo->name, Pm_GetErrorText(pmerr));
                             self->withPortMidiOut = 0;
+
+                            Py_BEGIN_ALLOW_THREADS
                             if (Pt_Started())
                                 Pt_Stop();
+                            Py_END_ALLOW_THREADS
+
                         }
                         else {
                             Server_debug(self, "Midi output (%s) opened.\n", outinfo->name);
@@ -177,13 +200,21 @@ Server_pm_init(Server *self)
             else if (self->midi_output >= num_devices) {
                 Server_debug(self, "Midi output device : all!\n");
                 self->midiout_count = 0;
+
+                Py_BEGIN_ALLOW_THREADS
                 if (!Pt_Started())
                     Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */
+                Py_END_ALLOW_THREADS
+
                 for (i=0; i<num_devices; i++) {
                     const PmDeviceInfo *outinfo = Pm_GetDeviceInfo(i);
                     if (outinfo != NULL) {
                         if (outinfo->output) {
+
+                            Py_BEGIN_ALLOW_THREADS
                             pmerr = Pm_OpenOutput(&be_data->midiout[self->midiout_count], i, NULL, 100, NULL, NULL, 1);
+                            Py_END_ALLOW_THREADS
+
                             if (pmerr) {
                                 Server_warning(self,
                                      "Portmidi warning: could not open midi output %d (%s): %s\n",
@@ -206,9 +237,13 @@ Server_pm_init(Server *self)
             }
 
             if (self->withPortMidi == 0 && self->withPortMidiOut == 0) {
+
+                Py_BEGIN_ALLOW_THREADS
                 if (Pt_Started())
                     Pt_Stop();
                 Pm_Terminate();
+                Py_END_ALLOW_THREADS
+
                 Server_warning(self, "Portmidi closed.\n");
                 ret = -1;
             }
@@ -217,7 +252,11 @@ Server_pm_init(Server *self)
             Server_warning(self, "Portmidi warning: no midi device found!\nPortmidi closed.\n");
             self->withPortMidi = 0;
             self->withPortMidiOut = 0;
+
+            Py_BEGIN_ALLOW_THREADS
             Pm_Terminate();
+            Py_END_ALLOW_THREADS
+
             ret = -1;
         }
     }
@@ -235,28 +274,43 @@ Server_pm_deinit(Server *self)
 {
     int i = 0;
 
-    //PyoPmBackendData *be_data = (PyoPmBackendData *) self->midi_be_data;
+    PyoPmBackendData *be_data = (PyoPmBackendData *) self->midi_be_data;
 
     /* An opened stream should be properly closed 
        but Pm_Close segfaults now and then so...
        This hack need to be tested on Windows and OSX. */
 
+    /* Restored with macros to protect Close statements. */
+
     if (self->withPortMidi == 1) {
         for (i=0; i<self->midiin_count; i++) {
-            //if (be_data->midiin[i] != NULL)
-                //Pm_Close(be_data->midiin[i]);
+            if (be_data->midiin[i] != NULL) {
+
+                Py_BEGIN_ALLOW_THREADS
+                Pm_Close(be_data->midiin[i]);
+                Py_END_ALLOW_THREADS
+
+            }
         }
     }
     if (self->withPortMidiOut == 1) {
         for (i=0; i<self->midiout_count; i++) {
-            //if (be_data->midiout[i] != NULL)
-                //Pm_Close(be_data->midiout[i]);
+            if (be_data->midiout[i] != NULL)
+
+                Py_BEGIN_ALLOW_THREADS
+                Pm_Close(be_data->midiout[i]);
+                Py_END_ALLOW_THREADS
+
         }
     }
     if (self->withPortMidi == 1 || self->withPortMidiOut == 1) {
+
+        Py_BEGIN_ALLOW_THREADS
         if (Pt_Started())
             Pt_Stop();
         Pm_Terminate();
+        Py_END_ALLOW_THREADS
+
     }
     self->withPortMidi = 0;
     self->withPortMidiOut = 0;
diff --git a/src/engine/midilistenermodule.c b/src/engine/midilistenermodule.c
index 3c9deac..df3f47d 100644
--- a/src/engine/midilistenermodule.c
+++ b/src/engine/midilistenermodule.c
@@ -136,10 +136,13 @@ static PyObject * MidiListener_play(MidiListener *self) {
     int i, num_devices, lsize, mididev;
     PmError pmerr;
 
+    Py_BEGIN_ALLOW_THREADS
     /* always start the timer before you start midi */
     Pt_Start(1, &process_midi, (void *)self);
     
     pmerr = Pm_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (pmerr) {
         PySys_WriteStdout("Portmidi warning: could not initialize Portmidi: %s\n", Pm_GetErrorText(pmerr));
     }
@@ -156,7 +159,11 @@ static PyObject * MidiListener_play(MidiListener *self) {
                 const PmDeviceInfo *info = Pm_GetDeviceInfo(mididev);
                 if (info != NULL) {
                     if (info->input) {
+
+                        Py_BEGIN_ALLOW_THREADS
                         pmerr = Pm_OpenInput(&self->midiin[0], mididev, NULL, 100, NULL, NULL);
+                        Py_END_ALLOW_THREADS
+
                         if (pmerr) {
                             PySys_WriteStdout("Portmidi warning: could not open midi input %d (%s): %s\n",
                                  mididev, info->name, Pm_GetErrorText(pmerr));
@@ -174,7 +181,11 @@ static PyObject * MidiListener_play(MidiListener *self) {
                     const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
                     if (info != NULL) {
                         if (info->input) {
+
+                            Py_BEGIN_ALLOW_THREADS
                             pmerr = Pm_OpenInput(&self->midiin[self->midicount], i, NULL, 100, NULL, NULL);
+                            Py_END_ALLOW_THREADS
+
                             if (pmerr) {
                                 PySys_WriteStdout("Portmidi warning: could not open midi input %d (%s): %s\n",
                                         i, info->name, Pm_GetErrorText(pmerr));
@@ -196,7 +207,11 @@ static PyObject * MidiListener_play(MidiListener *self) {
                 const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
                 if (info != NULL) {
                     if (info->input) {
+
+                        Py_BEGIN_ALLOW_THREADS
                         pmerr = Pm_OpenInput(&self->midiin[self->midicount], i, NULL, 100, NULL, NULL);
+                        Py_END_ALLOW_THREADS
+
                         if (pmerr) {
                             PySys_WriteStdout("Portmidi warning: could not open midi input %d (%s): %s\n",
                                     i, info->name, Pm_GetErrorText(pmerr));
@@ -224,11 +239,15 @@ static PyObject * MidiListener_play(MidiListener *self) {
 
 static PyObject * MidiListener_stop(MidiListener *self) { 
     int i;
+
+    Py_BEGIN_ALLOW_THREADS
     Pt_Stop();
     for (i=0; i<self->midicount; i++) {
         Pm_Close(self->midiin[i]);
     }
-    Pm_Terminate();    
+    Pm_Terminate();
+    Py_END_ALLOW_THREADS
+
     self->active = 0;
 	Py_INCREF(Py_None);
 	return Py_None;
@@ -375,10 +394,13 @@ static PyObject * MidiDispatcher_play(MidiDispatcher *self) {
     int i, num_devices, lsize, mididev;
     PmError pmerr;
 
+    Py_BEGIN_ALLOW_THREADS
     /* always start the timer before you start midi */
     Pt_Start(1, 0, 0);
     
     pmerr = Pm_Initialize();
+    Py_END_ALLOW_THREADS
+
     if (pmerr) {
         PySys_WriteStdout("Portmidi warning: could not initialize Portmidi: %s\n", Pm_GetErrorText(pmerr));
     }
@@ -395,7 +417,11 @@ static PyObject * MidiDispatcher_play(MidiDispatcher *self) {
                 const PmDeviceInfo *info = Pm_GetDeviceInfo(mididev);
                 if (info != NULL) {
                     if (info->output) {
+
+                        Py_BEGIN_ALLOW_THREADS
                         pmerr = Pm_OpenOutput(&self->midiout[0], mididev, NULL, 100, NULL, NULL, 1);
+                        Py_END_ALLOW_THREADS
+
                         if (pmerr) {
                             PySys_WriteStdout("Portmidi warning: could not open midi output %d (%s): %s\n",
                                  mididev, info->name, Pm_GetErrorText(pmerr));
@@ -413,7 +439,11 @@ static PyObject * MidiDispatcher_play(MidiDispatcher *self) {
                     const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
                     if (info != NULL) {
                         if (info->output) {
+
+                            Py_BEGIN_ALLOW_THREADS
                             pmerr = Pm_OpenOutput(&self->midiout[self->midicount], i, NULL, 100, NULL, NULL, 1);
+                            Py_END_ALLOW_THREADS
+
                             if (pmerr) {
                                 PySys_WriteStdout("Portmidi warning: could not open midi output %d (%s): %s\n",
                                         i, info->name, Pm_GetErrorText(pmerr));
@@ -435,7 +465,11 @@ static PyObject * MidiDispatcher_play(MidiDispatcher *self) {
                 const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
                 if (info != NULL) {
                     if (info->output) {
+
+                        Py_BEGIN_ALLOW_THREADS
                         pmerr = Pm_OpenOutput(&self->midiout[self->midicount], i, NULL, 100, NULL, NULL, 1);
+                        Py_END_ALLOW_THREADS
+
                         if (pmerr) {
                             PySys_WriteStdout("Portmidi warning: could not open midi output %d (%s): %s\n",
                                     i, info->name, Pm_GetErrorText(pmerr));
@@ -459,11 +493,15 @@ static PyObject * MidiDispatcher_play(MidiDispatcher *self) {
 
 static PyObject * MidiDispatcher_stop(MidiDispatcher *self) { 
     int i;
+
+    Py_BEGIN_ALLOW_THREADS
     Pt_Stop();
     for (i=0; i<self->midicount; i++) {
         Pm_Close(self->midiout[i]);
     }
     Pm_Terminate();    
+    Py_END_ALLOW_THREADS
+
     self->active = 0;
 	Py_INCREF(Py_None);
 	return Py_None;
diff --git a/src/engine/pyomodule.c b/src/engine/pyomodule.c
index 7b3da65..a80a56b 100644
--- a/src/engine/pyomodule.c
+++ b/src/engine/pyomodule.c
@@ -138,7 +138,7 @@ AUDIO devices:\n\
 
 #define portaudio_get_devices_infos_info \
 "\nReturns informations about all devices found by Portaudio.\n\n\
-This function returns two dictionaries, one containing a dictionary for each input device and one containing a dictionary for each output device.\
+This function returns two dictionaries, one containing a dictionary for each input device and one containing a dictionary for each output device. \
 Keys of outer dictionaries are the device index as returned by Portaudio. Keys of inner dictionaries are: 'name', 'host api index', 'default sr' and 'latency'.\n\n\
 >>> inputs, outputs = pa_get_devices_infos()\n\
 >>> print '- Inputs:'\n\
diff --git a/src/engine/servermodule.c b/src/engine/servermodule.c
index 7823c76..99d7e9a 100644
--- a/src/engine/servermodule.c
+++ b/src/engine/servermodule.c
@@ -49,6 +49,9 @@ int Server_jack_init(Server *self) { return -10; };
 int Server_jack_deinit(Server *self) { return 0; };
 int Server_jack_start(Server *self) { return 0; };
 int Server_jack_stop(Server *self) { return 0; };
+int jack_input_port_set_names(Server *self) { return 0; };
+int jack_output_port_set_names(Server *self) { return 0; };
+
 #endif
 
 #ifdef USE_COREAUDIO
@@ -570,6 +573,8 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     self->jackautoout = 1;
     self->jackAutoConnectInputPorts = PyList_New(0);
     self->jackAutoConnectOutputPorts = PyList_New(0);
+    self->isJackTransportSlave = 0;
+    self->jack_transport_state = 0;
     self->samplingRate = 44100.0;
     self->nchnls = 2;
     self->ichnls = 2;
@@ -907,6 +912,58 @@ Server_setJackAutoConnectOutputPorts(Server *self, PyObject *arg)
 }
 
 static PyObject *
+Server_setJackInputPortNames(Server *self, PyObject *arg)
+{
+    PyObject *tmp;
+
+    if (arg != NULL) {
+        if (PyList_Check(arg) || PY_STRING_CHECK(arg)) {
+            tmp = arg;
+            Py_XDECREF(self->jackInputPortNames);
+            Py_INCREF(tmp);
+            self->jackInputPortNames = tmp;
+
+            jack_input_port_set_names(self);
+        }
+    }
+
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+Server_setJackOutputPortNames(Server *self, PyObject *arg)
+{
+    PyObject *tmp;
+
+    if (arg != NULL) {
+        if (PyList_Check(arg) || PY_STRING_CHECK(arg)) {
+            tmp = arg;
+            Py_XDECREF(self->jackOutputPortNames);
+            Py_INCREF(tmp);
+            self->jackOutputPortNames = tmp;
+
+            jack_output_port_set_names(self);
+        }
+    }
+
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+Server_setIsJackTransportSlave(Server *self, PyObject *arg)
+{
+    if (self->server_booted) {
+        Server_warning(self,"Can't change isJackTransportSlave mode for booted server.\n");
+        Py_RETURN_NONE;
+    }
+    if (arg != NULL) {
+        if (PyInt_Check(arg))
+            self->isJackTransportSlave = PyInt_AsLong(arg);
+    }
+    Py_RETURN_NONE;
+}
+
+static PyObject *
 Server_setGlobalSeed(Server *self, PyObject *arg)
 {
     self->globalSeed = 0;
@@ -1110,7 +1167,7 @@ Server_shutdown(Server *self)
     Py_RETURN_NONE;
 }
 
-static PyObject *
+PyObject *
 Server_boot(Server *self, PyObject *arg)
 {
     int i, audioerr = 0, midierr = 0;
@@ -1221,7 +1278,7 @@ Server_boot(Server *self, PyObject *arg)
     Py_RETURN_NONE;
 }
 
-static PyObject *
+PyObject *
 Server_start(Server *self)
 {
     int err = -1;
@@ -1278,6 +1335,9 @@ Server_start(Server *self)
         Server_error(self, "Error starting server.\n");
     }
 
+    if (self->withGUI && PyObject_HasAttrString((PyObject *)self->GUI, "setStartButtonState"))
+        PyObject_CallMethod((PyObject *)self->GUI, "setStartButtonState", "i", 1);
+
     Py_RETURN_NONE;
 }
 
@@ -1312,9 +1372,8 @@ Server_stop(Server *self)
         self->server_started = 0;
     }
 
-    /* This call is needed to recover from thread fork with python3/jack on debian.*/
-    /* TODO: Need to be tested with other OSes and audio driver. */
-    //PyOS_AfterFork();
+    if (self->withGUI && PyObject_HasAttrString((PyObject *)self->GUI, "setStartButtonState"))
+        PyObject_CallMethod((PyObject *)self->GUI, "setStartButtonState", "i", 0);
 
     Py_RETURN_NONE;
 }
@@ -1953,6 +2012,9 @@ static PyMethodDef Server_methods[] = {
     {"setJackAuto", (PyCFunction)Server_setJackAuto, METH_VARARGS, "Tells the server to auto-connect Jack ports (0 = disable, 1 = enable)."},
     {"setJackAutoConnectInputPorts", (PyCFunction)Server_setJackAutoConnectInputPorts, METH_O, "Sets a list of ports to auto-connect inputs when using Jack."},
     {"setJackAutoConnectOutputPorts", (PyCFunction)Server_setJackAutoConnectOutputPorts, METH_O, "Sets a list of ports to auto-connect outputs when using Jack."},
+    {"setJackInputPortNames", (PyCFunction)Server_setJackInputPortNames, METH_O, "Sets the short name of input ports for jack server."},
+    {"setJackOutputPortNames", (PyCFunction)Server_setJackOutputPortNames, METH_O, "Sets the short name of output ports for jack server."},
+    {"setIsJackTransportSlave", (PyCFunction)Server_setIsJackTransportSlave, METH_O, "Sets if the server's start/stop is slave of jack transport."},
     {"setGlobalSeed", (PyCFunction)Server_setGlobalSeed, METH_O, "Sets the server's global seed for random objects."},
     {"setAmp", (PyCFunction)Server_setAmp, METH_O, "Sets the overall amplitude."},
     {"setAmpCallable", (PyCFunction)Server_setAmpCallable, METH_O, "Sets the Server's GUI callable object."},
diff --git a/utils/setup.py b/utils/setup.py
index e9e05e9..29ccb3b 100644
--- a/utils/setup.py
+++ b/utils/setup.py
@@ -16,11 +16,11 @@ OPTIONS = {'argv_emulation': False,
                'CFBundleExecutable': 'E-Pyo',
                'CFBundleIconFile': 'E-PyoIcon.icns',
                'CFBundleIdentifier': 'com.ajaxsoundstudio.E-Pyo',
-               'CFBundleInfoDictionaryVersion': '0.8.3',
+               'CFBundleInfoDictionaryVersion': '0.8.4',
                'CFBundleName': 'E-Pyo',
                'CFBundlePackageType': 'APPL',
-               'CFBundleShortVersionString': '0.8.3',
-               'CFBundleVersion': '0.8.3',
+               'CFBundleShortVersionString': '0.8.4',
+               'CFBundleVersion': '0.8.4',
                'CFBundleDocumentTypes': [{'CFBundleTypeOSTypes': ['TEXT'],
                                           'CFBundleTypeExtensions': ['py'],
                                           'CFBundleTypeRole': 'Editor',
diff --git a/work-in-progress/EventParser.py b/work-in-progress/EventParser.py
new file mode 100644
index 0000000..6d36144
--- /dev/null
+++ b/work-in-progress/EventParser.py
@@ -0,0 +1,63 @@
+"""
+EventParser first draft (very very alpha stage!).
+
+Score format:
+
+Class_name => starttime duration *args **kwargs
+
+Class_name {class reference}: The name of the class which should play the event,
+starttime {float}: Start time of the event, in seconds. 
+duration {float}: Start time of the event, in seconds.
+*args {list of floats}: user-defined arguments (optional).
+**kwargs {dictionary}: user-defined keyword arguments (optional).
+ 
+"""
+# Notes:
+# We should be able to define an event using multiple lines.
+# Actually, the parser uses splitlines() to separate events.
+# How to automatically use globals() from the __main__ module, I don't
+# want to ask the user to pass globals() to have access to instrument classes.
+
+class EventParser:
+    def __init__(self, server, score="", globals=None):
+        self.server = server
+        self.globals = globals
+        self._instruments = {}
+        self.parse(score)
+
+    def extractKwArgs(self, args):
+        kwargs = ""
+        if "{" in args:
+            p1 = args.find("{")
+            args, kwargs = args[:p1], args[p1:]
+        if kwargs:
+            kwargs = eval(kwargs)
+        return args, kwargs
+
+    def parse(self, score):
+        for line in score.splitlines():
+            if line.strip() == "":
+                continue
+            instr, args = line.split("=>")
+            instr = instr.strip()
+            args, kwargs = self.extractKwArgs(args)
+            args = [float(x) for x in args.split()] # should also eval variable
+                                                    # names, not just numbers
+            if instr not in self._instruments:
+                self._instruments[instr] = [{"args": args, "kwargs": kwargs}]
+            else:
+                self._instruments[instr].append({"args": args, "kwargs": kwargs})
+
+    def play(self):
+        self._playing = []
+        for instr in self._instruments:
+            for event in self._instruments[instr]:
+                self.server.setGlobalDel(event["args"][0])
+                self.server.setGlobalDur(event["args"][1] + 0.2)
+                if not event["kwargs"]:
+                    self._playing.append(self.globals[instr](*event["args"][1:]))
+                else:
+                    self._playing.append(self.globals[instr](*event["args"][1:], 
+                                                             **event["kwargs"]))
+        self.server.setGlobalDel(0)
+        self.server.setGlobalDur(0)
diff --git a/work-in-progress/keyboard_widget.py b/work-in-progress/keyboard_widget.py
new file mode 100644
index 0000000..bfa1693
--- /dev/null
+++ b/work-in-progress/keyboard_widget.py
@@ -0,0 +1,292 @@
+import sys
+import wx
+
+KEYBOARD_BACKGROUND_COLOUR = "#CCCCCC"
+
+class Keyboard(wx.Panel):
+    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, poly=64, style=wx.TAB_TRAVERSAL):
+        wx.Panel.__init__(self, parent, id, pos, size, style)
+        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)  
+        self.SetBackgroundColour(KEYBOARD_BACKGROUND_COLOUR)
+        self.parent = parent
+
+        self.poly = poly
+        self.gap = 0
+        self.offset = 12
+        self.w1 = 15
+        self.w2 = self.w1 // 2 + 1
+        self.hold = 1
+        self.keyPressed = None
+
+        self.Bind(wx.EVT_LEFT_DOWN, self.MouseDown)
+        self.Bind(wx.EVT_LEFT_UP, self.MouseUp)
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+
+        self.white = (0, 2, 4, 5, 7, 9, 11)
+        self.black = (1, 3, 6, 8, 10)
+        self.whiteSelected = []
+        self.blackSelected = []
+        self.whiteVelocities = {}
+        self.blackVelocities = {}
+        self.whiteKeys = []
+        self.blackKeys = []
+
+        wx.CallAfter(self.setRects)
+   
+    def getNotes(self):
+        notes = []
+        for key in self.whiteSelected:
+            notes.append((self.white[key % 7] + key // 7 * 12  + self.offset,
+                          127 - self.whiteVelocities[key]))
+        for key in self.blackSelected:
+            notes.append((self.black[key % 5] + key // 5 * 12  + self.offset,
+                          127 - self.blackVelocities[key]))
+        notes.sort()
+        return notes
+
+    def reset(self):
+        self.whiteSelected = []
+        self.blackSelected = []
+        self.whiteVelocities = {}
+        self.blackVelocities = {}
+        wx.CallAfter(self.Refresh)
+    
+    def setPoly(self, poly):
+        self.poly = poly
+    
+    def setRects(self):
+        w, h = self.GetSize()
+        self.offRec = wx.Rect(w - 55, 0, 21, h)
+        self.holdRec = wx.Rect(w - 34, 0, 21, h)
+        num = w // self.w1
+        self.gap = w - num * self.w1
+        self.whiteKeys = [wx.Rect(i * self.w1, 0, self.w1 - 1, h) for i in range(num)]
+        self.blackKeys = []
+        height2 = h * 4 // 7
+        for i in range(num // 7 + 1):
+            space2 = self.w1 * 7 * i
+            off = self.w1 // 2 + space2 + 3
+            self.blackKeys.append(wx.Rect(off, 0, self.w2, height2))
+            off += self.w1
+            self.blackKeys.append(wx.Rect(off, 0, self.w2, height2))
+            off += self.w1 * 2
+            self.blackKeys.append(wx.Rect(off, 0, self.w2, height2))
+            off += self.w1
+            self.blackKeys.append(wx.Rect(off, 0, self.w2, height2))
+            off += self.w1
+            self.blackKeys.append(wx.Rect(off, 0, self.w2, height2))
+        wx.CallAfter(self.Refresh)
+    
+    def OnSize(self, evt):
+        self.setRects()
+        wx.CallAfter(self.Refresh)
+
+    def MouseUp(self, evt):
+        if not self.hold and self.keyPressed is not None:
+            key = self.keyPressed[0]
+            pit = self.keyPressed[1]
+            if key in self.blackSelected:
+                self.blackSelected.remove(key)
+                del self.blackVelocities[key]
+            if key in self.whiteSelected:
+                self.whiteSelected.remove(key)
+                del self.whiteVelocities[key]
+            note = (pit, 0)
+            self.keyPressed = None
+            wx.CallAfter(self.Refresh)
+
+    def MouseDown(self, evt):
+        w,h = self.GetSize()
+        pos = evt.GetPosition()
+        if self.holdRec.Contains(pos):
+            if self.hold:
+                self.hold = 0
+            else:
+                self.hold = 1
+            wx.CallAfter(self.Refresh)
+            return
+        if self.offUpRec.Contains(pos):
+            self.offset += 12
+            if self.offset > 60:
+                self.offset = 60
+            wx.CallAfter(self.Refresh)
+            return
+        if self.offDownRec.Contains(pos):
+            self.offset -= 12
+            if self.offset < 0:
+                self.offset = 0
+            wx.CallAfter(self.Refresh)
+            return
+
+        total = len(self.blackSelected) + len(self.whiteSelected)
+        scanWhite = True
+        note = None
+        if self.hold:
+            for i, rec in enumerate(self.blackKeys):
+                if rec.Contains(pos):
+                    pit = self.black[i % 5] + i // 5 * 12  + self.offset
+                    if i in self.blackSelected:
+                        self.blackSelected.remove(i)
+                        del self.blackVelocities[i]
+                        vel = 0
+                    else:
+                        hb = h * 4 // 7
+                        vel = (hb - pos[1]) * 127 // hb
+                        if total < self.poly:
+                            self.blackSelected.append(i)
+                            self.blackVelocities[i] = int(127 - vel)
+                    note = (pit, vel)
+                    scanWhite = False
+                    break
+            if scanWhite:
+                for i, rec in enumerate(self.whiteKeys):
+                    if rec.Contains(pos):
+                        pit = self.white[i % 7] + i // 7 * 12  + self.offset
+                        if i in self.whiteSelected:
+                            self.whiteSelected.remove(i)
+                            del self.whiteVelocities[i]
+                            vel = 0
+                        else:
+                            vel = (h - pos[1]) * 127 // h
+                            if total < self.poly:
+                                self.whiteSelected.append(i)
+                                self.whiteVelocities[i] = int(127 - vel)
+                        note = (pit, vel)
+                        break
+            if note:
+                if note[1] == 0:
+                    pass
+                elif total < self.poly:
+                    pass
+        else:
+            self.keyPressed = None
+            for i, rec in enumerate(self.blackKeys):
+                if rec.Contains(pos):
+                    pit = self.black[i % 5] + i // 5 * 12  + self.offset
+                    if i not in self.blackSelected:
+                        hb = h * 4 // 7
+                        vel = (hb - pos[1]) * 127 // hb
+                        if total < self.poly:
+                            self.blackSelected.append(i)
+                            self.blackVelocities[i] = int(127 - vel)
+                    note = (pit, vel)
+                    self.keyPressed = (i, pit)
+                    scanWhite = False
+                    break
+            if scanWhite:
+                for i, rec in enumerate(self.whiteKeys):
+                    if rec.Contains(pos):
+                        pit = self.white[i % 7] + i // 7 * 12 + self.offset
+                        if i not in self.whiteSelected:
+                            vel = (h - pos[1]) * 127 // h
+                            if total < self.poly:
+                                self.whiteSelected.append(i)
+                                self.whiteVelocities[i] = int(127 - vel)
+                        note = (pit, vel)
+                        self.keyPressed = (i, pit)
+                        break
+            if note:
+                if total < self.poly:
+                    pass
+        wx.CallAfter(self.Refresh)
+    
+    def OnPaint(self, evt):
+        w,h = self.GetSize()
+        dc = wx.AutoBufferedPaintDC(self)
+        dc.SetBrush(wx.Brush("#000000", wx.SOLID))
+        dc.Clear()
+        dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID))
+        dc.DrawRectangle(0, 0, w, h)
+
+        if sys.platform == "darwin":
+            dc.SetFont(wx.Font(12, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
+                               wx.FONTWEIGHT_BOLD))
+        else:
+            dc.SetFont(wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL,
+                               wx.FONTWEIGHT_BOLD))
+
+        for i, rec in enumerate(self.whiteKeys):
+            if i in self.whiteSelected:
+                amp = int(self.whiteVelocities[i] * 1.5)
+                dc.GradientFillLinear(rec, (250, 250, 250), (amp, amp, amp), wx.SOUTH)
+                dc.SetBrush(wx.Brush("#CCCCCC", wx.SOLID))
+                dc.SetPen(wx.Pen("#CCCCCC", width=1, style=wx.SOLID))
+            else:
+                dc.SetBrush(wx.Brush("#FFFFFF", wx.SOLID))
+                dc.SetPen(wx.Pen("#FFFFFF", width=1, style=wx.SOLID))
+                dc.DrawRectangle(rec)
+            if i == (35 - (7 * (self.offset // 12))):
+                if i in self.whiteSelected:
+                    dc.SetTextForeground("#FFFFFF")
+                else:
+                    dc.SetTextForeground("#000000")
+                dc.DrawText("C", rec[0] + 3, rec[3] - 15)
+    
+        dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID))
+        for i, rec in enumerate(self.blackKeys):
+            if i in self.blackSelected:
+                amp = int(self.blackVelocities[i] * 1.5)
+                dc.GradientFillLinear(rec, (250, 250, 250), (amp, amp, amp), wx.SOUTH)
+                dc.DrawLine(rec[0], 0, rec[0], rec[3])
+                dc.DrawLine(rec[0] + rec[2], 0, rec[0] + rec[2], rec[3])
+                dc.DrawLine(rec[0], rec[3], rec[0] + rec[2], rec[3])
+                dc.SetBrush(wx.Brush("#DDDDDD", wx.SOLID))
+            else:
+                dc.SetBrush(wx.Brush("#000000", wx.SOLID))
+                dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID))
+                dc.DrawRectangle(rec)
+    
+        dc.SetBrush(wx.Brush(KEYBOARD_BACKGROUND_COLOUR, wx.SOLID))
+        dc.SetPen(wx.Pen("#AAAAAA", width=1, style=wx.SOLID))
+        dc.DrawRectangle(self.offRec)
+        dc.DrawRectangle(self.holdRec)
+        dc.DrawRectangle(wx.Rect(w - 14, 0, 14, h))
+        
+        dc.SetTextForeground("#000000")
+        dc.DrawText("oct", self.offRec[0] + 3, 15)
+        x1, y1 = self.offRec[0], self.offRec[1]
+        dc.SetBrush(wx.Brush("#000000", wx.SOLID))
+        if sys.platform == "darwin":
+            dc.DrawPolygon([wx.Point(x1 + 3, 36), wx.Point(x1 + 10, 29),
+                            wx.Point(x1 + 17, 36)])
+            self.offUpRec = wx.Rect(x1, 28, x1 + 20, 10)
+            dc.DrawPolygon([wx.Point(x1 + 3, 55), wx.Point(x1 + 10, 62),
+                            wx.Point(x1 + 17, 55)])
+            self.offDownRec = wx.Rect(x1, 54, x1 + 20, 10)
+        else:
+            dc.DrawPolygon([wx.Point(x1 + 3, 38), wx.Point(x1 + 10, 31),
+                            wx.Point(x1 + 17, 38)])
+            self.offUpRec = wx.Rect(x1, 30, x1 + 20, 10)
+            dc.DrawPolygon([wx.Point(x1 + 3, 57), wx.Point(x1 + 10, 64),
+                            wx.Point(x1 + 17, 57)])
+            self.offDownRec = wx.Rect(x1, 56, x1 + 20, 10)
+            
+        dc.DrawText("%d" % (self.offset // 12), x1 + 7, 41)
+    
+        if self.hold:
+            dc.SetTextForeground("#0000CC")
+        else:
+            dc.SetTextForeground("#000000")
+        for i, c in enumerate("HOLD"):
+            dc.DrawText(c, self.holdRec[0] + 6, self.holdRec[3] // 6 * i + 15)
+        
+        dc.SetBrush(wx.Brush(KEYBOARD_BACKGROUND_COLOUR, wx.SOLID))
+        dc.SetPen(wx.Pen(KEYBOARD_BACKGROUND_COLOUR, width=1, style=wx.SOLID))
+        dc.DrawRectangle(w - self.gap, 0, self.gap, h)
+        dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID))
+        dc.DrawLine(0, 3, w, 3)
+        dc.SetPen(wx.Pen("#444444", width=1, style=wx.SOLID))
+        dc.DrawLine(0, 2, w, 2)
+        dc.SetPen(wx.Pen("#888888", width=1, style=wx.SOLID))
+        dc.DrawLine(0, 1, w, 1)
+        dc.SetPen(wx.Pen("#CCCCCC", width=1, style=wx.SOLID))
+        dc.DrawLine(0, 0, w, 0)
+
+if __name__ == "__main__":
+    app = wx.App()
+    frame = wx.Frame(None, title="Piano keyboard test", size=(650, 110))
+    keyboard = Keyboard(frame)
+    frame.Show()
+    app.MainLoop()
diff --git a/work-in-progress/test_event_parser.py b/work-in-progress/test_event_parser.py
new file mode 100644
index 0000000..81703fa
--- /dev/null
+++ b/work-in-progress/test_event_parser.py
@@ -0,0 +1,69 @@
+import random
+from pyo import *
+from EventParser import EventParser
+
+### Orchestra ###
+class Instr1:
+    def __init__(self, duration, *args):
+        self.f = Fader(0.001, duration-0.005, duration, args[1]).play()
+        self.f.setExp(3)
+        self.osc = SineLoop([args[0], args[0]*1.01], feedback=0.12, mul=self.f)
+        self.filt = ButHP(self.osc, args[0]).out()
+
+class Rhythm:
+    def __init__(self, duration, *args):
+        self.env = CosTable([(0,0), (32,1), (1000,.25), (8191,0)])
+        rhythms = [[2,2,1,1,1,1,4], [1,2,1,2,2,1,2,1,2,1,1],[4,2,1,1,2,2,1.3,1.3,1.4]]
+        self.seq = Seq(.125, random.choice(rhythms), poly=1, onlyonce=True).play()
+        self.amp = TrigEnv(self.seq, self.env, .125, mul=args[1])
+        self.osc = Lorenz([args[0], args[0]*1.01], args[2], True, mul=self.amp)
+        self.disto = Disto(self.osc, drive=0.9, slope=0.9, mul=0.4)
+        self.bp = ButBP(self.disto, freq=1000, q=0.7).out()
+
+class Chord:
+    def __init__(self, duration, *args, **kwargs):
+        self.f = Fader(1, 1, duration, args[0]).play()
+        self.o1 = LFO(kwargs["f1"], sharp=0.95, type=2, mul=self.f).out()
+        self.o2 = LFO(kwargs["f2"], sharp=0.95, type=2, mul=self.f).out(1)
+        self.o3 = LFO(kwargs["f3"], sharp=0.95, type=2, mul=self.f).out()
+        self.o4 = LFO(kwargs["f4"], sharp=0.95, type=2, mul=self.f).out(1)
+
+### Little score generator ###
+sc = ""
+starttime = 8
+for i in range(300):
+    template = "Instr1 => %f %f %f %f\n"
+    dur = random.choice([.25, .75, 1])
+    freq = random.choice(midiToHz([67,70,72,75,79,84,86,87,91]))
+    amp = random.uniform(0.08, 0.15)
+    sc = sc + template % (starttime, dur, freq, amp)
+    starttime += random.choice([.125, .125, .25, .25])
+for i in range(65):
+    template = "Rhythm => %f %f %f %f %f\n"
+    pit = random.uniform(0.9, 1.0)
+    amp = random.uniform(0.1, 0.2)
+    chaos = random.uniform(0.8, 1.0)
+    sc = sc + template % (i, 1, pit, amp, chaos)
+for i in range(17):
+    template = "Chord => %f 5 0.03 %s\n"
+    notes = midiToHz([48.01,55.02,59.99,63.01,67,69.98,72.03,75.04])
+    cdict = {"f1": random.choice(notes), "f2": random.choice(notes),
+             "f3": random.choice(notes), "f4": random.choice(notes)}
+    sc = sc + template % (i * 4, str(cdict))
+
+### Rendering ###
+REALTIME = True
+
+if REALTIME:
+    s = Server().boot()
+else:
+    s = Server(buffersize=8, audio="offline").boot()
+    s.recordOptions(dur=70, filename="rendered_score.wav")
+
+reader = EventParser(s, sc, globals())
+reader.play()
+
+if REALTIME:
+    s.gui(locals())
+else:
+    s.start()
\ No newline at end of file

-- 
python-pyo packaging



More information about the pkg-multimedia-commits mailing list