[SCM] python-pyo/master: New commit with jack midi
tiago at users.alioth.debian.org
tiago at users.alioth.debian.org
Thu Nov 16 15:28:36 UTC 2017
The following commit has been merged in the master branch:
commit a07e090776ad4e591c5df1fbd217abc85370aa40
Author: Tiago Bortoletto Vaz <tiago at debian.org>
Date: Sun Nov 5 09:26:37 2017 -0500
New commit with jack midi
diff --git a/ChangeLog b/ChangeLog
index 3f03cd5..2146a38 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2017-11-03 belangeo <belangeo at gmail.com>
+
+ * Added a callback attribute to PVAnal. The function receives magnitudes
+ and true frequencies for every analysis frame.
+
+2017-10-09 belangeo <belangeo at gmail.com>
+
+ * Added Jack midi support to the Server.
+
+2017-10-07 belangeo <belangeo at gmail.com>
+
+ * Removed internal import of the random module. Scripts importing
+ both pyo and random modules segfault on garbagge collection at exit.
+
2017-08-28 belangeo <belangeo at gmail.com>
* Upgraded version number to 0.8.7.
diff --git a/README.md b/README.md
index 0df0be4..1ec8a1e 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,11 @@ How to get pyo running from sources on OS X and linux:
pyo was awarded **second prize** in the
[Lomus 2012](http://concours.afim-asso.org/2012/) Free Software Competition.
+**You want to help the development of pyo ?** Go to the
+[pyo features market](https://github.com/belangeo/pyo/wiki/Pyo-features-market)
+and make a donation for the feature you want to promote. You can also submit new
+features on the mailing-list ( pyo-discuss at googlegroups.com ).
+
## Radio Pyo ##
If you want to listen to scripts rendered in real-time, just connect to
diff --git a/TODO.md b/TODO.md
index 4abe454..bc84438 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,3 +1,21 @@
+To do ASAP!
+===========
+
+- [DOC] Compilation for anaconda (mail from Stanley Rosenbaum).
+
+- [DOC] Live recording without xruns, use Server.recstart() to record to a
+ file on a tmpfs, which is basically a filesystem in RAM.
+ mail: "Server recording to wav generates many xruns (Aug 26)".
+
+- Better error message when trying to open an input device but there is
+ none on the system. Automatic deactivation of duplex mode if no input.
+
+- E-Pyo: Reading preferences file should handle utf-8 paths.
+ - linux = ok
+ - osx = ok
+
+- Unicode paths don't work with python 3.6 on Windows.
+
This is a list of features/fixes to implement for future releases
=================================================================
@@ -19,9 +37,6 @@ Server
- A method to retrieve a graph of the internal state of the server
(active objects, connections, attribute values, ...).
-- Remove, if possible, PyGILState_Ensure/PyGILState_Release from
- the process_buffers function.
-
Examples
--------
@@ -61,8 +76,6 @@ GUI
- Keyboard, a virtual MIDI keyboard (adapted from Zyne's one).
-- Ability to set channel "name" in the view of PyoGuiScope and PyoGuiSpectrum.
-
Tables
------
@@ -77,8 +90,12 @@ Matrices
E-Pyo
-----
+- Split the file name (shown in the notebook tab) with the last dot, not the first.
+
- Complete review of E-Pyo on Windows. Lot of features don't seem to work.
+ - Project tree refresh function doesn't work.
+
- Window splitter to show more than one file at the time (multiple
views) ?
diff --git a/doc-sphinx/build.py b/doc-sphinx/build.py
index 30fa130..12f5efc 100644
--- a/doc-sphinx/build.py
+++ b/doc-sphinx/build.py
@@ -102,7 +102,7 @@ os.system("sphinx-build -a -E -j 4 -b %s ./source %s" % (build_format, build_fol
if build_format == "latex":
os.system("cd build_latex; pdflatex -interaction nonstopmode Pyo; pdflatex -interaction nonstopmode Pyo")
-rep = raw_input("Do you want to upload to ajax server (y/n) ? ")
+rep = input("Do you want to upload to ajax server (y/n) ? ")
if rep == "y":
os.system("scp -r build_html/* jeadum1 at ajaxsoundstudio.com:/home/jeadum1/ajaxsoundstudio.com/pyodoc")
diff --git a/doc-sphinx/source/compiling.rst b/doc-sphinx/source/compiling.rst
index cc40692..0712b05 100644
--- a/doc-sphinx/source/compiling.rst
+++ b/doc-sphinx/source/compiling.rst
@@ -11,14 +11,17 @@ Dependencies
To compile pyo with all its features, you will need the following dependencies:
-- `Python 2.7.x or 3.5.x <https://www.python.org/downloads/>`_
-- `WxPython 3.0 <http://www.wxpython.org/download.php/>`_
+- `Python 2.7 or 3.5 or 3.6 (recommended) <https://www.python.org/downloads/>`_. On Windows, install the 32-bit version of Python.
+- `WxPython 3.0.2.0 (classic) or 4.0.0 (phoenix, recommended) <http://www.wxpython.org/download.php/>`_
- `Portaudio <http://www.portaudio.com/>`_
- `Portmidi <http://portmedia.sourceforge.net/portmidi/>`_
- `libsndfile <http://www.mega-nerd.com/libsndfile/>`_
- `liblo <http://liblo.sourceforge.net/>`_
- `git <https://git-scm.com/>`_ (if you want the latest sources)
+Please note that under MacOS you will need to install the
+**Apple's developer tools** to compile pyo.
+
Getting sources
---------------
@@ -32,9 +35,6 @@ You can download pyo's sources by checking out the source code
Compilation
---------------
-Please note that under MacOS you will need to install the
-**Apple's developer tools** to compile pyo.
-
Once you have all the required dependencies, go in pyo's directory:
.. code-block:: bash
@@ -120,21 +120,22 @@ Compilation scripts
*******************
In the ./scripts folder, there is some alternate scripts to simplify the
-compilation process a little bit.
+compilation process a little bit. These scripts will compile pyo for the
+version of python pointed to by the command `python`.
-To compile both 32-bit and 64-bit resolutions on linux (with jack support):
+To compile both 32-bit and 64-bit resolutions on linux with jack support:
.. code-block:: bash
sudo sh scripts/compile_linux_withJack.sh
-To compile both 32-bit and 64-bit resolutions on macOS (without Jack):
+To compile both 32-bit and 64-bit resolutions on macOS without Jack support:
.. code-block:: bash
sudo sh scripts/compile_OSX.sh
-To compile both 32-bit and 64-bit resolutions on macOS (with Jack):
+To compile both 32-bit and 64-bit resolutions on macOS with Jack support (Jack headers must be present on the system):
.. code-block:: bash
@@ -144,26 +145,51 @@ Debian & Ubuntu (apt-get)
-------------------------
Under Debian & Ubuntu you can type the following commands to get pyo up
-and running:
+and running.
+
+For Python 2.7
+**************
.. code-block:: bash
- sudo apt-get install libjack-jackd2-dev libportmidi-dev portaudio19-dev liblo-dev
- sudo apt-get install libsndfile-dev python-dev python-tk
- sudo apt-get install python-imaging-tk python-wxgtk3.0
+ sudo apt-get install libjack-jackd2-dev libportmidi-dev portaudio19-dev liblo-dev libsndfile-dev
+ sudo apt-get install python-dev python-tk python-imaging-tk python-wxgtk3.0
git clone https://github.com/belangeo/pyo.git
cd pyo
sudo python setup.py install --install-layout=deb --use-jack --use-double
* On Ubuntu system prior to vivid, wxpython 3.0 must be compiled from sources.
-
+
+For Python 3.5
+**************
+
+.. code-block:: bash
+
+ sudo apt-get install libjack-jackd2-dev libportmidi-dev portaudio19-dev liblo-dev libsndfile-dev
+ sudo apt-get install python3-dev python3-tk python3-pil.imagetk python3-pip
+ git clone https://github.com/belangeo/pyo.git
+ cd pyo
+ sudo python3 setup.py install --install-layout=deb --use-jack --use-double
+
+If you want to be able to use all of pyo's gui widgets, you will need wxPython-phoenix.
+
+- Install requirements outlined in the README.rst at `https://github.com/wxWidgets/Phoenix/blob/master/README.rst <https://github.com/wxWidgets/Phoenix/blob/master/README.rst>`_
+
+- Install wxPython with pip:
+
+.. code-block:: bash
+
+ sudo pip3 install -U wxPython
+
+Be patient, the last step will take a while!
+
MacOS (Homebrew)
----------------
Under macOS, it is very simple to build pyo from sources with the Homebrew
package manager.
-First, install Python and WxPython from the binary installers.
+First, install Python and WxPython from their respective download pages (`Python <https://www.python.org/downloads/>`_, `WxPython <http://www.wxpython.org/download.php/>`_).
Second, you need to install `Homebrew <http://brew.sh/>`_. Then, in a terminal
window:
diff --git a/doc-sphinx/source/download.rst b/doc-sphinx/source/download.rst
index 56eac40..897e272 100644
--- a/doc-sphinx/source/download.rst
+++ b/doc-sphinx/source/download.rst
@@ -8,9 +8,11 @@ To download the latest pre-compiled version of pyo, go to the pyo's
`web page <http://ajaxsoundstudio.com/software/pyo/>`_.
Under Debian and Fedora distros, you can get pyo from the package manager.
-The library's name is **python-pyo**.
+The library's name is **python-pyo** or **python2-pyo** for Python 2.7
+and **python3-pyo** for Python 3.5+.
-If you are running Arch linux, the package is called **python2-pyo**.
+If you are running Arch linux, the package is called **python2-pyo**. There
+is no package yet for Python 3.5+.
Content of the installer
@@ -24,12 +26,11 @@ E-Pyo, a simple text editor especially tuned to edit and run audio python script
Pyo is a python module...
-----------------------------
-... which means that python must be present (version 2.7.x is the current one,
-version 3.5.x is experimental but everything seems to work well so far) on the
-system. If python is not installed, you can download it on
+... which means that python must be present (version 2.7, 3.5 or 3.6 (recommended))
+on the system. If python is not installed, you can download it on
`python.org <https://www.python.org/downloads/>`_.
Pyo also offers some GUI facilities to control or visualize the audio processing.
If you want to use all of pyo's GUI features, you must install WxPython 3.0
-(**classic** for python 2.7.x and **phoenix** for python 3.5.x), available on
+(**classic** for python 2.7 and **phoenix** for python 3.5+), available on
`wxpython.org <http://wxpython.org/download.php>`_.
\ No newline at end of file
diff --git a/doc-sphinx/source/index.rst b/doc-sphinx/source/index.rst
index a1c6bf5..588895c 100644
--- a/doc-sphinx/source/index.rst
+++ b/doc-sphinx/source/index.rst
@@ -26,6 +26,7 @@ Parts of the documentation
compiling
structure
gettingstarted
+ winaudioinspect
perftips
api/index
diff --git a/doc-sphinx/source/perftips.rst b/doc-sphinx/source/perftips.rst
index e52f048..02aab77 100644
--- a/doc-sphinx/source/perftips.rst
+++ b/doc-sphinx/source/perftips.rst
@@ -287,8 +287,8 @@ the same noise for multiple denormalizations:
Use a PyoObject when available
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Always look first if a PyoObject does what you want, it will always more
-efficient than a the same process written from scratch.
+Always look first if a PyoObject does what you want, it will always be more
+efficient than the same process written from scratch.
This construct, although pedagogically valid, will never be more efficient, in
term of CPU and memory usage, than a native PyoObject (Phaser) written in C.
diff --git a/doc-sphinx/source/winaudioinspect.rst b/doc-sphinx/source/winaudioinspect.rst
new file mode 100644
index 0000000..90820d4
--- /dev/null
+++ b/doc-sphinx/source/winaudioinspect.rst
@@ -0,0 +1,161 @@
+Configuring the audio output (especially on Windows)
+====================================================
+
+Here is some tips to help you to configure the audio input/output on Windows.
+Some of these procedures are also valid for other systems.
+
+How to choose the audio host api on Windows
+-------------------------------------------
+
+Choosing the good audio API on Windows can turn out to be a real headache.
+
+This document presents a script that will inspect your system and tell you if:
+
+- Pyo can run in duplex mode. That means both audio input and output instead of output only.
+
+- Pyo is able to connect to the different host APIs that are usually available on Windows.
+
+In the hope that this will help you having a good experience with pyo under Windows!
+
+`https://github.com/belangeo/pyo/tree/master/scripts/win_audio_drivers_inspector.py
+<https://github.com/belangeo/pyo/tree/master/scripts/win_audio_drivers_inspector.py>`_
+
+.. code-block:: python
+
+ """
+ Windows audio host inspector.
+
+ This script will check if pyo can run in duplex mode (both audio input and output)
+ and will test every host API to help the user in making his audio device choice.
+
+ """
+ import sys, time
+ from pyo import *
+
+ if sys.platform == "win32":
+ host_apis = ["mme", "directsound", "asio", "wasapi", "wdm-ks"]
+ else:
+ print("This program must be used on a windows system! Ciao!")
+ exit()
+
+
+ print("* Checking for any available audio input... *")
+
+ input_names, input_indexes = pa_get_input_devices()
+
+ print("* Checking audio output hosts... *")
+
+ s = Server(duplex=0)
+ s.verbosity = 0
+
+ host_results = []
+ for host in host_apis:
+ print("* Testing %s... *" % host)
+ try:
+ s.reinit(buffersize=1024, duplex=0, winhost=host)
+ s.boot().start()
+ a = Sine(freq=440, mul=0.2).out()
+ time.sleep(2)
+ s.stop()
+ s.shutdown()
+ host_results.append(True)
+ except:
+ host_results.append(False)
+
+ print("\nResults")
+ print("-------\n")
+
+ if len(input_names):
+ print("Duplex mode OK!")
+ print("Initialize the Server with duplex=1 as argument.\n")
+ else:
+ print("No input available. Duplex mode should be turned off.")
+ print("Initialize the Server with duplex=0 as argument.\n")
+
+ for i, host in enumerate(host_apis):
+ if host_results[i]:
+ print("Host: %s ==> OK!" % host)
+ else:
+ print("Host: %s ==> Failed..." % host)
+
+ print("Initialize the Server with the desired host given to winhost argument.")
+
+ print("\nFinished!")
+
+Tunning the Windows WASAPI driver
+--------------------------------
+
+The Windows Audio Session API (WASAPI) is Microsoft's most modern method for talking with audio devices.
+It is available in Windows since Vista. Pyo's default host is the WASAPI. If the script above tells you:
+
+ Host: wasapi ==> Failed...
+
+there is some things you can do to make it work. Open the **Sound** window by double-clicking on the volume
+icon and choosing *Playback Devices*. Here, select your device and click on the *Properties* button. In
+the *advanced* tab, make sure that the sampling rate is the same that the one used by pyo (pyo defaults to
+44100 Hz). You can check the exclusive mode box if you want, this will bypass the system mixer, default
+settings, and typically any effects provided by the audio driver.
+
+Perform the same in the *recording* tab if you want to run pyo in duplex mode. If you got the message:
+
+ No input available. Duplex mode should be turned off.
+
+you'll have to make sure first that there is an available input device in that tab.
+
+If you use a cheap soundcard (typically, any built in soundcard is not very good!), you may have to increase
+the buffer size of the pyo's Server in order to avoid glitches in the audio streams.
+
+Server initialization examples
+------------------------------
+
+.. code-block:: python
+
+ # sampling rate = 44100 Hz, buffer size = 256, channels = 2, full duplex, host = WASAPI
+ s = Server()
+
+ # sampling rate = 48000 Hz, buffer size = 1024, channels = 2, full duplex, host = WASAPI
+ s = Server(sr=48000, buffersize=1024)
+
+ # sampling rate = 48000 Hz, buffer size = 512, channels = 2, output only, host = ASIO
+ s = Server(sr=48000, buffersize=512, duplex=0, winhost="asio")
+
+ # sampling rate = 96000 Hz, buffer size = 128, channels = 1, full duplex, host = ASIO
+ s = Server(sr=96000, nchnls=1, buffersize=128, duplex=1, winhost="asio")
+
+Choosing a specific device
+--------------------------
+
+A single host API can target more than one available devices.
+
+There is some useful functions that can help you in the choice of the audio device:
+
+- **pa_list_host_apis()**: Prints the list of audio host APIs.
+- **pa_list_devices()**: Prints the list of audio devices. The first column if the index of the device.
+- **pa_get_default_input()**: Returns the index of the default input device.
+- **pa_get_default_output()**: Returns the index of the default output device.
+- **pa_get_default_devices_from_host(host)**: Returns the default input and output devices for a given audio host.
+
+Run this code to see the current state of your audio setup:
+
+.. code-block:: python
+
+ from pyo import *
+
+ print("Audio host APIS:")
+ pa_list_host_apis()
+ pa_list_devices()
+ print("Default input device: %i" % pa_get_default_input())
+ print("Default output device: %i" % pa_get_default_output())
+
+If the default device for the desired host is not the one you want, you can tell the Server
+which device you want to use with the *setInputDevice(x)* and *setOutputDevice(x)* methods. These
+methods take the index of the desired device and must be called before booting the Server. Ex:
+
+.. code-block:: python
+
+ from pyo import *
+
+ s = Server(duplex=0)
+ s.setOutputDevice(0)
+ s.boot()
+
diff --git a/examples/wxgui/01_gui_widgets_example.py b/examples/wxgui/01_gui_widgets_example.py
index 4c2f384..0714c4e 100644
--- a/examples/wxgui/01_gui_widgets_example.py
+++ b/examples/wxgui/01_gui_widgets_example.py
@@ -27,7 +27,7 @@ sc = Scope(f)
class MyFrame(wx.Frame):
- def __init__(self, parent, title, pos=(50, 50), size=(850, 600)):
+ def __init__(self, parent, title, pos=(50, 50), size=(1000, 600)):
wx.Frame.__init__(self, parent, -1, title, pos, size)
self.Bind(wx.EVT_CLOSE, self.on_quit)
diff --git a/include/ad_jack.h b/include/ad_jack.h
index c41b874..892fba8 100644
--- a/include/ad_jack.h
+++ b/include/ad_jack.h
@@ -22,26 +22,41 @@
#define _AD_JACK_H
#include <jack/jack.h>
+#include <jack/midiport.h>
#include "servermodule.h"
typedef struct {
+ unsigned long timestamp;
+ int status;
+ int data1;
+ int data2;
+} PyoJackMidiEvent;
+
+typedef struct {
jack_client_t *jack_client;
jack_port_t **jack_in_ports;
jack_port_t **jack_out_ports;
+ unsigned int midi_event_count;
+ PyoJackMidiEvent *midi_events;
+ jack_port_t *jack_midiin_port;
+ jack_port_t *jack_midiout_port;
} PyoJackBackendData;
-int jack_callback(jack_nframes_t nframes, void *arg);
-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 jack_midi_input_port_set_name(Server *self);
+int jack_midi_output_port_set_name(Server *self);
int Server_jack_init(Server *self);
int Server_jack_deinit(Server *self);
int Server_jack_start(Server *self);
int Server_jack_stop(Server *self);
+void jack_noteout(Server *self, int pit, int vel, int chan, long timestamp);
+void jack_afterout(Server *self, int pit, int vel, int chan, long timestamp);
+void jack_ctlout(Server *self, int ctlnum, int value, int chan, long timestamp);
+void jack_programout(Server *self, int value, int chan, long timestamp);
+void jack_pressout(Server *self, int value, int chan, long timestamp);
+void jack_bendout(Server *self, int value, int chan, long timestamp);
+
#endif
/* _AD_JACK_H */
diff --git a/include/servermodule.h b/include/servermodule.h
index 85dba44..14326c1 100644
--- a/include/servermodule.h
+++ b/include/servermodule.h
@@ -41,7 +41,8 @@ typedef enum {
} PyoAudioBackendType;
typedef enum {
- PyoPortmidi = 0
+ PyoPortmidi = 0,
+ PyoJackMidi = 1
} PyoMidiBackendType;
/* Portmidi midi message and event type clones. */
@@ -76,8 +77,12 @@ 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 */
+ 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) */
+ PyObject *jackAutoConnectMidiInputPort; /* lists of jack auto-connection ports to pyo midi input */
+ PyObject *jackAutoConnectMidiOutputPort; /* lists of jack auto-connection ports from pyo midi output */
+ PyObject *jackMidiInputPortName; /* string (midi input port short names for jack server) */
+ PyObject *jackMidiOutputPortName; /* string (midi output port short names for jack server) */
int isJackTransportSlave;
int jack_transport_state; /* 0 = stopped, 1 = started */
PyoMidiEvent midiEvents[200];
@@ -100,6 +105,7 @@ typedef struct {
int midi_output;
int withPortMidi;
int withPortMidiOut;
+ int withJackMidi;
int midiActive;
int allowMMMapper;
int server_started;
diff --git a/pyolib/_core.py b/pyolib/_core.py
index 275f628..1418932 100644
--- a/pyolib/_core.py
+++ b/pyolib/_core.py
@@ -28,7 +28,7 @@ License along with pyo. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
-import random
+import time
import inspect
import tempfile
from subprocess import call
@@ -126,6 +126,27 @@ FUNCTIONS_INIT_LINES = {
"getPyoKeywords": "getPyoKeywords()"
}
+def get_random_integer(mx=32767):
+ if sys.version_info[0] < 3 and sys.version_info[1] < 3:
+ seed = int(str(time.clock()).split(".")[1])
+ else:
+ seed = int(str(time.process_time()).split(".")[1])
+ return (seed * 31351 + 21997) % mx
+
+def listscramble(lst):
+ if sys.version_info[0] < 3 and sys.version_info[1] < 3:
+ seed = int(str(time.clock()).split(".")[1])
+ else:
+ seed = int(str(time.process_time()).split(".")[1])
+ l = lst[:]
+ new = []
+ pos = 1
+ while l:
+ pos = (pos * seed) % len(l)
+ new.append(l[pos])
+ del l[pos]
+ return new
+
def stringencode(st):
if sys.version_info[0] >= 3:
if type(st) is str:
@@ -498,15 +519,14 @@ def convertArgsToLists(*args):
Return new args and maximum list length.
"""
- converted = []
- for i in args:
- if isinstance(i, PyoObjectBase) or isinstance(i, list):
- converted.append(i)
- else:
- converted.append([i])
+ converted = list(args)
+ for i, arg in enumerate(converted):
+ if not isinstance(arg, PyoObjectBase) and not isinstance(arg, list):
+ converted[i] = [arg]
- max_length = max(len(i) for i in converted)
- return tuple(converted + [max_length])
+ max_length = max(len(arg) for arg in converted)
+ converted.append(max_length)
+ return tuple(converted)
def wrap(arg, i):
"""
@@ -1296,8 +1316,7 @@ class PyoObject(PyoObjectBase):
else:
if chnl < 0:
[obj.out(i*inc, wrap(dur, i), wrap(delay, i)) \
- for i, obj in enumerate(random.sample(self._base_objs,
- len(self._base_objs)))]
+ for i, obj in enumerate(listscramble(self._base_objs))]
else:
[obj.out(chnl+i*inc, wrap(dur, i), wrap(delay, i)) \
for i, obj in enumerate(self._base_objs)]
@@ -2008,10 +2027,11 @@ class PyoTableObject(PyoObjectBase):
table: PyoTableObject
The source table.
srcpos: int, optional
- The start position in the source table. Defaults to 0.
+ The start position, in samples, in the source table.
+ Defaults to 0.
destpos ; int, optional
- The start position in the destination (self) table. Defaults
- to 0.
+ The start position, in samples, in the destination (self) table.
+ Defaults to 0.
length: int, optional
The number of samples to copy from source to destination. if
length is negative, the length of the smallest table is used.
diff --git a/pyolib/_tkwidgets.py b/pyolib/_tkwidgets.py
index 4216428..4873530 100644
--- a/pyolib/_tkwidgets.py
+++ b/pyolib/_tkwidgets.py
@@ -151,16 +151,22 @@ class PyoObjectControl(tk.Frame):
self._displays = {}
self._maps = {}
self._sigs = {}
+ self._res = {}
for i, m in enumerate(self._map_list):
- key, init = m.name, m.init
+ key, init, res, dataOnly = m.name, m.init, m.res, m.dataOnly
# filters PyoObjects
if type(init) not in [list, float, int]:
self._excluded.append(key)
else:
self._maps[key] = m
+ self._res[key] = res
# label (param name)
- label = tk.Label(self, height=1, width=10, highlightthickness=0,
- text=key)
+ if dataOnly:
+ label = tk.Label(self, height=1, width=10, highlightthickness=0,
+ text=key+" *")
+ else:
+ label = tk.Label(self, height=1, width=10, highlightthickness=0,
+ text=key)
label.grid(row=i, column=0)
# create and pack slider
if not isinstance(init, list):
@@ -189,13 +195,15 @@ class PyoObjectControl(tk.Frame):
else:
self._displays[key].set("\n".join(["%.4f" % i for i in init]))
# set obj attribute to PyoObject SigTo
- self._sigs[key] = SigTo(init, .025, init)
- refStream = self._obj.getBaseObjects()[0]._getStream()
- server = self._obj.getBaseObjects()[0].getServer()
- for k in range(len(self._sigs[key].getBaseObjects())):
- curStream = self._sigs[key].getBaseObjects()[k]._getStream()
- server.changeStreamPosition(refStream, curStream)
- setattr(self._obj, key, self._sigs[key])
+ if not dataOnly:
+ self._values[key] = init
+ self._sigs[key] = SigTo(init, .025, init)
+ refStream = self._obj.getBaseObjects()[0]._getStream()
+ server = self._obj.getBaseObjects()[0].getServer()
+ for k in range(len(self._sigs[key].getBaseObjects())):
+ curStream = self._sigs[key].getBaseObjects()[k]._getStream()
+ server.changeStreamPosition(refStream, curStream)
+ setattr(self._obj, key, self._sigs[key])
# padding
top = self.winfo_toplevel()
top.rowconfigure(0, weight=1)
@@ -219,8 +227,14 @@ class PyoObjectControl(tk.Frame):
value = [self._maps[key].get(float(y)) for y in x]
self._displays[key].set("\n".join(["%.4f" % i for i in value]))
- self._values[key] = value
- setattr(self._sigs[key], "value", value)
+ if self._res[key].startswith("i"):
+ value = int(value)
+
+ if key in self._values:
+ self._values[key] = value
+ setattr(self._sigs[key], "value", value)
+ else:
+ setattr(self._obj, key, value)
######################################################################
### View window for PyoTableObject
diff --git a/pyolib/_widgets.py b/pyolib/_widgets.py
index 6f2f348..80d6f32 100644
--- a/pyolib/_widgets.py
+++ b/pyolib/_widgets.py
@@ -27,7 +27,6 @@ License along with pyo. If not, see <http://www.gnu.org/licenses/>.
"""
import sys
import os
-import random
use_wx = 1
if "PYO_GUI_WX" in os.environ:
@@ -136,7 +135,8 @@ def wxDisplayWindow(f, title):
MAX_X = x + CURRENT_X
f.SetPosition((px, py))
else:
- f.SetPosition((random.randint(250, 500), random.randint(200, 400)))
+ CURRENT_X, MAX_X, NEXT_Y = 50, 50, 50
+ wxDisplayWindow(f, title)
f.Show()
def wxShowWindow(f, title, root):
@@ -422,4 +422,4 @@ def createServerGUI(nchnls, start, stop, recstart, recstop, setAmp, started,
wx.CallAfter(wxCreateDelayedScopeWindows)
wx.CallAfter(wxCreateDelayedExprEditorWindows)
wx.CallAfter(f.Raise)
- return f, win
+ return f, win, PYO_USE_WX
diff --git a/pyolib/_wxwidgets.py b/pyolib/_wxwidgets.py
index 01dce27..31c97e1 100644
--- a/pyolib/_wxwidgets.py
+++ b/pyolib/_wxwidgets.py
@@ -20,7 +20,7 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with pyo. If not, see <http://www.gnu.org/licenses/>.
"""
-import wx, os, sys, math, time, random, unicodedata
+import wx, os, sys, math, time, unicodedata
import wx.stc as stc
if "phoenix" in wx.version():
@@ -28,6 +28,8 @@ if "phoenix" in wx.version():
wx.EmptyBitmap = wx.Bitmap
wx.EmptyImage = wx.Image
wx.BitmapFromImage = wx.Bitmap
+ wx.Image_HSVValue = wx.Image.HSVValue
+ wx.Image_HSVtoRGB = wx.Image.HSVtoRGB
BACKGROUND_COLOUR = "#EBEBEB"
@@ -1436,6 +1438,14 @@ class SpectrumDisplay(wx.Frame):
X_OFF = 24
else:
X_OFF = 16
+
+ if self.obj is None:
+ initgain = 0.0
+ self.channelNamesVisible = True
+ else:
+ initgain = self.obj.gain
+ self.channelNamesVisible = self.obj.channelNamesVisible
+
tw, th = self.GetTextExtent("Start")
self.activeTog = wx.ToggleButton(self.panel, -1, label="Start", size=(tw+X_OFF, th+10))
self.activeTog.SetValue(1)
@@ -1473,7 +1483,7 @@ class SpectrumDisplay(wx.Frame):
valtype='float', log=False, function=self.setZoomH)
self.box.Add(self.zoomH, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
self.dispBox.Add(self.box, 1, wx.EXPAND, 0)
- self.gainSlider = ControlSlider(self.panel, -24, 24, 0, outFunction=self.setGain, orient=wx.VERTICAL)
+ self.gainSlider = ControlSlider(self.panel, -24, 24, initgain, outFunction=self.setGain, orient=wx.VERTICAL)
self.dispBox.Add(self.gainSlider, 0, wx.EXPAND|wx.TOP, 5)
self.dispBox.AddSpacer(5)
self.mainBox.Add(self.dispBox, 1, wx.EXPAND)
@@ -1531,6 +1541,10 @@ class SpectrumDisplay(wx.Frame):
self.spectrumPanel.setMscaling(x)
wx.CallAfter(self.spectrumPanel.Refresh)
+ def showChannelNames(self, visible):
+ self.spectrumPanel.showChannelNames(visible)
+ self.channelNamesVisible = visible
+
def _destroy(self, evt):
self.obj._setViewFrame(None)
self.Destroy()
@@ -1542,21 +1556,30 @@ class SpectrumPanel(wx.Panel):
pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
wx.Panel.__init__(self, parent, pos=pos, size=size, style=style)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+ self.SetMinSize((300,100))
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_SIZE, self.OnSize)
- #self.chnls = chnls
+ if chnls == 1:
+ self.chnls = 64
+ else:
+ self.chnls = chnls
+ try:
+ self.channelNamesVisible = self.GetParent().GetParent().channelNamesVisible
+ except:
+ self.channelNamesVisible = True
self.img = None
self.obj = None
self.lowfreq = lowfreq
self.highfreq = highfreq
self.fscaling = fscaling
self.mscaling = mscaling
- self.pens = [wx.Pen(wx.Colour(166,4,0)), wx.Pen(wx.Colour(8,11,116)), wx.Pen(wx.Colour(0,204,0)),
- wx.Pen(wx.Colour(255,167,0)), wx.Pen(wx.Colour(133,0,75)), wx.Pen(wx.Colour(255,236,0)),
- wx.Pen(wx.Colour(1,147,154)), wx.Pen(wx.Colour(162,239,0))]
- self.brushes = [wx.Brush(wx.Colour(166,4,0,128)), wx.Brush(wx.Colour(8,11,116,128)), wx.Brush(wx.Colour(0,204,0,128)),
- wx.Brush(wx.Colour(255,167,0,128)), wx.Brush(wx.Colour(133,0,75,128)), wx.Brush(wx.Colour(255,236,0,128)),
- wx.Brush(wx.Colour(1,147,154,128)), wx.Brush(wx.Colour(162,239,0,128))]
+ self.pens = []
+ self.brushes = []
+ for x in range(self.chnls):
+ hsv = wx.Image_HSVValue(x / float(self.chnls), 1.0, 1.0)
+ rgb = wx.Image_HSVtoRGB(hsv)
+ self.pens.append(wx.Pen(wx.Colour(rgb.red, rgb.green, rgb.blue)))
+ self.brushes.append(wx.Brush(wx.Colour(rgb.red, rgb.green, rgb.blue, 128)))
if sys.platform == "win32" or sys.platform.startswith("linux"):
self.dcref = wx.BufferedPaintDC
else:
@@ -1592,6 +1615,9 @@ class SpectrumPanel(wx.Panel):
def setHighFreq(self, x):
self.highfreq = x
+ def showChannelNames(self, visible):
+ self.channelNamesVisible = visible
+
def OnPaint(self, evt):
w,h = self.GetSize()
dc = self.dcref(self)
@@ -1716,14 +1742,15 @@ class SpectrumPanel(wx.Panel):
if self.img is not None:
last_tw = tw
# legend
- tw, th = dc.GetTextExtent("chan 8")
- for i in range(len(self.img)):
- dc.SetTextForeground(self.pens[i%8].GetColour())
- dc.DrawText("chan %d" % (i+1), w-tw-20-last_tw, i*th+th+7)
+ if len(self.img) > 1 and self.channelNamesVisible:
+ tw, th = dc.GetTextExtent("chan 8")
+ for i in range(len(self.img)):
+ dc.SetTextForeground(self.pens[i%self.chnls].GetColour())
+ dc.DrawText("chan %d" % (i+1), w-tw-20-last_tw, i*th+th+7)
# channel spectrums
for i, samples in enumerate(self.img):
- gc.SetPen(self.pens[i%8])
- gc.SetBrush(self.brushes[i%8])
+ gc.SetPen(self.pens[i%self.chnls])
+ gc.SetBrush(self.brushes[i%self.chnls])
gc.DrawLines(samples)
######################################################################
@@ -1790,6 +1817,9 @@ class ScopeDisplay(wx.Frame):
def update(self, points):
self.scopePanel.setImage(points)
+ def showChannelNames(self, visible):
+ self.scopePanel.showChannelNames(visible)
+
def _destroy(self, evt):
self.obj._setViewFrame(None)
self.Destroy()
@@ -1800,6 +1830,7 @@ class ScopePanel(wx.Panel):
size=wx.DefaultSize, style=0):
wx.Panel.__init__(self, parent, pos=pos, size=size, style=style)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
+ self.SetMinSize((300,100))
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.img = [[]]
@@ -1807,13 +1838,18 @@ class ScopePanel(wx.Panel):
if self.obj is not None:
self.gain = self.obj.gain
self.length = self.obj.length
+ self.chnls = len(self.obj)
+ self.channelNamesVisible = self.obj.channelNamesVisible
else:
self.gain = 1
self.length = 0.05
- #self.chnls = len(self.obj)
- self.pens = [wx.Pen(wx.Colour(166,4,0), width=2), wx.Pen(wx.Colour(8,11,116), width=2), wx.Pen(wx.Colour(0,204,0), width=2),
- wx.Pen(wx.Colour(255,167,0), width=2), wx.Pen(wx.Colour(133,0,75), width=2), wx.Pen(wx.Colour(255,236,0), width=2),
- wx.Pen(wx.Colour(1,147,154), width=2), wx.Pen(wx.Colour(162,239,0), width=2)]
+ self.chnls = 64
+ self.channelNamesVisible = True
+ self.pens = []
+ for x in range(self.chnls):
+ hsv = wx.Image_HSVValue(x / float(self.chnls), 1.0, 1.0)
+ rgb = wx.Image_HSVtoRGB(hsv)
+ self.pens.append(wx.Pen(wx.Colour(rgb.red, rgb.green, rgb.blue)))
if sys.platform == "win32" or sys.platform.startswith("linux"):
self.dcref = wx.BufferedPaintDC
@@ -1839,6 +1875,9 @@ class ScopePanel(wx.Panel):
self.img = points
wx.CallAfter(self.Refresh)
+ def showChannelNames(self, visible=True):
+ self.channelNamesVisible = visible
+
def OnPaint(self, evt):
w,h = self.GetSize()
dc = self.dcref(self)
@@ -1850,10 +1889,14 @@ class ScopePanel(wx.Panel):
gc.SetPen(wx.Pen('#000000', width=1, style=wx.SOLID))
gc.SetBrush(wx.Brush("#FFFFFF", style=wx.TRANSPARENT))
dc.SetTextForeground("#444444")
- if sys.platform in "darwin":
+ if sys.platform == "darwin":
font, ptsize = dc.GetFont(), dc.GetFont().GetPointSize()
font.SetPointSize(ptsize - 3)
dc.SetFont(font)
+ elif sys.platform.startswith("linux"):
+ font, ptsize = dc.GetFont(), dc.GetFont().GetPointSize()
+ font.SetPointSize(ptsize - 1)
+ dc.SetFont(font)
elif sys.platform == "win32":
font = dc.GetFont()
font.SetPointSize(8)
@@ -1881,15 +1924,16 @@ class ScopePanel(wx.Panel):
# draw waveforms
for i, samples in enumerate(self.img):
gc.SetPen(self.pens[i%8])
- if len(samples):
+ if len(samples) > 1:
gc.DrawLines(samples)
# legend
last_tw = tw
- tw, th = dc.GetTextExtent("chan 8")
- for i in range(len(self.img)):
- dc.SetTextForeground(self.pens[i%8].GetColour())
- dc.DrawText("chan %d" % (i+1), w-tw-20-last_tw, i*th+10)
+ if len(self.img) > 1 and self.channelNamesVisible:
+ tw, th = dc.GetTextExtent("chan 8")
+ for i in range(len(self.img)):
+ dc.SetTextForeground(self.pens[i % self.chnls].GetColour())
+ dc.DrawText("chan %d" % (i+1), w-tw-20-last_tw, i*th+10)
######################################################################
## Grapher window for PyoTableObject control
@@ -2894,56 +2938,49 @@ class ServerGUI(wx.Frame):
panel.SetBackgroundColour(BACKGROUND_COLOUR)
box = wx.BoxSizer(wx.VERTICAL)
- if sys.platform == "win32":
- leftMargin = 24
- else:
- leftMargin = 25
-
buttonBox = wx.BoxSizer(wx.HORIZONTAL)
- self.startButton = wx.Button(panel, -1, 'Start', (20,20), (72,-1))
+ self.startButton = wx.Button(panel, -1, 'Start')
self.startButton.Bind(wx.EVT_BUTTON, self.start)
- buttonBox.Add(self.startButton, 0, wx.RIGHT, 5)
+ buttonBox.Add(self.startButton, 0, wx.LEFT | wx.RIGHT, 5)
- self.recButton = wx.Button(panel, -1, 'Rec Start', (20,20), (72,-1))
+ self.recButton = wx.Button(panel, -1, 'Rec Start')
self.recButton.Bind(wx.EVT_BUTTON, self.record)
buttonBox.Add(self.recButton, 0, wx.RIGHT, 5)
- self.quitButton = wx.Button(panel, -1, 'Quit', (20,20), (72,-1))
+ self.quitButton = wx.Button(panel, -1, 'Quit')
self.quitButton.Bind(wx.EVT_BUTTON, self.on_quit)
- buttonBox.Add(self.quitButton, 0, wx.RIGHT, 0)
+ buttonBox.Add(self.quitButton, 0, wx.RIGHT, 5)
- box.Add(buttonBox, 0, wx.TOP | wx.LEFT | wx.RIGHT, 10)
+ box.Add(buttonBox, 0, wx.TOP, 10)
box.AddSpacer(10)
- box.Add(wx.StaticText(panel, -1, "Amplitude (dB)"), 0, wx.LEFT, leftMargin)
- ampBox = wx.BoxSizer(wx.HORIZONTAL)
+ box.Add(wx.StaticText(panel, -1, "Amplitude (dB)"), 0, wx.LEFT, 5)
self.ampScale = ControlSlider(panel, -60, 18, 20.0 * math.log10(amp), size=(202, 16), outFunction=self.setAmp)
- ampBox.Add(self.ampScale, 0, wx.LEFT, leftMargin-10)
- box.Add(ampBox, 0, wx.LEFT | wx.RIGHT, 8)
+ box.Add(self.ampScale, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 5)
if meter:
box.AddSpacer(10)
self.meter = VuMeter(panel, size=(200,5*self.nchnls+1), numSliders=self.nchnls)
- box.Add(self.meter, 0, wx.LEFT, leftMargin-1)
+ box.Add(self.meter, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 5)
box.AddSpacer(5)
if timer:
box.AddSpacer(10)
tt = wx.StaticText(panel, -1, "Elapsed time (hh:mm:ss:ms)")
- box.Add(tt, 0, wx.LEFT, leftMargin)
+ box.Add(tt, 0, wx.LEFT, 5)
box.AddSpacer(3)
self.timetext = wx.StaticText(panel, -1, "00 : 00 : 00 : 000")
- box.Add(self.timetext, 0, wx.LEFT, leftMargin)
+ box.Add(self.timetext, 0, wx.LEFT, 5)
if self.locals is not None:
box.AddSpacer(10)
t = wx.StaticText(panel, -1, "Interpreter")
- box.Add(t, 0, wx.LEFT, leftMargin)
+ box.Add(t, 0, wx.LEFT, 5)
tw, th = self.GetTextExtent("|")
self.text = wx.TextCtrl(panel, -1, "", size=(202, th+8), style=wx.TE_PROCESS_ENTER)
self.text.Bind(wx.EVT_TEXT_ENTER, self.getText)
self.text.Bind(wx.EVT_CHAR, self.onChar)
- box.Add(self.text, 0, wx.LEFT, leftMargin-1)
+ box.Add(self.text, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 5)
box.AddSpacer(10)
panel.SetSizerAndFit(box)
diff --git a/pyolib/analysis.py b/pyolib/analysis.py
index 4189cbf..7352148 100644
--- a/pyolib/analysis.py
+++ b/pyolib/analysis.py
@@ -837,6 +837,8 @@ class Spectrum(PyoObject):
list of lists, one list per channel). Useful if someone
wants to save the analysis data into a text file.
Defaults to None.
+ wintitle: string, optional
+ GUI window title. Defaults to "Spectrum".
.. note::
@@ -850,11 +852,12 @@ class Spectrum(PyoObject):
>>> spec = Spectrum(a, size=1024)
"""
- def __init__(self, input, size=1024, wintype=2, function=None):
- pyoArgsAssert(self, "oiiC", input, size, wintype, function)
+ def __init__(self, input, size=1024, wintype=2, function=None, wintitle="Spectrum"):
+ pyoArgsAssert(self, "oiiCS", input, size, wintype, function, wintitle)
PyoObject.__init__(self)
self.points = None
self.viewFrame = None
+ self.channelNamesVisible = True
self._input = input
self._size = size
self._wintype = wintype
@@ -871,7 +874,7 @@ class Spectrum(PyoObject):
self._base_objs = [Spectrum_base(wrap(in_fader,i), wrap(size,i), wrap(wintype,i)) for i in range(lmax)]
self._timer = Pattern(self.refreshView, 0.05).play()
if function is None:
- self.view()
+ self.view(wintitle)
self.play()
def setInput(self, x, fadetime=0.05):
@@ -1171,6 +1174,15 @@ class Spectrum(PyoObject):
pyoArgsAssert(self, "SB", title, wxnoserver)
createSpectrumWindow(self, title, wxnoserver)
+ def showChannelNames(self, visible=True):
+ """
+ If True (the default), channel names will be displayed in the window.
+
+ """
+ self.channelNamesVisible = visible
+ if self.viewFrame is not None:
+ self.viewFrame.showChannelNames(visible)
+
def _setViewFrame(self, frame):
self.viewFrame = frame
@@ -1281,6 +1293,8 @@ class Scope(PyoObject):
list of lists, one list per channel). Useful if someone
wants to save the analysis data into a text file.
Defaults to None.
+ wintitle: string, optional
+ GUI window title. Defaults to "Scope".
.. note::
@@ -1295,8 +1309,8 @@ class Scope(PyoObject):
>>> scope = Scope(a+b)
"""
- def __init__(self, input, length=0.05, gain=0.67, function=None):
- pyoArgsAssert(self, "oNNC", input, length, gain, function)
+ def __init__(self, input, length=0.05, gain=0.67, function=None, wintitle="Scope"):
+ pyoArgsAssert(self, "oNNCS", input, length, gain, function, wintitle)
PyoObject.__init__(self)
self.points = None
self.viewFrame = None
@@ -1306,12 +1320,13 @@ class Scope(PyoObject):
self._function = function
self._width = 500
self._height = 400
+ self.channelNamesVisible = True
self._in_fader = InputFader(input)
in_fader, lmax = convertArgsToLists(self._in_fader)
self._base_objs = [Scope_base(wrap(in_fader,i), length) for i in range(lmax)]
self._timer = Pattern(self.refreshView, length).play()
if function is None:
- self.view()
+ self.view(wintitle)
self.play()
def setInput(self, x, fadetime=0.05):
@@ -1415,12 +1430,12 @@ class Scope(PyoObject):
def view(self, title="Scope", wxnoserver=False):
"""
- Opens a window showing the result of the analysis.
+ Opens a window showing the incoming waveform.
:Args:
title: string, optional
- Window title. Defaults to "Spectrum".
+ Window title. Defaults to "Scope".
wxnoserver: boolean, optional
With wxPython graphical toolkit, if True, tells the
interpreter that there will be no server window.
@@ -1447,6 +1462,15 @@ class Scope(PyoObject):
pyoArgsAssert(self, "C", function)
self._function = getWeakMethodRef(function)
+ def showChannelNames(self, visible=True):
+ """
+ If True (the default), channel names will be displayed in the window.
+
+ """
+ self.channelNamesVisible = visible
+ if self.viewFrame is not None:
+ self.viewFrame.showChannelNames(visible)
+
def _setViewFrame(self, frame):
self.viewFrame = frame
diff --git a/pyolib/listener.py b/pyolib/listener.py
index 5a1a09d..b13b078 100644
--- a/pyolib/listener.py
+++ b/pyolib/listener.py
@@ -76,6 +76,13 @@ class MidiListener(threading.Thread):
except:
pass
+ def stop(self):
+ """
+ Stops the listener and properly close the midi ports.
+
+ """
+ self._listener.stop()
+
def getDeviceInfos(self):
"""
Returns infos about connected midi devices.
diff --git a/pyolib/pan.py b/pyolib/pan.py
index ab68e2d..5b4bd83 100644
--- a/pyolib/pan.py
+++ b/pyolib/pan.py
@@ -26,7 +26,7 @@ GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with pyo. If not, see <http://www.gnu.org/licenses/>.
"""
-import sys, random
+import sys
from ._core import *
from ._maps import *
@@ -670,9 +670,9 @@ class Mixer(PyoObject):
"""
pyoArgsAssert(self, "o", input)
if voice is None:
- voice = random.randint(0, 32767)
+ voice = get_random_integer(mx=32767)
while voice in self._inputs:
- voice = random.randint(0, 32767)
+ voice = get_random_integer(mx=32767)
if voice in self._inputs:
print("Mixer has already a key named %s" % voice, file=sys.stderr)
return
diff --git a/pyolib/phasevoc.py b/pyolib/phasevoc.py
index 0bc1774..c10ea5d 100644
--- a/pyolib/phasevoc.py
+++ b/pyolib/phasevoc.py
@@ -74,6 +74,16 @@ class PVAnal(PyoPVObject):
6. Blackman-Harris 7-term
7. Tuckey (alpha = 0.66)
8. Sine (half-sine window)
+ callback: callable, optional
+ If not None (default), this function will be called with the result
+ of the analysis at the end of every overlap. The function will
+ receive two arguments, a list of floats for both the magnitudes
+ and the frequencies. The signature is:
+
+ callback(magnitudes, frequencies)
+
+ If you analyse a multi-channel signal, you should pass a list
+ of callables, one per channel to analyse.
>>> s = Server().boot()
>>> s.start()
@@ -82,16 +92,17 @@ class PVAnal(PyoPVObject):
>>> pvs = PVSynth(pva).mix(2).out()
"""
- def __init__(self, input, size=1024, overlaps=4, wintype=2):
- pyoArgsAssert(self, "oiii", input, size, overlaps, wintype)
+ def __init__(self, input, size=1024, overlaps=4, wintype=2, callback=None):
+ pyoArgsAssert(self, "oiiic", input, size, overlaps, wintype, callback)
PyoPVObject.__init__(self)
self._input = input
self._size = size
self._overlaps = overlaps
self._wintype = wintype
+ self._callback = callback
self._in_fader = InputFader(input)
- in_fader, size, overlaps, wintype, lmax = convertArgsToLists(self._in_fader, size, overlaps, wintype)
- self._base_objs = [PVAnal_base(wrap(in_fader,i), wrap(size,i), wrap(overlaps,i), wrap(wintype,i)) for i in range(lmax)]
+ in_fader, size, overlaps, wintype, callback, lmax = convertArgsToLists(self._in_fader, size, overlaps, wintype, callback)
+ self._base_objs = [PVAnal_base(wrap(in_fader,i), wrap(size,i), wrap(overlaps,i), wrap(wintype,i), wrap(callback,i)) for i in range(lmax)]
self.play()
def setInput(self, x, fadetime=0.05):
@@ -150,10 +161,26 @@ class PVAnal(PyoPVObject):
new `wintype` attribute.
"""
+ pyoArgsAssert(self, "i", x)
self._wintype = x
x, lmax = convertArgsToLists(x)
[obj.setWinType(wrap(x,i)) for i, obj in enumerate(self._base_objs)]
+ def setCallback(self, x):
+ """
+ Replace the `callback` attribute.
+
+ :Args:
+
+ x: callable
+ new `callback` attribute.
+
+ """
+ pyoArgsAssert(self, "c", x)
+ self._callback = x
+ x, lmax = convertArgsToLists(x)
+ [obj.setCallback(wrap(x,i)) for i, obj in enumerate(self._base_objs)]
+
@property
def input(self):
"""PyoObject. Input signal to process."""
diff --git a/pyolib/server.py b/pyolib/server.py
index 37a54e3..9d30948 100644
--- a/pyolib/server.py
+++ b/pyolib/server.py
@@ -85,6 +85,12 @@ class Server(object):
winhost: string, optional
Under Windows, pyo's Server will try to use the default devices of the given host.
This behaviour can be changed with the SetXXXDevice methods.
+ midi: string {'portmidi', 'pm', 'jack'}, optional
+ Midi backend to use. 'pm' is equivalent to 'portmidi'. Default is 'portmidi'.
+
+ If 'jack' is selected but jackd is not already started when the program is executed, pyo
+ will ask jack to start in the background. Note that pyo never ask jack to close. It is
+ the user's responsability to manage the audio/midi configuration of its system.
.. note::
@@ -115,8 +121,8 @@ class Server(object):
>>> s.start()
"""
- def __init__(self, sr=44100, nchnls=2, buffersize=256, duplex=1,
- audio='portaudio', jackname='pyo', ichnls=None, winhost="wasapi"):
+ def __init__(self, sr=44100, nchnls=2, buffersize=256, duplex=1, audio='portaudio',
+ jackname='pyo', ichnls=None, winhost="wasapi", midi="portmidi"):
if "PYO_SERVER_AUDIO" in os.environ and "offline" not in audio and "embedded" not in audio:
audio = os.environ["PYO_SERVER_AUDIO"]
self._time = time
@@ -136,7 +142,7 @@ class Server(object):
self._globalseed = 0
self._resampling = 1
self._isJackTransportSlave = False
- self._server = Server_base(sr, nchnls, buffersize, duplex, audio, jackname, self._ichnls)
+ self._server = Server_base(sr, nchnls, buffersize, duplex, audio, jackname, self._ichnls, midi)
self._server._setDefaultRecPath(os.path.join(os.path.expanduser("~"), "pyo_rec.wav"))
if sys.platform.startswith("win"):
@@ -154,8 +160,8 @@ class Server(object):
self.shutdown()
self._time.sleep(.25)
- def reinit(self, sr=44100, nchnls=2, buffersize=256, duplex=1,
- audio='portaudio', jackname='pyo', ichnls=None, winhost="wasapi"):
+ def reinit(self, sr=44100, nchnls=2, buffersize=256, duplex=1, audio='portaudio',
+ jackname='pyo', ichnls=None, winhost="wasapi", midi="portmidi"):
"""
Reinit the server'settings. Useful to alternate between real-time and offline server.
@@ -181,7 +187,7 @@ class Server(object):
self._globalseed = 0
self._resampling = 1
self._isJackTransportSlave = False
- self._server.__init__(sr, nchnls, buffersize, duplex, audio, jackname, self._ichnls)
+ self._server.__init__(sr, nchnls, buffersize, duplex, audio, jackname, self._ichnls, midi)
if sys.platform.startswith("win"):
host_default_in, host_default_out = pa_get_default_devices_from_host(winhost)
@@ -225,7 +231,7 @@ class Server(object):
title, "Pyo Server" is used.
"""
- self._gui_frame, win = createServerGUI(self._nchnls, self.start, self.stop,
+ self._gui_frame, win, withWX = createServerGUI(self._nchnls, self.start, self.stop,
self.recstart, self.recstop, self.setAmp,
self.getIsStarted(), locals, self.shutdown,
meter, timer, self._amp, exit, title)
@@ -233,11 +239,10 @@ class Server(object):
self._server.setAmpCallable(self._gui_frame)
if timer:
self._server.setTimeCallable(self._gui_frame)
- try:
+ if withWX:
+ win.MainLoop()
+ else:
win.mainloop()
- except:
- if win is not None:
- win.MainLoop()
def closeGui(self):
"""
@@ -603,6 +608,64 @@ class Server(object):
"""
self._server.setJackOutputPortNames(name)
+ def setJackAutoConnectMidiInputPort(self, ports):
+ """
+ Tells the server to auto-connect Jack midi input port to pre-defined Jack ports.
+
+ :Args:
+
+ ports: string or list of strings
+ Name of the Jack ports to auto-connect to pyo midi input channel.
+
+ ['ports', 'to', 'midi', 'input', ...]
+
+ """
+ ports, lmax = convertArgsToLists(ports)
+ self._server.setJackAutoConnectMidiInputPort(ports)
+
+ def setJackAutoConnectMidiOutputPort(self, ports):
+ """
+ Tells the server to auto-connect Jack midi output port to pre-defined Jack ports.
+
+ :Args:
+
+ ports: string or list of strings
+ Name of the Jack ports to auto-connect to pyo midi output channel.
+
+ ['ports', 'to', 'midi', 'output', ...]
+
+ """
+ ports, lmax = convertArgsToLists(ports)
+ self._server.setJackAutoConnectMidiOutputPort(ports)
+
+ def setJackMidiInputPortName(self, name):
+ """
+ Change the short name of pyo's midi input port for the jack server.
+
+ This method must be called after the server is booted.
+
+ :Args:
+
+ name: string
+ New name of midi input port for the jack server.
+
+ """
+ self._server.setJackMidiInputPortName(name)
+
+ def setJackMidiOutputPortName(self, name):
+ """
+ Change the short name of pyo's midi output port for the jack server.
+
+ This method must be called after the server is booted.
+
+ :Args:
+
+ name: string
+ New name of midi output port for the jack server.
+
+ """
+ self._server.setJackMidiOutputPortName(name)
+
def setIsJackTransportSlave(self, x):
"""
Set if pyo's server is slave to jack transport or not.
@@ -812,7 +875,7 @@ class Server(object):
print('Warning: Filename has no extension. Using fileformat value.')
self._fileformat = fileformat
self._sampletype = sampletype
- self._server.recordOptions(dur, stringencode(filename), fileformat, sampletype, quality)
+ self._server.recordOptions(dur, filename, fileformat, sampletype, quality)
def recstart(self, filename=None):
"""
@@ -840,7 +903,7 @@ class Server(object):
fileformat = FILE_FORMATS[ext]
if fileformat != self._fileformat:
self._fileformat = fileformat
- self._server.recordOptions(self._dur, stringencode(filename), self._fileformat, self._sampletype)
+ self._server.recordOptions(self._dur, filename, self._fileformat, self._sampletype)
self._server.recstart(filename)
@@ -870,9 +933,9 @@ class Server(object):
note is sent. A channel of 0 means all channels.
Defaults to 0.
timestamp: int, optional
- The delay time, in milliseconds, before the note
- is sent on the portmidi stream. A value of 0 means
- to play the note now. Defaults to 0.
+ The delay time, in milliseconds, before the message
+ is sent to the output midi stream. A value of 0
+ means to play the note now. Defaults to 0.
"""
pitch, velocity, channel, timestamp, lmax = convertArgsToLists(pitch, velocity, channel, timestamp)
[self._server.noteout(wrap(pitch,i), wrap(velocity,i), wrap(channel,i), wrap(timestamp,i)) for i in range(lmax)]
@@ -895,9 +958,9 @@ class Server(object):
note is sent. A channel of 0 means all channels.
Defaults to 0.
timestamp: int, optional
- The delay time, in milliseconds, before the note
- is sent on the portmidi stream. A value of 0 means
- to play the note now. Defaults to 0.
+ The delay time, in milliseconds, before the message
+ is sent to the output midi stream. A value of 0
+ means to play the note now. Defaults to 0.
"""
pitch, velocity, channel, timestamp, lmax = convertArgsToLists(pitch, velocity, channel, timestamp)
[self._server.afterout(wrap(pitch,i), wrap(velocity,i), wrap(channel,i), wrap(timestamp,i)) for i in range(lmax)]
@@ -921,8 +984,8 @@ class Server(object):
Defaults to 0.
timestamp: int, optional
The delay time, in milliseconds, before the message
- is sent on the portmidi stream. A value of 0 means
- to play the message now. Defaults to 0.
+ is sent to the output midi stream. A value of 0
+ means to play the note now. Defaults to 0.
"""
ctlnum, value, channel, timestamp, lmax = convertArgsToLists(ctlnum, value, channel, timestamp)
[self._server.ctlout(wrap(ctlnum,i), wrap(value,i), wrap(channel,i), wrap(timestamp,i)) for i in range(lmax)]
@@ -944,8 +1007,8 @@ class Server(object):
Defaults to 0.
timestamp: int, optional
The delay time, in milliseconds, before the message
- is sent on the portmidi stream. A value of 0 means
- to play the message now. Defaults to 0.
+ is sent to the output midi stream. A value of 0
+ means to play the note now. Defaults to 0.
"""
value, channel, timestamp, lmax = convertArgsToLists(value, channel, timestamp)
[self._server.programout(wrap(value,i), wrap(channel,i), wrap(timestamp,i)) for i in range(lmax)]
@@ -967,8 +1030,8 @@ class Server(object):
Defaults to 0.
timestamp: int, optional
The delay time, in milliseconds, before the message
- is sent on the portmidi stream. A value of 0 means
- to play the message now. Defaults to 0.
+ is sent to the output midi stream. A value of 0
+ means to play the note now. Defaults to 0.
"""
value, channel, timestamp, lmax = convertArgsToLists(value, channel, timestamp)
[self._server.pressout(wrap(value,i), wrap(channel,i), wrap(timestamp,i)) for i in range(lmax)]
@@ -991,8 +1054,8 @@ class Server(object):
Defaults to 0.
timestamp: int, optional
The delay time, in milliseconds, before the message
- is sent on the portmidi stream. A value of 0 means
- to play the message now. Defaults to 0.
+ is sent to the output midi stream. A value of 0
+ means to play the note now. Defaults to 0.
"""
value, channel, timestamp, lmax = convertArgsToLists(value, channel, timestamp)
[self._server.bendout(wrap(value,i), wrap(channel,i), wrap(timestamp,i)) for i in range(lmax)]
@@ -1002,7 +1065,8 @@ class Server(object):
Send a system exclusive message to the selected midi output device.
Arguments can be list of values/messages to generate multiple events
- in one call.
+ in one call. Implemented only for portmidi, this method is not available
+ when using jack as the midi backend.
:Args:
@@ -1011,7 +1075,7 @@ class Server(object):
must be 0xf0 and the last one must be 0xf7.
timestamp: int, optional
The delay time, in milliseconds, before the message
- is sent on the portmidi stream. A value of 0 means
+ is sent to the output midi stream. A value of 0 means
to play the message now. Defaults to 0.
"""
msg, timestamp, lmax = convertArgsToLists(msg, timestamp)
diff --git a/scripts/win_audio_drivers_inspector.py b/scripts/win_audio_drivers_inspector.py
new file mode 100644
index 0000000..1e72de5
--- /dev/null
+++ b/scripts/win_audio_drivers_inspector.py
@@ -0,0 +1,59 @@
+"""
+Windows audio host inspector.
+
+This script will check if pyo can run in duplex mode (both audio input and output)
+and will test every host API to help the user in making his audio device choice.
+
+"""
+import sys, time
+from pyo import *
+
+if sys.platform == "win32":
+ host_apis = ["mme", "directsound", "asio", "wasapi", "wdm-ks"]
+else:
+ print("This program must be used on a windows system! Ciao!")
+ exit()
+
+
+print("* Checking for any available audio input... *")
+
+input_names, input_indexes = pa_get_input_devices()
+
+print("* Checking audio output hosts... *")
+
+s = Server(duplex=0)
+s.verbosity = 0
+
+host_results = []
+for host in host_apis:
+ print("* Testing %s... *" % host)
+ try:
+ s.reinit(buffersize=1024, duplex=0, winhost=host)
+ s.boot().start()
+ a = Sine(freq=440, mul=0.2).out()
+ time.sleep(2)
+ s.stop()
+ s.shutdown()
+ host_results.append(True)
+ except:
+ host_results.append(False)
+
+print("\nResults")
+print("-------\n")
+
+if len(input_names):
+ print("Duplex mode OK!")
+ print("Initialize the Server with duplex=1 as argument.\n")
+else:
+ print("No input available. Duplex mode should be turned off.")
+ print("Initialize the Server with duplex=0 as argument.\n")
+
+for i, host in enumerate(host_apis):
+ if host_results[i]:
+ print("Host: %s ==> OK!" % host)
+ else:
+ print("Host: %s ==> Failed..." % host)
+
+print("Initialize the Server with the desired host given to winhost argument.")
+
+print("\nFinished!")
diff --git a/setup.py b/setup.py
index f96a0c4..6860aeb 100644
--- a/setup.py
+++ b/setup.py
@@ -81,15 +81,15 @@ if '--compile-externals' in sys.argv:
if '--debug' in sys.argv:
sys.argv.remove('--debug')
- gflag = "-g3"
+ gflag = ["-g3", "-UNDEBUG"]
else:
- gflag = "-g0"
+ gflag = ["-g0", "-DNDEBUG"]
if '--fast-compile' in sys.argv:
sys.argv.remove('--fast-compile')
- oflag = "-O0"
+ oflag = ["-O0"]
else:
- oflag = "-O3"
+ oflag = ["-O3"]
# Specific audio drivers source files to compile
ad_files = []
@@ -181,7 +181,7 @@ else:
libraries.append('jack')
libraries += ['m']
-extra_compile_args = ['-Wno-strict-prototypes', '-Wno-strict-aliasing', oflag, gflag]
+extra_compile_args = ['-Wno-strict-prototypes', '-Wno-strict-aliasing'] + oflag + gflag
extensions = []
for extension_name, extra_macros in zip(extension_names, extra_macros_per_extension):
diff --git a/src/engine/ad_jack.c b/src/engine/ad_jack.c
index d63ca39..19e4c13 100644
--- a/src/engine/ad_jack.c
+++ b/src/engine/ad_jack.c
@@ -21,7 +21,45 @@
#include "ad_jack.h"
#include "py2to3.h"
-int
+static PyoMidiEvent
+JackMidiEventToPyoMidiEvent(jack_midi_event_t event)
+{
+ PyoMidiEvent newbuf;
+ newbuf.message = PyoMidi_Message(event.buffer[0], event.buffer[1], event.buffer[2]);
+ newbuf.timestamp = (long)event.time;
+ return newbuf;
+}
+
+static int
+jackMidiEventCompare(const void *evt1, const void *evt2) {
+ const PyoJackMidiEvent *event1 = evt1;
+ const PyoJackMidiEvent *event2 = evt2;
+ if (event1->timestamp > event2->timestamp) return 1;
+ if (event1->timestamp < event2->timestamp) return -1;
+ return 0;
+}
+
+static int
+jackMidiEventSort(PyoJackMidiEvent *orig, PyoJackMidiEvent *new, Server *server) {
+ int i, count = 0;
+ jack_nframes_t time = 0;
+ for (i=0; i<512; i++) {
+ if (orig[i].timestamp != -1 && orig[i].timestamp < (server->elapsedSamples + server->bufferSize)) {
+ time = orig[i].timestamp % server->bufferSize;
+ new[count] = orig[i];
+ new[count].timestamp = time;
+ orig[i].timestamp = -1;
+ count++;
+ }
+ }
+ if (count > 1) {
+ qsort(new, count, sizeof(PyoJackMidiEvent), jackMidiEventCompare);
+ }
+
+ return count;
+}
+
+static int
jack_callback(jack_nframes_t nframes, void *arg) {
int i, j;
Server *server = (Server *) arg;
@@ -49,7 +87,37 @@ jack_callback(jack_nframes_t nframes, void *arg) {
return 0;
}
- if (server->withPortMidi == 1) {
+ if (server->withJackMidi) {
+ // Handle midiout events.
+ if (be_data->midi_event_count > 0) {
+ PyoJackMidiEvent ordlist[512];
+ int numevents = jackMidiEventSort(be_data->midi_events, ordlist, server);
+ be_data->midi_event_count -= numevents;
+
+ unsigned char *buffer;
+ void *port_buf_out = jack_port_get_buffer(be_data->jack_midiout_port, server->bufferSize);
+ jack_midi_clear_buffer(port_buf_out);
+ for (i=0; i<numevents; i++) {
+ buffer = jack_midi_event_reserve(port_buf_out, ordlist[i].timestamp, 3);
+ buffer[0] = ordlist[i].status;
+ buffer[1] = ordlist[i].data1;
+ buffer[2] = ordlist[i].data2;
+ }
+ }
+
+ // Handle midiin events.
+ void* port_buf_in = jack_port_get_buffer(be_data->jack_midiin_port, server->bufferSize);
+
+ jack_midi_event_t in_event;
+ jack_nframes_t event_count = jack_midi_get_event_count(port_buf_in);
+
+ if (event_count > 0) {
+ for (i=0; i<event_count; i++) {
+ jack_midi_event_get(&in_event, port_buf_in, i);
+ server->midiEvents[server->midi_count++] = JackMidiEventToPyoMidiEvent(in_event);
+ }
+ }
+ } else {
pyoGetMidiEvents(server);
}
@@ -71,7 +139,7 @@ jack_callback(jack_nframes_t nframes, void *arg) {
return 0;
}
-int
+static int
jack_transport_cb(jack_transport_state_t state, jack_position_t *pos, void *arg) {
Server *server = (Server *) arg;
@@ -104,7 +172,7 @@ jack_transport_cb(jack_transport_state_t state, jack_position_t *pos, void *arg)
return 0;
}
-int
+static int
jack_srate_cb(jack_nframes_t nframes, void *arg) {
Server *server = (Server *) arg;
server->samplingRate = (double) nframes;
@@ -116,7 +184,7 @@ jack_srate_cb(jack_nframes_t nframes, void *arg) {
return 0;
}
-int
+static int
jack_bufsize_cb(jack_nframes_t nframes, void *arg) {
Server *server = (Server *) arg;
server->bufferSize = (int) nframes;
@@ -128,14 +196,14 @@ jack_bufsize_cb(jack_nframes_t nframes, void *arg) {
return 0;
}
-void
+static void
jack_error_cb(const char *desc) {
PyGILState_STATE s = PyGILState_Ensure();
PySys_WriteStdout("JACK error: %s\n", desc);
PyGILState_Release(s);
}
-void
+static void
jack_shutdown_cb(void *arg) {
Server *server = (Server *) arg;
@@ -145,7 +213,7 @@ jack_shutdown_cb(void *arg) {
PyGILState_Release(s);
}
-void
+static void
Server_jack_autoconnect(Server *self) {
const char **ports;
char *portname;
@@ -257,10 +325,55 @@ Server_jack_autoconnect(Server *self) {
}
}
}
+
+ if (self->withJackMidi) {
+ num = PyList_Size(self->jackAutoConnectMidiInputPort);
+ if (num > 0) {
+ for (i=0; i<num; i++) {
+ portname = PY_STRING_AS_STRING(PyList_GetItem(self->jackAutoConnectMidiInputPort, i));
+
+ if (jack_port_by_name(be_data->jack_client, portname) != NULL) {
+
+ Py_BEGIN_ALLOW_THREADS
+ err = jack_connect(be_data->jack_client, portname, jack_port_name(be_data->jack_midiin_port));
+ Py_END_ALLOW_THREADS
+
+ if (err) {
+ Server_error(self, "Jack: cannot connect '%s' to midi input port\n", portname);
+ }
+ }
+ else {
+ Server_error(self, "Jack: cannot find port '%s'\n", portname);
+ }
+ }
+ }
+
+ num = PyList_Size(self->jackAutoConnectMidiOutputPort);
+ if (num > 0) {
+ for (i=0; i<num; i++) {
+ portname = PY_STRING_AS_STRING(PyList_GetItem(self->jackAutoConnectMidiOutputPort, i));
+
+ if (jack_port_by_name(be_data->jack_client, portname) != NULL) {
+
+ Py_BEGIN_ALLOW_THREADS
+ err = jack_connect(be_data->jack_client, jack_port_name(be_data->jack_midiout_port), portname);
+ Py_END_ALLOW_THREADS
+
+ if (err) {
+ Server_error(self, "Jack: cannot connect '%s' to midi output port\n", portname);
+ }
+ }
+ else {
+ Server_error(self, "Jack: cannot find port '%s'\n", portname);
+ }
+ }
+ }
+ }
}
int
Server_jack_init(Server *self) {
+ int i = 0;
char client_name[32];
char name[16];
const char *server_name = "server";
@@ -278,9 +391,16 @@ Server_jack_init(Server *self) {
strncpy(client_name, self->serverName, 32);
Py_BEGIN_ALLOW_THREADS
+ be_data->midi_event_count = 0;
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 *));
be_data->jack_client = jack_client_open(client_name, options, &status, server_name);
+ if (self->withJackMidi) {
+ be_data->midi_events = (PyoJackMidiEvent *)malloc(512 * sizeof(PyoJackMidiEvent));
+ for (i=0; i<512; i++) {
+ be_data->midi_events[i].timestamp = -1;
+ }
+ }
Py_END_ALLOW_THREADS
if (be_data->jack_client == NULL) {
@@ -326,6 +446,17 @@ Server_jack_init(Server *self) {
Server_debug(self, "Jack engine buffer size: %" PRIu32 "\n", bufferSize);
}
+ if (self->withJackMidi) {
+ Py_BEGIN_ALLOW_THREADS
+ be_data->jack_midiin_port = jack_port_register(be_data->jack_client, "input",
+ JACK_DEFAULT_MIDI_TYPE,
+ JackPortIsInput, 0);
+ be_data->jack_midiout_port = jack_port_register(be_data->jack_client, "output",
+ JACK_DEFAULT_MIDI_TYPE,
+ JackPortIsOutput, 0);
+ Py_END_ALLOW_THREADS
+ }
+
nchnls = total_nchnls = self->ichnls + self->input_offset;
while (nchnls-- > 0) {
index = total_nchnls - nchnls - 1;
@@ -410,7 +541,14 @@ Server_jack_deinit(Server *self) {
free(be_data->jack_in_ports);
free(be_data->jack_out_ports);
- free(self->audio_be_data);
+ if (self->withJackMidi == 1) {
+ free(be_data->midi_events);
+ }
+ /* Since I add midi_event_count entry in the PyoJackBackendData struct,
+ freeing the struct gives an error "free(): invalid next size (fast)".
+ until I find the correct solution, I'll just let leak the struct when
+ we shutdown the server. */
+ //free(self->audio_be_data);
return ret;
}
@@ -510,6 +648,58 @@ jack_output_port_set_names(Server *self) {
}
int
+jack_midi_input_port_set_name(Server *self) {
+ int err;
+ char *name;
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ if (PY_STRING_CHECK(self->jackMidiInputPortName)) {
+ name = PY_STRING_AS_STRING(self->jackMidiInputPortName);
+
+ Py_BEGIN_ALLOW_THREADS
+#ifdef JACK_NEW_API
+ err = jack_port_rename(be_data->jack_client, be_data->jack_midiin_port, name);
+#else
+ err = jack_port_set_name(be_data->jack_midiin_port, name);
+#endif
+ Py_END_ALLOW_THREADS
+
+ if (err)
+ Server_error(self, "Jack error: cannot change midi input port short name.\n");
+ }
+ else
+ Server_error(self, "Jack error: midi input port name must be a string.\n");
+
+ return 0;
+}
+
+int
+jack_midi_output_port_set_name(Server *self) {
+ int err;
+ char *name;
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ if (PY_STRING_CHECK(self->jackMidiOutputPortName)) {
+ name = PY_STRING_AS_STRING(self->jackMidiOutputPortName);
+
+ Py_BEGIN_ALLOW_THREADS
+#ifdef JACK_NEW_API
+ err = jack_port_rename(be_data->jack_client, be_data->jack_midiout_port, name);
+#else
+ err = jack_port_set_name(be_data->jack_midiout_port, name);
+#endif
+ Py_END_ALLOW_THREADS
+
+ if (err)
+ Server_error(self, "Jack error: cannot change midi output port short name.\n");
+ }
+ else
+ Server_error(self, "Jack error: midi output port name must be a string.\n");
+
+ return 0;
+}
+
+int
Server_jack_start(Server *self) {
return 0;
}
@@ -518,3 +708,163 @@ int
Server_jack_stop(Server *self) {
return 0;
}
+
+void
+jack_noteout(Server *self, int pit, int vel, int chan, long timestamp)
+{
+ int i;
+ unsigned long elapsed = Server_getElapsedTime(self);
+
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ PyoJackMidiEvent event;
+ event.timestamp = elapsed + (unsigned long)(timestamp * 0.001 * self->samplingRate);
+ if (chan == 0)
+ event.status = 0x90;
+ else
+ event.status = 0x90 | (chan - 1);
+ event.data1 = pit;
+ event.data2 = vel;
+
+ for (i=0; i<512; i++) {
+ if (be_data->midi_events[i].timestamp == -1) {
+ be_data->midi_events[i] = event;
+ be_data->midi_event_count++;
+ break;
+ }
+ }
+}
+
+void
+jack_afterout(Server *self, int pit, int vel, int chan, long timestamp)
+{
+ int i;
+ unsigned long elapsed = Server_getElapsedTime(self);
+
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ PyoJackMidiEvent event;
+ event.timestamp = elapsed + (unsigned long)(timestamp * 0.001 * self->samplingRate);
+ if (chan == 0)
+ event.status = 0xA0;
+ else
+ event.status = 0xA0 | (chan - 1);
+ event.data1 = pit;
+ event.data2 = vel;
+
+ for (i=0; i<512; i++) {
+ if (be_data->midi_events[i].timestamp == -1) {
+ be_data->midi_events[i] = event;
+ be_data->midi_event_count++;
+ break;
+ }
+ }
+}
+
+void
+jack_ctlout(Server *self, int ctlnum, int value, int chan, long timestamp)
+{
+ int i;
+ unsigned long elapsed = Server_getElapsedTime(self);
+
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ PyoJackMidiEvent event;
+ event.timestamp = elapsed + (unsigned long)(timestamp * 0.001 * self->samplingRate);
+ if (chan == 0)
+ event.status = 0xB0;
+ else
+ event.status = 0xB0 | (chan - 1);
+ event.data1 = ctlnum;
+ event.data2 = value;
+
+ for (i=0; i<512; i++) {
+ if (be_data->midi_events[i].timestamp == -1) {
+ be_data->midi_events[i] = event;
+ be_data->midi_event_count++;
+ break;
+ }
+ }
+}
+
+void
+jack_programout(Server *self, int value, int chan, long timestamp)
+{
+ int i;
+ unsigned long elapsed = Server_getElapsedTime(self);
+
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ PyoJackMidiEvent event;
+ event.timestamp = elapsed + (unsigned long)(timestamp * 0.001 * self->samplingRate);
+ if (chan == 0)
+ event.status = 0xC0;
+ else
+ event.status = 0xC0 | (chan - 1);
+ event.data1 = value;
+ event.data2 = 0;
+
+ for (i=0; i<512; i++) {
+ if (be_data->midi_events[i].timestamp == -1) {
+ be_data->midi_events[i] = event;
+ be_data->midi_event_count++;
+ break;
+ }
+ }
+}
+
+void
+jack_pressout(Server *self, int value, int chan, long timestamp)
+{
+ int i;
+ unsigned long elapsed = Server_getElapsedTime(self);
+
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ PyoJackMidiEvent event;
+ event.timestamp = elapsed + (unsigned long)(timestamp * 0.001 * self->samplingRate);
+ if (chan == 0)
+ event.status = 0xD0;
+ else
+ event.status = 0xD0 | (chan - 1);
+ event.data1 = value;
+ event.data2 = 0;
+
+ for (i=0; i<512; i++) {
+ if (be_data->midi_events[i].timestamp == -1) {
+ be_data->midi_events[i] = event;
+ be_data->midi_event_count++;
+ break;
+ }
+ }
+}
+
+void
+jack_bendout(Server *self, int value, int chan, long timestamp)
+{
+ int i, lsb, msb;
+ unsigned long elapsed = Server_getElapsedTime(self);
+
+ PyoJackBackendData *be_data = (PyoJackBackendData *) self->audio_be_data;
+
+ PyoJackMidiEvent event;
+ event.timestamp = elapsed + (unsigned long)(timestamp * 0.001 * self->samplingRate);
+ if (chan == 0)
+ event.status = 0xE0;
+ else
+ event.status = 0xE0 | (chan - 1);
+
+ lsb = value & 0x007F;
+ msb = (value & (0x007F << 7)) >> 7;
+
+ event.data1 = lsb;
+ event.data2 = msb;
+
+ for (i=0; i<512; i++) {
+ if (be_data->midi_events[i].timestamp == -1) {
+ be_data->midi_events[i] = event;
+ be_data->midi_event_count++;
+ break;
+ }
+ }
+}
diff --git a/src/engine/pyomodule.c b/src/engine/pyomodule.c
index 1e7d7cc..3ba0b5b 100644
--- a/src/engine/pyomodule.c
+++ b/src/engine/pyomodule.c
@@ -32,56 +32,56 @@
#ifdef USE_PORTAUDIO
#include "ad_portaudio.h"
-PyObject * with_portaudio() { Py_INCREF(Py_True); return Py_True; };
+static PyObject * with_portaudio() { Py_INCREF(Py_True); return Py_True; };
#else
#define pa_warning "Pyo built without Portaudio support.\n"
-PyObject * portaudio_get_version() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_version_text() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_count_host_apis() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_list_host_apis() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_default_host_api() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_count_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_list_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_devices_infos() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_output_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_output_max_channels(PyObject *self, PyObject *arg) { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_input_max_channels(PyObject *self, PyObject *arg) { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_input_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_default_input() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * portaudio_get_default_output() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
-PyObject * with_portaudio() { Py_INCREF(Py_False); return Py_False; };
+static PyObject * portaudio_get_version() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_version_text() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_count_host_apis() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_list_host_apis() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_default_host_api() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_count_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_list_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_devices_infos() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_output_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_output_max_channels(PyObject *self, PyObject *arg) { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_input_max_channels(PyObject *self, PyObject *arg) { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_input_devices() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_default_input() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * portaudio_get_default_output() { PySys_WriteStdout(pa_warning); Py_RETURN_NONE; };
+static PyObject * with_portaudio() { Py_INCREF(Py_False); return Py_False; };
#endif
#ifdef USE_PORTMIDI
#include "md_portmidi.h"
-PyObject * with_portmidi() { Py_INCREF(Py_True); return Py_True; };
+static PyObject * with_portmidi() { Py_INCREF(Py_True); return Py_True; };
#else
#define pm_warning "Pyo built without Portmidi sipport.\n"
-PyObject * portmidi_count_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
-PyObject * portmidi_list_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
-PyObject * portmidi_get_input_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
-PyObject * portmidi_get_output_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
-PyObject * portmidi_get_default_input() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
-PyObject * portmidi_get_default_output() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
-PyObject * with_portmidi() { Py_INCREF(Py_False); return Py_False; };
+static PyObject * portmidi_count_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
+static PyObject * portmidi_list_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
+static PyObject * portmidi_get_input_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
+static PyObject * portmidi_get_output_devices() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
+static PyObject * portmidi_get_default_input() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
+static PyObject * portmidi_get_default_output() { PySys_WriteStdout(pm_warning); Py_RETURN_NONE; };
+static PyObject * with_portmidi() { Py_INCREF(Py_False); return Py_False; };
#endif
#ifdef USE_JACK
-PyObject * with_jack() { Py_INCREF(Py_True); return Py_True; };
+static PyObject * with_jack() { Py_INCREF(Py_True); return Py_True; };
#else
-PyObject * with_jack() { Py_INCREF(Py_False); return Py_False; };
+static PyObject * with_jack() { Py_INCREF(Py_False); return Py_False; };
#endif
#ifdef USE_COREAUDIO
-PyObject * with_coreaudio() { Py_INCREF(Py_True); return Py_True; };
+static PyObject * with_coreaudio() { Py_INCREF(Py_True); return Py_True; };
#else
-PyObject * with_coreaudio() { Py_INCREF(Py_False); return Py_False; };
+static PyObject * with_coreaudio() { Py_INCREF(Py_False); return Py_False; };
#endif
#ifdef USE_OSC
-PyObject * with_osc() { Py_INCREF(Py_True); return Py_True; };
+static PyObject * with_osc() { Py_INCREF(Py_True); return Py_True; };
#else
-PyObject * with_osc() { Py_INCREF(Py_False); return Py_False; };
+static PyObject * with_osc() { Py_INCREF(Py_False); return Py_False; };
#endif
/** Portaudio utility functions __doc__ strings. **/
@@ -582,7 +582,8 @@ MYFLT HALF_BLACKMAN[513] = {5.999999848427251e-05, 6.0518785176100209e-05, 6.214
size is the convolution impulse response length in samples.
freq is the cutoff frequency in radians.
*/
-void gen_lp_impulse(MYFLT *array, int size, float freq) {
+static void
+gen_lp_impulse(MYFLT *array, int size, float freq) {
int i, ppi;
MYFLT pp, ppf, env, scl, invSum, val;
int half = size / 2;
@@ -621,7 +622,8 @@ void gen_lp_impulse(MYFLT *array, int size, float freq) {
size is the filter order. Minimum suggested = 16, ideal = 128 or higher.
gain is the gain of the filter.
*/
-void lp_conv(MYFLT *samples, MYFLT *impulse, int num_samps, int size, int gain) {
+static void
+lp_conv(MYFLT *samples, MYFLT *impulse, int num_samps, int size, int gain) {
int i, j, count, tmp_count;
MYFLT val;
MYFLT intmp[size];
@@ -877,7 +879,7 @@ typedef struct STACK_RECORD {
STACK_RECORD *m_pStack = NULL;
-void StackPush( int nAnchorIndex, int nFloaterIndex )
+static void StackPush( int nAnchorIndex, int nFloaterIndex )
{
STACK_RECORD *precPrev = m_pStack;
m_pStack = (STACK_RECORD *)malloc( sizeof(STACK_RECORD) );
@@ -886,7 +888,7 @@ void StackPush( int nAnchorIndex, int nFloaterIndex )
m_pStack->precPrev = precPrev;
}
-int StackPop( int *pnAnchorIndex, int *pnFloaterIndex )
+static int StackPop( int *pnAnchorIndex, int *pnFloaterIndex )
{
STACK_RECORD *precStack = m_pStack;
if ( precStack == NULL )
@@ -924,6 +926,7 @@ reducePoints(PyObject *self, PyObject *args, PyObject *kwds)
nPointsCount = PyList_Size(pointlist);
+ // TODO: these malloc's are never freed.
pPointsX = (MYFLT *)malloc(nPointsCount * sizeof(MYFLT));
pPointsY = (MYFLT *)malloc(nPointsCount * sizeof(MYFLT));
pnUseFlag = (int *)malloc(nPointsCount * sizeof(int));
diff --git a/src/engine/servermodule.c b/src/engine/servermodule.c
index 0f9f68c..476297d 100644
--- a/src/engine/servermodule.c
+++ b/src/engine/servermodule.c
@@ -51,6 +51,14 @@ 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; };
+int jack_midi_input_port_set_name(Server *self) { return 0; };
+int jack_midi_output_port_set_name(Server *self) { return 0; };
+void jack_noteout(Server *self, int pit, int vel, int chan, long timestamp) {};
+void jack_afterout(Server *self, int pit, int vel, int chan, long timestamp) {};
+void jack_ctlout(Server *self, int ctlnum, int value, int chan, long timestamp) {};
+void jack_programout(Server *self, int value, int chan, long timestamp) {};
+void jack_pressout(Server *self, int value, int chan, long timestamp) {};
+void jack_bendout(Server *self, int value, int chan, long timestamp) {};
#endif
@@ -526,9 +534,12 @@ Server_traverse(Server *self, visitproc visit, void *arg)
Py_VISIT(self->TIME);
if (self->CALLBACK != NULL)
Py_VISIT(self->CALLBACK);
- Py_VISIT(self->streams);
+ if (PyList_Size(self->streams) > 0)
+ Py_VISIT(self->streams);
Py_VISIT(self->jackAutoConnectInputPorts);
Py_VISIT(self->jackAutoConnectOutputPorts);
+ Py_VISIT(self->jackAutoConnectMidiInputPort);
+ Py_VISIT(self->jackAutoConnectMidiOutputPort);
return 0;
}
@@ -539,9 +550,12 @@ Server_clear(Server *self)
Py_CLEAR(self->TIME);
if (self->CALLBACK != NULL)
Py_CLEAR(self->CALLBACK);
- Py_CLEAR(self->streams);
+ if (PyList_Size(self->streams) > 0)
+ Py_CLEAR(self->streams);
Py_CLEAR(self->jackAutoConnectInputPorts);
Py_CLEAR(self->jackAutoConnectOutputPorts);
+ Py_CLEAR(self->jackAutoConnectMidiInputPort);
+ Py_CLEAR(self->jackAutoConnectMidiOutputPort);
return 0;
}
@@ -570,11 +584,12 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
int bufferSize = 256;
int duplex = 0;
char *audioType = "portaudio";
+ char *midiType = "portmidi";
char *serverName = "pyo";
- static char *kwlist[] = {"sr", "nchnls", "buffersize", "duplex", "audio", "jackname", "ichnls", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|diiissi", kwlist,
- &samplingRate, &nchnls, &bufferSize, &duplex, &audioType, &serverName, &ichnls)) {
+ static char *kwlist[] = {"sr", "nchnls", "buffersize", "duplex", "audio", "jackname", "ichnls", "midi", NULL};
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|diiissis", kwlist,
+ &samplingRate, &nchnls, &bufferSize, &duplex, &audioType, &serverName, &ichnls, &midiType)) {
Py_INCREF(Py_False);
return Py_False;
}
@@ -600,8 +615,11 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->jackautoout = 1;
self->jackAutoConnectInputPorts = PyList_New(0);
self->jackAutoConnectOutputPorts = PyList_New(0);
+ self->jackAutoConnectMidiInputPort = PyList_New(0);
+ self->jackAutoConnectMidiOutputPort = PyList_New(0);
self->isJackTransportSlave = 0;
self->jack_transport_state = 0;
+ self->withJackMidi = 0;
self->samplingRate = 44100.0;
self->nchnls = 2;
self->ichnls = 2;
@@ -622,7 +640,7 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->allowMMMapper = 0; // Disable Microsoft MIDI Mapper by default.
self->midi_time_offset = 0;
self->amp = self->resetAmp = 1.;
- self->currentAmp = self->lastAmp = 1.; // If set to 0, there is a 5ms fadein at server start.
+ self->currentAmp = self->lastAmp = 0.; // If set to 0, there is a 5ms fadein at server start.
self->withGUI = 0;
self->withTIME = 0;
self->verbosity = 7;
@@ -644,14 +662,14 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static int
Server_init(Server *self, PyObject *args, PyObject *kwds)
{
- static char *kwlist[] = {"sr", "nchnls", "buffersize", "duplex", "audio", "jackname", "ichnls", NULL};
+ static char *kwlist[] = {"sr", "nchnls", "buffersize", "duplex", "audio", "jackname", "ichnls", "midi", NULL};
char *audioType = "portaudio";
char *midiType = "portmidi";
char *serverName = "pyo";
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|diiissi", kwlist,
- &self->samplingRate, &self->nchnls, &self->bufferSize, &self->duplex, &audioType, &serverName, &self->ichnls))
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|diiissis", kwlist,
+ &self->samplingRate, &self->nchnls, &self->bufferSize, &self->duplex, &audioType, &serverName, &self->ichnls, &midiType))
return -1;
if (strcmp(audioType, "jack") == 0) {
@@ -677,9 +695,14 @@ Server_init(Server *self, PyObject *args, PyObject *kwds)
self->audio_be_type = PyoPortaudio;
}
+ self->withJackMidi = 0;
if (strcmp(midiType, "portmidi") == 0 || strcmp(midiType, "pm") == 0 ) {
self->midi_be_type = PyoPortmidi;
}
+ else if (strcmp(midiType, "jack") == 0) {
+ self->midi_be_type = PyoJackMidi;
+ self->withJackMidi = 1;
+ }
else {
Server_warning(self, "Unknown midi type. Using Portmidi\n");
self->midi_be_type = PyoPortmidi;
@@ -940,6 +963,40 @@ Server_setJackAutoConnectOutputPorts(Server *self, PyObject *arg)
}
static PyObject *
+Server_setJackAutoConnectMidiInputPort(Server *self, PyObject *arg)
+{
+ PyObject *tmp;
+
+ if (arg != NULL) {
+ if (PyList_Check(arg)) {
+ tmp = arg;
+ Py_XDECREF(self->jackAutoConnectMidiInputPort);
+ Py_INCREF(tmp);
+ self->jackAutoConnectMidiInputPort = tmp;
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+Server_setJackAutoConnectMidiOutputPort(Server *self, PyObject *arg)
+{
+ PyObject *tmp;
+
+ if (arg != NULL) {
+ if (PyList_Check(arg)) {
+ tmp = arg;
+ Py_XDECREF(self->jackAutoConnectMidiOutputPort);
+ Py_INCREF(tmp);
+ self->jackAutoConnectMidiOutputPort = tmp;
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
Server_setJackInputPortNames(Server *self, PyObject *arg)
{
PyObject *tmp;
@@ -978,6 +1035,44 @@ Server_setJackOutputPortNames(Server *self, PyObject *arg)
}
static PyObject *
+Server_setJackMidiInputPortName(Server *self, PyObject *arg)
+{
+ PyObject *tmp;
+
+ if (arg != NULL) {
+ if (PY_STRING_CHECK(arg)) {
+ tmp = arg;
+ Py_XDECREF(self->jackMidiInputPortName);
+ Py_INCREF(tmp);
+ self->jackMidiInputPortName = tmp;
+
+ jack_midi_input_port_set_name(self);
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+Server_setJackMidiOutputPortName(Server *self, PyObject *arg)
+{
+ PyObject *tmp;
+
+ if (arg != NULL) {
+ if (PY_STRING_CHECK(arg)) {
+ tmp = arg;
+ Py_XDECREF(self->jackMidiOutputPortName);
+ Py_INCREF(tmp);
+ self->jackMidiOutputPortName = tmp;
+
+ jack_midi_output_port_set_name(self);
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
Server_setIsJackTransportSlave(Server *self, PyObject *arg)
{
if (self->server_booted) {
@@ -1307,6 +1402,12 @@ Server_boot(Server *self, PyObject *arg)
Server_error(self, "Pyo built without Portmidi support\n");
}
break;
+ case PyoJackMidi:
+ /* Initialized inside the jack audio backend. */
+ if (self->audio_be_type != PyoJack) {
+ Server_error(self, "To use jack midi, you must also use jack as the audio backend.\n");
+ }
+ break;
}
}
@@ -1416,10 +1517,10 @@ Server_stop(Server *self)
static PyObject *
Server_recordOptions(Server *self, PyObject *args, PyObject *kwds)
{
- Py_ssize_t psize;
+ //Py_ssize_t psize;
static char *kwlist[] = {"dur", "filename", "fileformat", "sampletype", "quality", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "d|s#iid", kwlist, &self->recdur, &self->recpath, &psize, &self->recformat, &self->rectype, &self->recquality)) {
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "d|siid", kwlist, &self->recdur, &self->recpath, &self->recformat, &self->rectype, &self->recquality)) {
return PyInt_FromLong(-1);
}
@@ -1429,12 +1530,12 @@ Server_recordOptions(Server *self, PyObject *args, PyObject *kwds)
static PyObject *
Server_start_rec(Server *self, PyObject *args, PyObject *kwds)
{
- Py_ssize_t psize;
+ //Py_ssize_t psize;
char *filename=NULL;
static char *kwlist[] = {"filename", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s#", kwlist, &filename, &psize)) {
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &filename)) {
return PyInt_FromLong(-1);
}
Server_start_rec_internal(self, filename);
@@ -1567,8 +1668,7 @@ Server_removeStream(Server *self, int id)
int i, sid;
Stream *stream_tmp;
PyGILState_STATE s = PyGILState_Ensure();
- //if (PyObject_HasAttrString((PyObject *)self, "streams")) {
- if (PySequence_Size(self->streams) != -1) {
+ if (my_server[self->thisServerID] != NULL && PySequence_Size(self->streams) != -1) {
for (i=0; i<self->stream_count; i++) {
stream_tmp = (Stream *)PyList_GetItem(self->streams, i);
if (stream_tmp != NULL) {
@@ -1627,8 +1727,12 @@ Server_changeStreamPosition(Server *self, PyObject *args)
void pyoGetMidiEvents(Server *self) {
switch (self->midi_be_type) {
case PyoPortmidi:
- portmidiGetEvents(self);
+ if (self->withPortMidi == 1) {
+ portmidiGetEvents(self);
+ }
break;
+ case PyoJackMidi:
+ /* Handled inside jack audio callback! */
default:
break;
}
@@ -1643,14 +1747,17 @@ Server_noteout(Server *self, PyObject *args)
if (! PyArg_ParseTuple(args, "iiil", &pit, &vel, &chan, ×tamp))
return PyInt_FromLong(-1);
- if (self->withPortMidiOut) {
- switch (self->midi_be_type) {
- case PyoPortmidi:
+ switch (self->midi_be_type) {
+ case PyoPortmidi:
+ if (self->withPortMidiOut) {
pm_noteout(self, pit, vel, chan, timestamp);
- break;
- default:
- break;
- }
+ }
+ break;
+ case PyoJackMidi:
+ jack_noteout(self, pit, vel, chan, timestamp);
+ break;
+ default:
+ break;
}
Py_RETURN_NONE;
}
@@ -1664,14 +1771,17 @@ Server_afterout(Server *self, PyObject *args)
if (! PyArg_ParseTuple(args, "iiil", &pit, &vel, &chan, ×tamp))
return PyInt_FromLong(-1);
- if (self->withPortMidiOut) {
- switch (self->midi_be_type) {
- case PyoPortmidi:
+ switch (self->midi_be_type) {
+ case PyoPortmidi:
+ if (self->withPortMidiOut) {
pm_afterout(self, pit, vel, chan, timestamp);
- break;
- default:
- break;
- }
+ }
+ break;
+ case PyoJackMidi:
+ jack_afterout(self, pit, vel, chan, timestamp);
+ break;
+ default:
+ break;
}
Py_RETURN_NONE;
}
@@ -1685,14 +1795,17 @@ Server_ctlout(Server *self, PyObject *args)
if (! PyArg_ParseTuple(args, "iiil", &ctlnum, &value, &chan, ×tamp))
return PyInt_FromLong(-1);
- if (self->withPortMidiOut) {
- switch (self->midi_be_type) {
- case PyoPortmidi:
+ switch (self->midi_be_type) {
+ case PyoPortmidi:
+ if (self->withPortMidiOut) {
pm_ctlout(self, ctlnum, value, chan, timestamp);
- break;
- default:
- break;
- }
+ }
+ break;
+ case PyoJackMidi:
+ jack_ctlout(self, ctlnum, value, chan, timestamp);
+ break;
+ default:
+ break;
}
Py_RETURN_NONE;
}
@@ -1706,14 +1819,17 @@ Server_programout(Server *self, PyObject *args)
if (! PyArg_ParseTuple(args, "iil", &value, &chan, ×tamp))
return PyInt_FromLong(-1);
- if (self->withPortMidiOut) {
- switch (self->midi_be_type) {
- case PyoPortmidi:
+ switch (self->midi_be_type) {
+ case PyoPortmidi:
+ if (self->withPortMidiOut) {
pm_programout(self, value, chan, timestamp);
- break;
- default:
- break;
- }
+ }
+ break;
+ case PyoJackMidi:
+ jack_programout(self, value, chan, timestamp);
+ break;
+ default:
+ break;
}
Py_RETURN_NONE;
}
@@ -1727,14 +1843,17 @@ Server_pressout(Server *self, PyObject *args)
if (! PyArg_ParseTuple(args, "iil", &value, &chan, ×tamp))
return PyInt_FromLong(-1);
- if (self->withPortMidiOut) {
- switch (self->midi_be_type) {
- case PyoPortmidi:
+ switch (self->midi_be_type) {
+ case PyoPortmidi:
+ if (self->withPortMidiOut) {
pm_pressout(self, value, chan, timestamp);
- break;
- default:
- break;
- }
+ }
+ break;
+ case PyoJackMidi:
+ jack_pressout(self, value, chan, timestamp);
+ break;
+ default:
+ break;
}
Py_RETURN_NONE;
}
@@ -1748,14 +1867,17 @@ Server_bendout(Server *self, PyObject *args)
if (! PyArg_ParseTuple(args, "iil", &value, &chan, ×tamp))
return PyInt_FromLong(-1);
- if (self->withPortMidiOut) {
- switch (self->midi_be_type) {
- case PyoPortmidi:
+ switch (self->midi_be_type) {
+ case PyoPortmidi:
+ if (self->withPortMidiOut) {
pm_bendout(self, value, chan, timestamp);
- break;
- default:
- break;
- }
+ }
+ break;
+ case PyoJackMidi:
+ jack_bendout(self, value, chan, timestamp);
+ break;
+ default:
+ break;
}
Py_RETURN_NONE;
}
@@ -2049,8 +2171,12 @@ 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."},
+ {"setJackAutoConnectMidiInputPort", (PyCFunction)Server_setJackAutoConnectMidiInputPort, METH_O, "Sets a list of ports to auto-connect midi inputs when using JackMidi."},
+ {"setJackAutoConnectMidiOutputPort", (PyCFunction)Server_setJackAutoConnectMidiOutputPort, METH_O, "Sets a list of ports to auto-connect midi outputs when using JackMidi."},
{"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."},
+ {"setJackMidiInputPortName", (PyCFunction)Server_setJackMidiInputPortName, METH_O, "Sets the short name of midi input port for jack server."},
+ {"setJackMidiOutputPortName", (PyCFunction)Server_setJackMidiOutputPortName, METH_O, "Sets the short name of midi output port 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."},
diff --git a/src/objects/granulatormodule.c b/src/objects/granulatormodule.c
index 78db68b..d1ab54f 100644
--- a/src/objects/granulatormodule.c
+++ b/src/objects/granulatormodule.c
@@ -50,6 +50,7 @@ typedef struct {
MYFLT *gsize;
MYFLT *gphase;
MYFLT *lastppos;
+ MYFLT srScale;
int modebuffer[5];
} Granulator;
@@ -89,7 +90,7 @@ Granulator_transform_iii(Granulator *self) {
if (ppos < self->lastppos[j]) {
self->startPos[j] = pos;
- self->gsize[j] = dur * self->sr;
+ self->gsize[j] = dur * self->sr * self->srScale;
}
self->lastppos[j] = ppos;
@@ -152,7 +153,7 @@ Granulator_transform_aii(Granulator *self) {
if (ppos < self->lastppos[j]) {
self->startPos[j] = pos;
- self->gsize[j] = dur * self->sr;
+ self->gsize[j] = dur * self->sr * self->srScale;
}
self->lastppos[j] = ppos;
@@ -195,7 +196,7 @@ Granulator_transform_iai(Granulator *self) {
inc = pit * (1.0 / self->basedur) / self->sr;
- MYFLT gsize = dur * self->sr;
+ MYFLT gsize = dur * self->sr * self->srScale;
for (j=0; j<self->ngrains; j++) {
self->gsize[j] = gsize;
@@ -282,7 +283,7 @@ Granulator_transform_aai(Granulator *self) {
if (ppos < self->lastppos[j]) {
self->startPos[j] = pos[i];
- self->gsize[j] = dur * self->sr;
+ self->gsize[j] = dur * self->sr * self->srScale;
}
self->lastppos[j] = ppos;
@@ -344,7 +345,7 @@ Granulator_transform_iia(Granulator *self) {
if (ppos < self->lastppos[j]) {
self->startPos[j] = pos;
- self->gsize[j] = dur[i] * self->sr;
+ self->gsize[j] = dur[i] * self->sr * self->srScale;
}
self->lastppos[j] = ppos;
@@ -407,7 +408,7 @@ Granulator_transform_aia(Granulator *self) {
if (ppos < self->lastppos[j]) {
self->startPos[j] = pos;
- self->gsize[j] = dur[i] * self->sr;
+ self->gsize[j] = dur[i] * self->sr * self->srScale;
}
self->lastppos[j] = ppos;
@@ -469,7 +470,7 @@ Granulator_transform_iaa(Granulator *self) {
if (ppos < self->lastppos[j]) {
self->startPos[j] = pos[i];
- self->gsize[j] = dur[i] * self->sr;
+ self->gsize[j] = dur[i] * self->sr * self->srScale;
}
self->lastppos[j] = ppos;
@@ -532,7 +533,7 @@ Granulator_transform_aaa(Granulator *self) {
if (ppos < self->lastppos[j]) {
self->startPos[j] = pos[i];
- self->gsize[j] = dur[i] * self->sr;
+ self->gsize[j] = dur[i] * self->sr * self->srScale;
}
self->lastppos[j] = ppos;
@@ -696,6 +697,7 @@ Granulator_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->ngrains = 8;
self->basedur = 0.1;
self->pointerPos = 1.0;
+ self->srScale = 1.0;
self->modebuffer[0] = 0;
self->modebuffer[1] = 0;
self->modebuffer[2] = 0;
@@ -717,6 +719,7 @@ Granulator_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}
Py_XDECREF(self->table);
self->table = PyObject_CallMethod((PyObject *)tabletmp, "getTableStream", "");
+ self->srScale = TableStream_getSamplingRate(self->table) / self->sr;
if ( PyObject_HasAttrString((PyObject *)envtmp, "getTableStream") == 0 ) {
PyErr_SetString(PyExc_TypeError, "\"env\" argument of Granulator must be a PyoTableObject.\n");
diff --git a/src/objects/midimodule.c b/src/objects/midimodule.c
index be090eb..1053726 100644
--- a/src/objects/midimodule.c
+++ b/src/objects/midimodule.c
@@ -426,19 +426,24 @@ PyTypeObject CtlScan2Type = {
int
getPosToWrite(long timestamp, Server *server, double sr, int bufsize)
{
- int offset = 0;
- long realtimestamp, elapsed, ms;
- realtimestamp = timestamp - Server_getMidiTimeOffset(server);
- if (realtimestamp < 0)
- return 0;
- elapsed = (long)(Server_getElapsedTime(server) / sr * 1000);
- ms = realtimestamp - (elapsed - (long)(bufsize / sr * 1000));
- offset = (int)(ms * 0.001 * sr);
- if (offset < 0)
- offset = 0;
- else if (offset >= bufsize)
- offset = bufsize - 1;
- return offset;
+ /* With JackMidi, the timestamp is already a sample offset inside the buffer size. */
+ if (server->withJackMidi) {
+ return (int)timestamp;
+ } else {
+ int offset = 0;
+ long realtimestamp, elapsed, ms;
+ realtimestamp = timestamp - Server_getMidiTimeOffset(server);
+ if (realtimestamp < 0)
+ return 0;
+ elapsed = (long)(Server_getElapsedTime(server) / sr * 1000);
+ ms = realtimestamp - (elapsed - (long)(bufsize / sr * 1000));
+ offset = (int)(ms * 0.001 * sr);
+ if (offset < 0)
+ offset = 0;
+ else if (offset >= bufsize)
+ offset = bufsize - 1;
+ return offset;
+ }
}
typedef struct {
diff --git a/src/objects/panmodule.c b/src/objects/panmodule.c
index 2880276..05d3f26 100644
--- a/src/objects/panmodule.c
+++ b/src/objects/panmodule.c
@@ -1990,11 +1990,11 @@ static void
VoiceManager_dealloc(VoiceManager* self)
{
pyo_DEALLOC
+ VoiceManager_clear(self);
if (self->voices != NULL) {
free(self->voices);
free(self->trigger_streams);
}
- VoiceManager_clear(self);
Py_TYPE(self)->tp_free((PyObject*)self);
}
diff --git a/src/objects/phasevocmodule.c b/src/objects/phasevocmodule.c
index 73b9d58..9396098 100644
--- a/src/objects/phasevocmodule.c
+++ b/src/objects/phasevocmodule.c
@@ -38,6 +38,7 @@ isPowerOfTwo(int x) {
typedef struct {
pyo_audio_HEAD
+ PyObject *callback;
PyObject *input;
Stream *input_stream;
PVStream *pv_stream;
@@ -110,6 +111,30 @@ PVAnal_realloc_memories(PVAnal *self) {
}
static void
+PVAnal_data_callback(PVAnal *self) {
+ int i;
+ PyObject *tuple, *result, *magnitudes, *frequencies;
+
+ magnitudes = PyList_New(self->hsize);
+ frequencies = PyList_New(self->hsize);
+ for (i=0; i<self->hsize; i++) {
+ PyList_SET_ITEM(magnitudes, i, PyFloat_FromDouble(self->magn[self->overcount][i]));
+ PyList_SET_ITEM(frequencies, i, PyFloat_FromDouble(self->freq[self->overcount][i]));
+ }
+
+ tuple = PyTuple_New(2);
+ PyTuple_SET_ITEM(tuple, 0, magnitudes);
+ PyTuple_SET_ITEM(tuple, 1, frequencies);
+ result = PyObject_Call(self->callback, tuple, NULL);
+ if (result == NULL) {
+ PyErr_Print();
+ }
+
+ Py_DECREF(magnitudes);
+ Py_DECREF(frequencies);
+}
+
+static void
PVAnal_process(PVAnal *self) {
int i, k, mod;
MYFLT real, imag, mag, phase, tmp;
@@ -145,6 +170,9 @@ PVAnal_process(PVAnal *self) {
self->magn[self->overcount][k] = mag;
self->freq[self->overcount][k] = (tmp + k * self->scale) * self->factor;
}
+ if (self->callback != Py_None) {
+ PVAnal_data_callback(self);
+ }
for (k=0; k<self->inputLatency; k++) {
self->input_buffer[k] = self->input_buffer[k + self->hopsize];
}
@@ -174,6 +202,9 @@ PVAnal_traverse(PVAnal *self, visitproc visit, void *arg)
Py_VISIT(self->input);
Py_VISIT(self->input_stream);
Py_VISIT(self->pv_stream);
+ if (self->callback != Py_None) {
+ Py_VISIT(self->callback);
+ }
return 0;
}
@@ -184,6 +215,9 @@ PVAnal_clear(PVAnal *self)
Py_CLEAR(self->input);
Py_CLEAR(self->input_stream);
Py_CLEAR(self->pv_stream);
+ if (self->callback != Py_None) {
+ Py_CLEAR(self->callback);
+ }
return 0;
}
@@ -218,10 +252,12 @@ static PyObject *
PVAnal_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
int i, k;
- PyObject *inputtmp, *input_streamtmp;
+ PyObject *inputtmp, *input_streamtmp, *callbacktmp=NULL;
PVAnal *self;
self = (PVAnal *)type->tp_alloc(type, 0);
+ Py_INCREF(Py_None);
+ self->callback = Py_None;
self->size = 1024;
self->olaps = 4;
self->wintype = 2;
@@ -229,13 +265,17 @@ PVAnal_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
Stream_setFunctionPtr(self->stream, PVAnal_compute_next_data_frame);
self->mode_func_ptr = PVAnal_setProcMode;
- static char *kwlist[] = {"input", "size", "olaps", "wintype", NULL};
+ static char *kwlist[] = {"input", "size", "olaps", "wintype", "callback", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|iii", kwlist, &inputtmp, &self->size, &self->olaps, &self->wintype))
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|iiiO", kwlist, &inputtmp, &self->size, &self->olaps, &self->wintype, &callbacktmp))
Py_RETURN_NONE;
INIT_INPUT_STREAM
+ if (callbacktmp) {
+ PyObject_CallMethod((PyObject *)self, "setCallback", "O", callbacktmp);
+ }
+
PyObject_CallMethod(self->server, "addStream", "O", self->stream);
MAKE_NEW_PV_STREAM(self->pv_stream, &PVStreamType, NULL);
@@ -316,6 +356,26 @@ PVAnal_setWinType(PVAnal *self, PyObject *arg)
return Py_None;
}
+static PyObject *
+PVAnal_setCallback(PVAnal *self, PyObject *arg)
+{
+ PyObject *tmp;
+
+ if (! PyCallable_Check(arg) && arg != Py_None) {
+ PyErr_SetString(PyExc_TypeError, "The callback attribute must be callable.");
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ tmp = arg;
+ Py_XDECREF(self->callback);
+ Py_INCREF(tmp);
+ self->callback = tmp;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
static PyMemberDef PVAnal_members[] = {
{"server", T_OBJECT_EX, offsetof(PVAnal, server), 0, "Pyo server."},
{"stream", T_OBJECT_EX, offsetof(PVAnal, stream), 0, "Stream object."},
@@ -333,6 +393,7 @@ static PyMethodDef PVAnal_methods[] = {
{"setSize", (PyCFunction)PVAnal_setSize, METH_O, "Sets a new FFT size."},
{"setOverlaps", (PyCFunction)PVAnal_setOverlaps, METH_O, "Sets a new number of overlaps."},
{"setWinType", (PyCFunction)PVAnal_setWinType, METH_O, "Sets a new window type."},
+{"setCallback", (PyCFunction)PVAnal_setCallback, METH_O, "Sets a callback function."},
{NULL} /* Sentinel */
};
diff --git a/tests/encoding_test.py b/tests/encoding_test.py
index e3629d7..1baed82 100644
--- a/tests/encoding_test.py
+++ b/tests/encoding_test.py
@@ -15,46 +15,47 @@ print("Locale preferred encoding: ", locale.getpreferredencoding())
print("Locale default encoding: ", locale.getdefaultlocale())
s = Server().boot()
+s.verbosity = 15
### Need a python layer to encode the path in python 3.
# Python 3
-#p = 'bébêtte/noise.aif'.encode(sys.getfilesystemencoding())
+#p = 'bébêtte/noise.aif'.encode(sys.getfilesystemencoding())
# Python 2
-p = 'bébêtte/noise.aif'
+p = 'bébêtte/noise.aif'
########## SNDINFO ###############
info = sndinfo(p)
print("sndinfo output:\n", info)
####### SAVEFILE ##############
-sr, dur, chnls, path = 44100, 5, 2, os.path.join("bébêtte", 'savefile.aif')
+sr, dur, chnls, path = 44100, 5, 2, os.path.join("bébêtte", 'savefile.aif')
samples = [[uniform(-0.5,0.5) for i in range(sr*dur)] for i in range(chnls)]
savefile(samples=samples, path=path, sr=sr, channels=chnls, fileformat=1, sampletype=1)
-print("Savefile record:", os.path.isfile(os.path.join("bébêtte", 'savefile.aif')))
+print("Savefile record:", os.path.isfile(os.path.join("bébêtte", 'savefile.aif')))
####### SAVEFILEFROMTABLE #########
home = os.path.expanduser('~')
path1 = SNDS_PATH + '/transparent.aif'
-path2 = os.path.join("bébêtte", 'savefileFromTable.aif')
+path2 = os.path.join("bébêtte", 'savefileFromTable.aif')
t = SndTable(path1)
savefileFromTable(table=t, path=path2, fileformat=1, sampletype=1)
-print("SavefileFromTable record:", os.path.isfile(os.path.join("bébêtte", 'savefileFromTable.aif')))
+print("SavefileFromTable record:", os.path.isfile(os.path.join("bébêtte", 'savefileFromTable.aif')))
##### UPSAMP/DOWNSAMP ######
# upsample a signal 3 times
-upfile = os.path.join("bébêtte", 'upsamp.aif')
+upfile = os.path.join("bébêtte", 'upsamp.aif')
upsamp(p, upfile, 2, 256)
-print("upsamp record:", os.path.isfile(os.path.join("bébêtte", 'upsamp.aif')))
+print("upsamp record:", os.path.isfile(os.path.join("bébêtte", 'upsamp.aif')))
# downsample the upsampled signal 3 times
-downfile = os.path.join("bébêtte", 'downsamp.aif')
+downfile = os.path.join("bébêtte", 'downsamp.aif')
downsamp(upfile, downfile, 3, 256)
-print("downsamp record:", os.path.isfile(os.path.join("bébêtte", 'downsamp.aif')))
+print("downsamp record:", os.path.isfile(os.path.join("bébêtte", 'downsamp.aif')))
######### SfPlayer ###########
sf1 = SfPlayer(p, mul=0.1)
sf1.setPath(downfile)
-p2 = os.path.join("bébêtte", 'transparent.aif')
+p2 = os.path.join("bébêtte", 'transparent.aif')
######### SfMarkerShuffler ###########
sf2 = SfMarkerShuffler(p2, mul=0.3)
@@ -71,11 +72,11 @@ tab1.insert(p2, 0.1, 0.1)
tabplay = Osc(tab1, [tab1.getRate(), tab1.getRate() * 1.01], mul=0.5)
######### CvlVerb impulse path ###########
-impl = os.path.join("bébêtte", 'IRMediumHallStereo.wav')
+impl = os.path.join("bébêtte", 'IRMediumHallStereo.wav')
cv = CvlVerb(tabplay, impl, size=1024, bal=0.7).out()
######### Record ###########
-recfile = os.path.join("bébêtte", 'recfile.wav')
+recfile = os.path.join("bébêtte", 'recfile.wav')
rec = Record(cv, recfile, chnls=2, fileformat=0, sampletype=0, buffering=4, quality=0.40)
recplay = None
def reccallback():
@@ -88,10 +89,10 @@ def reccallback():
after = CallAfter(reccallback, 4)
######### Server ###########
-servrecfile = os.path.join("bébêtte", 'servrecfile.wav')
-#s.recordOptions(dur=-1, filename=servrecfile)
-servrecfile = os.path.join("bébêtte", 'servrecfile2.wav')
-s.recstart(servrecfile)
+servrecfile = os.path.join("bébêtte", 'servrecfile.wav')
+s.recordOptions(dur=-1, filename=servrecfile)
+#servrecfile = os.path.join("bébêtte", 'servrecfile2.wav')
+#s.recstart(servrecfile)
s.gui(locals(), exit=False)
@@ -102,13 +103,13 @@ def delfile(f):
# On windows, we should delete Sf* objects before deleting the audio files.
del sf1
del recplay
-delfile(os.path.join("bébêtte", 'savefile.aif'))
-delfile(os.path.join("bébêtte", 'savefileFromTable.aif'))
-delfile(os.path.join("bébêtte", 'upsamp.aif'))
-delfile(os.path.join("bébêtte", 'downsamp.aif'))
-delfile(os.path.join("bébêtte", 'recfile.wav'))
-delfile(os.path.join("bébêtte", 'servrecfile.wav'))
-delfile(os.path.join("bébêtte", 'servrecfile2.wav'))
+delfile(os.path.join("bébêtte", 'savefile.aif'))
+delfile(os.path.join("bébêtte", 'savefileFromTable.aif'))
+delfile(os.path.join("bébêtte", 'upsamp.aif'))
+delfile(os.path.join("bébêtte", 'downsamp.aif'))
+delfile(os.path.join("bébêtte", 'recfile.wav'))
+delfile(os.path.join("bébêtte", 'servrecfile.wav'))
+delfile(os.path.join("bébêtte", 'servrecfile2.wav'))
"""
1 - Adapt encoding line for E-Pyo tempfile. **done**
diff --git a/utils/E-Pyo.py b/utils/E-Pyo.py
index d545fbc..ecb4ce3 100755
--- a/utils/E-Pyo.py
+++ b/utils/E-Pyo.py
@@ -26,7 +26,7 @@ import wx.lib.scrolledpanel as scrolled
import wx.lib.dialogs
import wx.stc as stc
import wx.lib.agw.flatnotebook as FNB
-from pyo import * # what do we really need? OBJECTS_TREE, PYO_VERSION
+from pyo import * # TODO: what do we really need? OBJECTS_TREE, PYO_VERSION
from PyoDoc import ManualFrame
if sys.version_info[0] < 3:
@@ -34,9 +34,11 @@ if sys.version_info[0] < 3:
unicode_t = unicode
reload(sys)
sys.setdefaultencoding("utf-8")
+ exec_string = "python"
else:
from io import StringIO as StringIO
unicode_t = str
+ exec_string = "python3"
if "phoenix" in wx.version():
from wx.adv import AboutDialogInfo, AboutBox
@@ -141,7 +143,8 @@ if not os.path.isfile(PREFERENCES_PATH):
f.write("epyo_prefs = {}")
epyo_prefs = {}
-with open(PREFERENCES_PATH, "r") as f:
+# TODO: Should handle utf-8 encoding!
+with codecs.open(PREFERENCES_PATH, "r", encoding="utf-8") as f:
text = f.read()
spos = text.find("=")
dictext = text[spos+1:]
@@ -344,11 +347,11 @@ BACKGROUND_SERVER_DEFAULT_ARGS = 'sr=44100, nchnls=2, buffersize=256, duplex=1,
BACKGROUND_SERVER_ARGS = PREFERENCES.get("background_server_args", BACKGROUND_SERVER_DEFAULT_ARGS)
################## TEMPLATES ##################
-HEADER_TEMPLATE = """#!/usr/bin/env python
+HEADER_TEMPLATE = """#!/usr/bin/env %s
# encoding: utf-8
-"""
+""" % exec_string
-PYO_TEMPLATE = """#!/usr/bin/env python
+PYO_TEMPLATE = """#!/usr/bin/env %s
# encoding: utf-8
from pyo import *
@@ -358,7 +361,7 @@ s = Server(sr=44100, nchnls=2, buffersize=512, duplex=1).boot()
s.gui(locals())
-"""
+""" % exec_string
CECILIA5_TEMPLATE = '''class Module(BaseModule):
"""
@@ -447,7 +450,7 @@ mainFrame.Show()
app.MainLoop()
'''
-WXPYTHON_TEMPLATE = '''#!/usr/bin/env python
+WXPYTHON_TEMPLATE = '''#!/usr/bin/env %s
# encoding: utf-8
import wx
@@ -463,7 +466,7 @@ if __name__ == "__main__":
mainFrame = MyFrame(None, title='Simple App', pos=(100,100), size=(500,300))
mainFrame.Show()
app.MainLoop()
-'''
+''' % exec_string
RADIOPYO_TEMPLATE = '''#!/usr/bin/env python
# encoding: utf-8
@@ -538,7 +541,7 @@ templateid = 91
template_files = sorted([f for f in os.listdir(TEMPLATE_PATH) if f.endswith(".py")])
for f in template_files:
try:
- with open(os.path.join(TEMPLATE_PATH, f)) as ftemp:
+ with codecs.open(os.path.join(TEMPLATE_PATH, f), "r", encoding="utf-8") as ftemp:
ftext = ftemp.read()
TEMPLATE_NAMES[templateid] = f.replace(".py", "")
TEMPLATE_DICT[templateid] = ftext
@@ -549,8 +552,13 @@ for f in template_files:
################## BUILTIN KEYWORDS COMPLETION ##################
FROM_COMP = ''' `module` import `*`
'''
-EXEC_COMP = ''' "`expression`" in `self.locals`
+if sys.version_info[0] < 3:
+ EXEC_COMP = ''' "`expression`" in `self.locals`
+'''
+else:
+ EXEC_COMP = '''("`expression`", `globals()`, `locals()`)
'''
+
RAISE_COMP = ''' Exception("`An exception occurred...`")
'''
TRY_COMP = ''':
@@ -585,7 +593,6 @@ WHILE_COMP = """ `i` `>` `0`:
ASSERT_COMP = ''' `expression` `>` `0`, "`expression should be positive`"
'''
-# TODO: Python 3 syntax (if possible, compatible with python 2)
BUILTINS_DICT = {"from": FROM_COMP, "try": TRY_COMP, "if": IF_COMP,
"def": DEF_COMP, "class": CLASS_COMP, "for": FOR_COMP,
"while": WHILE_COMP, "exec": EXEC_COMP, "raise": RAISE_COMP,
@@ -862,8 +869,8 @@ elif wx.Platform == '__WXMAC__':
FONT_SIZE2 = 9
DEFAULT_FONT_FACE = 'Monaco'
else:
- FONT_SIZE = 8
- FONT_SIZE2 = 7
+ FONT_SIZE = 11
+ FONT_SIZE2 = 8
DEFAULT_FONT_FACE = 'Monospace'
@@ -886,7 +893,7 @@ STYLES_LABELS = {'default': 'Foreground', 'background': 'Background',
'bracelight': 'Brace Match', 'bracebad': 'Brace Mismatch',
'lineedge': 'Line Edge'}
-with open(PREF_STYLE) as f:
+with codecs.open(PREF_STYLE, "r", encoding="utf-8") as f:
text = f.read()
spos = text.find("=")
dictext = text[spos+1:]
@@ -1014,8 +1021,10 @@ class RunningThread(threading.Thread):
wx.QueueEvent(self.event_receiver, data_event)
while self.proc.poll() == None and not self.terminated:
log = ""
- for line in self.proc.stdout.readline():
- log = log + str(line)
+ line = self.proc.stdout.readline()
+ if line.startswith("ALSA lib"):
+ continue
+ log = log + str(line)
log = log.replace(">>> ", "").replace("... ", "")
data_event = DataEvent({"log": log, "pid": self.pid, "filename": self.filename, "active": True})
wx.QueueEvent(self.event_receiver, data_event)
@@ -1515,7 +1524,7 @@ class ColourEditor(wx.Frame):
global STYLES
stl = event.GetString()
self.cur_style = stl
- with open(os.path.join(ensureNFD(STYLES_PATH), stl)) as f:
+ with codecs.open(os.path.join(ensureNFD(STYLES_PATH), stl), "r", encoding="utf-8") as f:
text = f.read()
spos = text.find("=")
dictext = text[spos+1:]
@@ -2496,7 +2505,7 @@ class MainFrame(wx.Frame):
submenu = wx.Menu(title=cat)
files = [f for f in os.listdir(os.path.join(SNIPPETS_PATH, cat))]
for file in files:
- with open(os.path.join(SNIPPETS_PATH, cat, file), "r") as f:
+ with codecs.open(os.path.join(SNIPPETS_PATH, cat, file), "r", encoding="utf-8") as f:
text = f.read()
spos = text.find("=")
dictext = text[spos+1:]
@@ -2608,7 +2617,7 @@ class MainFrame(wx.Frame):
for i in range(12000, self.ID_FILTERS):
self.filters_menu.Delete(i)
ID_FILTERS = 12000
- with open(FILTERS_FILE, "r") as f:
+ with codecs.open(FILTERS_FILE, "r", encoding="utf-8") as f:
for line in f.readlines():
if line.startswith("def "):
ppos = line.find("(")
@@ -2623,7 +2632,7 @@ class MainFrame(wx.Frame):
self.panel.addPage(FILTERS_FILE)
def applyFilter(self, evt):
- with open(FILTERS_FILE, "r") as f:
+ with codecs.open(FILTERS_FILE, "r", encoding="utf-8") as f:
text = f.read()
functions = {}
exec(text, functions)
@@ -2881,7 +2890,7 @@ class MainFrame(wx.Frame):
def setStyle(self, st, fromMenu=False):
global STYLES
- with open(os.path.join(ensureNFD(STYLES_PATH), st)) as f:
+ with codecs.open(os.path.join(ensureNFD(STYLES_PATH), st), "r", encoding="utf-8") as f:
text = f.read()
spos = text.find("=")
dictext = text[spos+1:]
@@ -3218,7 +3227,7 @@ class MainFrame(wx.Frame):
def runner(self, event):
if self.master_document != None:
- with open(self.master_document, "r") as f:
+ with codecs.open(self.master_document, "r", encoding="utf-8") as f:
text = f.read()
else:
text = self.panel.editor.GetText()
@@ -3511,7 +3520,7 @@ class MainPanel(wx.Panel):
def addPage(self, file, encoding=None):
editor = Editor(self.notebook, -1, size=(0, -1), setTitle=self.SetTitle, getTitle=self.GetTitle)
- label = os.path.split(file)[1].split('.')[0]
+ label = os.path.split(file)[1]
self.notebook.AddPage(editor, label, True)
text = ""
if encoding != None:
@@ -3784,7 +3793,10 @@ class Editor(stc.StyledTextCtrl):
if ext in ["py", "pyw", "c5"]:
self.SetLexer(stc.STC_LEX_PYTHON)
self.SetStyleBits(self.GetStyleBitsNeeded())
- self.SetKeyWords(0, " ".join(keyword.kwlist) + " None True False print ")
+ if sys.version_info[0] < 3:
+ self.SetKeyWords(0, " ".join(keyword.kwlist) + " None True False print ")
+ else:
+ self.SetKeyWords(0, " ".join(keyword.kwlist) + " None True False ")
self.SetKeyWords(1, " ".join(PYO_WORDLIST))
self.StyleSetSpec(stc.STC_P_DEFAULT, buildStyle('default'))
self.StyleSetSpec(stc.STC_P_COMMENTLINE, buildStyle('comment'))
@@ -4986,7 +4998,7 @@ class OutputLogPanel(wx.Panel):
self.toolbar.Realize()
toolbarbox.Add(self.toolbar, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 0)
- tb2 = wx.ToolBar(self, -1, size=(-1,32))
+ tb2 = wx.ToolBar(self, -1)
if PLATFORM == "darwin":
tb2.SetToolBitmapSize(tsize)
tb2.AddSeparator()
@@ -5113,7 +5125,6 @@ class ProjectTree(wx.Panel):
self.selectedItem = None
self.edititem = self.editfolder = self.itempath = self.scope = None
- tsize = (24, 24)
file_add_bmp = catalog['file_add_icon.png'].GetBitmap()
folder_add_bmp = catalog['folder_add_icon.png'].GetBitmap()
close_panel_bmp = catalog['close_panel_icon.png'].GetBitmap()
@@ -5122,8 +5133,7 @@ class ProjectTree(wx.Panel):
self.sizer = wx.BoxSizer(wx.VERTICAL)
toolbarbox = wx.BoxSizer(wx.HORIZONTAL)
- self.toolbar = wx.ToolBar(self, -1, size=(-1,36))
- self.toolbar.SetToolBitmapSize(tsize)
+ self.toolbar = wx.ToolBar(self, -1)
if "phoenix" not in wx.version():
self.toolbar.AddLabelTool(TOOL_ADD_FILE_ID, "Add File",
@@ -5144,8 +5154,7 @@ class ProjectTree(wx.Panel):
self.toolbar.Realize()
toolbarbox.Add(self.toolbar, 1, wx.ALIGN_LEFT | wx.EXPAND, 0)
- tb2 = wx.ToolBar(self, -1, size=(-1,36))
- tb2.SetToolBitmapSize(tsize)
+ tb2 = wx.ToolBar(self, -1)
if "phoenix" not in wx.version():
tb2.AddLabelTool(15, "Close Panel", close_panel_bmp, shortHelp="Close Panel")
@@ -5168,6 +5177,8 @@ class ProjectTree(wx.Panel):
if PLATFORM == 'darwin':
pt = 11
+ elif PLATFORM.startswith("linux"):
+ pt = 10
else:
pt = 8
fnt = wx.Font(pt, wx.ROMAN, wx.NORMAL, wx.NORMAL, False, STYLES['face'])
diff --git a/utils/snippets/Utilities/ChooseAudioDev b/utils/snippets/Utilities/ChooseAudioDev
index 07d267c..5b1a020 100644
--- a/utils/snippets/Utilities/ChooseAudioDev
+++ b/utils/snippets/Utilities/ChooseAudioDev
@@ -1 +1 @@
-snippet = {'shortcut': u'Shift-Alt-7', 'value': u'pa_list_devices()\ndevIn = input("Enter your input device number : ")\ndevOut = input("Enter your output device number : ")\ns = Server()\ns.setInputDevice(devIn)\ns.setOutputDevice(devOut)\ns.boot()\n'}
\ No newline at end of file
+snippet = {'shortcut': u'Shift-Alt-7', 'value': u'pa_list_devices()\ndevIn = int(input("Enter your input device number : "))\ndevOut = int(input("Enter your output device number : "))\ns = Server()\ns.setInputDevice(devIn)\ns.setOutputDevice(devOut)\ns.boot()\n'}
--
python-pyo packaging
More information about the pkg-multimedia-commits
mailing list