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()

Reply via email to