-- oops, the first one was sent as a reply, here's the whole thing as a
new thread.
frustrated by the poor implementation of the jack bindings for python
(pyjack), i wrote my own in native python using ctypes.
the first test client mixed a 440hz sine wave using native python lists,
and the cpu usage was about ~11%.
i reimplemented the sine generator with numerics, and got it down to
~2%.
i believe that considering the overhead of the python implementation,
that result isn't too bad, and maybe allows for more than just
prototyping.
i attached the jack wrapper with the test client contained for those who
are interested. its not entirely wrapped and lacks some functionality.
--
-- leonard "paniq" ritter
-- http://www.mjoo.org
-- http://www.paniq.org
import sys
import ctypes
import _ctypes
from ctypes import c_uint, \
c_int, \
c_float, \
CFUNCTYPE, \
c_char_p, \
POINTER, \
byref, \
c_char, \
c_double, \
c_short, \
memmove
_libjack = ctypes.cdll.LoadLibrary('libjack.so')
JACK_DEFAULT_AUDIO_TYPE = "32 bit float mono audio"
# JackPortFlags
JackPortIsInput = 0x1
JackPortIsOutput = 0x2
JackPortIsPhysical = 0x4
JackPortCanMonitor = 0x8
JackPortIsTerminal = 0x10
# JackOptions
JackNullOption = 0x00
JackNoStartServer = 0x01
JackUseExactName = 0x02
JackServerName = 0x04
JackLoadName = 0x08
JackLoadInit = 0x10
JackOpenOptions = JackServerName|JackNoStartServer|JackUseExactName
JackLoadOptions = JackLoadInit|JackLoadName|JackUseExactName
# JackStatus
JackFailure = 0x01
JackInvalidOption = 0x02
JackNameNotUnique = 0x04
JackServerStarted = 0x08
JackServerFailed = 0x10
JackServerError = 0x20
JackNoSuchClient = 0x40
JackLoadFailure = 0x80
JackInitFailure = 0x100
JackShmFailure = 0x200
JackVersionError = 0x400
def _jack_error_callback(msg):
sys.stderr.write(msg)
jack_error_callback = CFUNCTYPE(None,c_char_p)
jack_error_callback_ptr = jack_error_callback(_jack_error_callback)
_libjack.jack_set_error_function(jack_error_callback_ptr)
_libjack.jack_port_get_buffer.restype = POINTER(c_float)
_libjack.jack_cpu_load.restype = c_float
class PortHandle:
def __init__(self, client, port):
self._client = client
self._port = port
class Port(PortHandle):
def __init__(self, client, port_name, port_type, flags, buffer_size):
PortHandle.__init__(self,client,_libjack.jack_port_register(client, port_name, port_type, flags, buffer_size))
def __del__(self):
print 'passing'
def get_buffer(self,frames):
_libjack.jack_port_get_buffer.restype = POINTER(c_float)
return _libjack.jack_port_get_buffer(self._port, frames).contents
def set_buffer(self,arraydata):
chardata = arraydata.tostring()
size = len(chardata)
_libjack.jack_port_get_buffer.restype = POINTER(c_float)
memmove(_libjack.jack_port_get_buffer(self._port, size/4), chardata, size)
JackShutdownCallback = CFUNCTYPE(None,POINTER(c_uint))
JackProcessCallback = CFUNCTYPE(c_int,c_uint,POINTER(c_uint))
JackThreadInitCallback = CFUNCTYPE(None,POINTER(c_uint))
JackGraphOrderCallback = CFUNCTYPE(c_int,POINTER(c_uint))
JackXRunCallback = CFUNCTYPE(c_int,POINTER(c_uint))
JackBufferSizeCallback = CFUNCTYPE(c_int,c_uint,POINTER(c_uint))
JackSampleRateCallback = CFUNCTYPE(c_int,c_uint,POINTER(c_uint))
JackPortRegistrationCallback = CFUNCTYPE(None,c_uint,c_int,POINTER(c_uint))
JackFreewheelCallback = CFUNCTYPE(None,c_int,POINTER(c_uint))
class Client:
def __init__(self,client_name):
self._client = _libjack.jack_client_new(client_name)
self.ports = []
self._on_shutdown = JackShutdownCallback(self.on_shutdown)
self._process = JackProcessCallback(self.process)
self._thread_init = JackThreadInitCallback(self.thread_init)
self._graph_order = JackGraphOrderCallback(self.graph_order)
self._xrun = JackXRunCallback(self.xrun)
self._buffer_size = JackBufferSizeCallback(self.buffer_size)
self._sample_rate = JackSampleRateCallback(self.sample_rate)
self._port_registration = JackPortRegistrationCallback(self.port_registration)
self._freewheel = JackFreewheelCallback(self.freewheel)
_libjack.jack_on_shutdown(self._client, self._on_shutdown, None)
_libjack.jack_set_process_callback(self._client, self._process, None)
_libjack.jack_set_thread_init_callback(self._client, self._thread_init, None)
_libjack.jack_set_freewheel_callback(self._client, self._freewheel, None)
_libjack.jack_set_buffer_size_callback(self._client, self._buffer_size, None)
_libjack.jack_set_sample_rate_callback(self._client, self._sample_rate, None)
_libjack.jack_set_port_registration_callback(self._client, self._port_registration, None)
_libjack.jack_set_graph_order_callback(self._client, self._graph_order, None)
_libjack.jack_set_xrun_callback(self._client, self._xrun, None)
def on_shutdown(self, arg):
pass
def process(self, nframes, arg):
return -1
def thread_init(self, arg):
pass
def graph_order(self, arg):
return 0
def xrun(self, arg):
return 0
def buffer_size(self, nframes, arg):
return 0
def sample_rate(self, nframes, arg):
return 0
def port_registration(self, port, i, arg):
pass
def freewheel(self, starting, arg):
pass
def __del__(self):
for port in self.ports:
port._client = None
self._ports = None
_libjack.jack_client_close(self._client)
def new_port(self, port_name, port_type = JACK_DEFAULT_AUDIO_TYPE, flags = JackPortIsOutput, buffer_size = 0):
port = Port(self._client, port_name, port_type, flags, buffer_size)
self.ports.append(port)
return port
def new_audio_output(self, port_name):
return self.new_port(port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)
def new_audio_input(self, port_name):
return self.new_port(port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)
#~ char *get_client_name();
#~ bool is_realtime();
#~ virtual void on_shutdown();
#~ virtual int process(jack_nframes_t nframes);
#~ virtual int buffer_size(jack_nframes_t nframes);
#~ virtual void freewheel(int starting);
#~ virtual int graph_order();
#~ virtual void port_registration(jack_port_id_t port, int i);
#~ virtual int sample_rate(jack_nframes_t nframes);
#~ virtual void thread_init();
#~ virtual int xrun();
#~ int set_freewheel(bool onoff);
#~ int set_buffer_size(jack_nframes_t nframes);
#~ int activate();
def activate(self):
return _libjack.jack_activate(self._client)
#~ int deactivate();
def deactivate(self):
return _libjack.jack_deactivate(self._client)
#~ int recompute_total_latencies();
#~ bool is_mine(const jack_port_t *port);
#~ int request_monitor_by_name(const char *port_name, bool onoff);
#~ int connect(const char *source_port, const char *destination_port);
def connect(self,source_port,destination_port):
return _libjack.jack_connect(self._client,source_port,destination_port)
#~ int disconnect(const char *source_port, const char *destination_port);
def disconnect(self,source_port,destination_port):
return _libjack.jack_disconnect(self._client,source_port,destination_port)
#~ const char **get_ports(const char *port_name_pattern, const char *type_name_pattern, unsigned long flags);
#~ jack_port_t *port_by_name (const char *port_name);
def port_by_name(self,port_name):
return _libjack.jack_port_by_name(self._client,port_name)
#~ jack_port_t *port_by_id (jack_port_id_t port_id);
#~ jack_nframes_t get_sample_rate();
def get_sample_rate(self):
return _libjack.jack_get_sample_rate(self._client)
#~ jack_nframes_t get_buffer_size();
def get_buffer_size(self):
return _libjack.jack_get_buffer_size(self._client)
#~ int engine_takeover_timebase();
#~ jack_nframes_t frames_since_cycle_start();
#~ jack_nframes_t frame_time();
#~ jack_nframes_t last_frame_time();
#~ float cpu_load();
def cpu_load(self):
return _libjack.jack_cpu_load(self._client)
#~ pthread_t client_thread_id();
from Numeric import array, resize, arrayrange, sin as usin
from math import sin
class TestClient(Client):
def __init__(self):
import Queue
Client.__init__(self,'Test')
self.in0 = self.new_audio_input('in_0')
self.out0 = self.new_audio_output('out_0')
self.sample_rate = self.get_sample_rate()
self.hz = 2 * 3.14159 * 440.0 / self.sample_rate
self.phase = 0
self.amps = Queue.Queue()
print self.sample_rate
def __del__(self):
self.in0 = None
self.out0 = None
Client.__del__(self)
def native_mix(self,nframes):
res = range(int(nframes))
for i in xrange(nframes):
res[i] = sin((res[i]+self.phase) * self.hz)
self.phase += 1
return array(res,'f')
def mix(self,nframes):
res = usin((arrayrange(float(nframes))+self.phase) * self.hz)
self.phase = (self.phase + int(nframes))
return res.astype('f')
def process(self, nframes, arg):
amp = 0.0
self.out0.set_buffer(self.mix(nframes))
return 0
import psyco
psyco.profile()
def test():
from time import sleep,time
client = TestClient()
client.activate()
client.connect('Test:out_0','alsa_pcm:playback_1')
t = time()
while (time() - t) < 30:
print 'cpu:%.3f%%' % (client.cpu_load())
sleep(1)
client.deactivate()
del client
print 'done.'
if __name__ == '__main__':
test()