aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil S. Matheussen <ksvalast@users.sourceforge.net>2004-01-25 15:24:47 +0000
committerKjetil S. Matheussen <ksvalast@users.sourceforge.net>2004-01-25 15:24:47 +0000
commitba84e2aabb0663846804ea158539359939257539 (patch)
treec199e7caba51025cb30d52bdc7b71141b849195e
send and receive sound to and from jack ports, k_jack~ v0.0.2svn2git-root
svn path=/trunk/externals/k_jack~/; revision=1292
-rw-r--r--README18
-rw-r--r--help-k_jack~.pd33
-rw-r--r--k_jack~.c424
-rw-r--r--makefile85
4 files changed, 560 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..5c74df9
--- /dev/null
+++ b/README
@@ -0,0 +1,18 @@
+
+
+k_jack~ - General signal in/out external for pure data. Kjetil S. Matheussen.
+V0.0.2
+
+
+This external makes objects with signal inlets and outlets to jack ports. See
+the help patch.
+
+It works by calling functions and using variables in pd/src/s_audio.c that probably
+was not ment to be used for this purpose. But it works. And thats whats important.
+
+
+Kjetil S. Matheussen
+k.s.matheussen@notam02.no
+
+
+
diff --git a/help-k_jack~.pd b/help-k_jack~.pd
new file mode 100644
index 0000000..f8aa28d
--- /dev/null
+++ b/help-k_jack~.pd
@@ -0,0 +1,33 @@
+#N canvas 245 237 811 481 10;
+#X obj 20 67 osc~ 500;
+#X obj 96 113 k_jack~ freqtweak;
+#X obj 97 65 osc~ 600;
+#X obj 11 22 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10
+-262144 -1 -1 0 256;
+#X obj 97 23 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10
+-262144 -1 -1 0 256;
+#X text 597 425 k_jack~ General jack in/out;
+#X text 593 443 -Kjetil S. Matheussen \, 2004;
+#X text 230 114 <- Make sure freqtweak is running first!;
+#X text 292 64 <- Argument is a regexp string;
+#X obj 171 64 k_jack~ alsa_pcm;
+#X text 227 181 <- Its no harm having two similar k_jack objects;
+#X obj 105 181 k_jack~ alsa_pcm;
+#X obj 11 269 k_jack~ alsa_pcm:capture;
+#X obj 11 246 k_jack~ alsa_pcm:playback;
+#X text 201 246 <- Only playback.;
+#X text 200 267 <- Only capture.;
+#X obj 10 355 k_jack~ * . . . . . . . . . . . . . . . . . . .;
+#X text 359 357 <- "*" means all jack ports available. (The "."-s are
+there just for spacing.);
+#X obj 12 297 k_jack~ _1;
+#X text 95 298 <- All ports containing "_1";
+#X connect 0 0 1 0;
+#X connect 1 0 11 0;
+#X connect 1 1 11 1;
+#X connect 2 0 1 1;
+#X connect 3 0 0 0;
+#X connect 4 0 2 0;
+#X connect 9 0 1 0;
+#X connect 9 1 1 1;
+#X coords 0 0 1 1 200 140 1;
diff --git a/k_jack~.c b/k_jack~.c
new file mode 100644
index 0000000..0fd9d22
--- /dev/null
+++ b/k_jack~.c
@@ -0,0 +1,424 @@
+/* --------------------------- k_jack~ ----------------------------------- */
+/* ;; Kjetil S. Matheussen, 2004. */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+
+
+#include <m_pd.h>
+#include <s_stuff.h>
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <jack/jack.h>
+
+
+// Currently, the jack implementation in PD only supports 2 pd instances.
+// However, it doesn't hurt to use 100, so we use 100 to be prepared for
+// the future.
+
+#define MAX_PD_JACKCLIENTS 100
+
+
+static char *version =
+"k_jack~ v0.0.2, written by Kjetil S. Matheussen, k.s.matheussen@notam02.no";
+
+
+
+
+
+/***********************************************************/
+/********************* Jack part ***************************/
+/***********************************************************/
+
+struct JackPort{
+ struct JackPort *next;
+ char *name;
+ bool is_input;
+ int num;
+};
+
+static struct JackPort *jackports=NULL;
+
+/* Name(+":") of the client we belong to. ("pure_data_0:", "pure_data_1:", ...) */
+static char *clientname=NULL;
+
+
+
+static void set_pd_channels(int inc_ins,int inc_outs){
+ int num_recs=sys_get_inchannels();
+ int num_plays=sys_get_outchannels();
+
+ int t1[1]={0};
+ int t2[1]={0};
+ int t3[1]={num_recs+inc_ins};
+ int t4[1]={num_plays+inc_outs};
+ sys_close_audio();
+ sys_open_audio(1,t1,
+ 1,t3,
+ 1,t2,
+ 1,t4,
+ sys_getsr(),sys_schedadvance/1000,1);
+}
+
+
+
+static bool find_clientname(jack_client_t *client){
+ bool ret=false;
+
+ if(clientname!=NULL){
+ ret=true;
+ }else{
+ int lokke;
+ int num_clients=0;
+ int num_ports[MAX_PD_JACKCLIENTS]={0};
+ char temp[500];
+ const char **ports;
+
+ for(lokke=0;lokke<MAX_PD_JACKCLIENTS;lokke++){
+ sprintf(temp,"pure_data_%d",lokke);
+ ports=jack_get_ports(client,temp,"",0);
+ if(ports!=NULL){
+ while(ports[num_ports[lokke]]!=NULL){
+ num_ports[lokke]+=1;
+ }
+ }
+ }
+
+ set_pd_channels(1,1);
+
+ for(lokke=0;lokke<MAX_PD_JACKCLIENTS;lokke++){
+ int num_ports2[MAX_PD_JACKCLIENTS]={0};
+ sprintf(temp,"pure_data_%d",lokke);
+ ports=jack_get_ports(client,temp,"",0);
+ if(ports!=NULL){
+ while(ports[num_ports2[lokke]]!=NULL){
+ num_ports2[lokke]+=1;
+ }
+ }
+ if(num_ports2[lokke]!=num_ports[lokke]){
+ clientname=malloc(500);
+ sprintf(clientname,"pure_data_%d:",lokke);
+ //post("Got clientname: -%s-",clientname);
+ ret=true;
+ break;
+ }
+ }
+ set_pd_channels(-1,-1);
+ }
+
+ return ret;
+}
+
+
+/* Nearly the same as jack_get_ports, but filter out pure_data_%d ports. */
+static const char **my_jack_get_ports(jack_client_t *client,char *name){
+ const char **ret;
+ const char **ports;
+ int lokke=0;
+ int to=0;
+ int num_ports=0;
+
+ if(!strcmp("*",name))
+ ports=jack_get_ports(client,"","",0);
+ else
+ ports=jack_get_ports(client,name,"",0);
+ if(ports==NULL) return NULL;
+
+ while(ports[num_ports]!=NULL){
+ num_ports++;
+ }
+ ret=calloc(sizeof(char *),num_ports);
+
+ while(ports[lokke]!=NULL){
+ if(strstr(ports[lokke],clientname)==NULL){
+ ret[to]=ports[lokke];
+ to++;
+ }
+ lokke++;
+ }
+ if(to==0){
+ free(ret);
+ ret=NULL;
+ }
+
+ exit:
+ free(ports);
+ return ret;
+}
+
+
+/* Disconnect all the ports connection to an alsa_pcm port. */
+static void disconnect_all_alsa(jack_client_t *client,const char *portname){
+ int lokke=0;
+ jack_port_t *port=jack_port_by_name(client,portname);
+ const char **ports=jack_port_get_all_connections(client,port);
+ if(ports==NULL) return;
+
+ while(ports[lokke]!=NULL){
+ if(strstr(ports[lokke],"alsa_pcm:")==ports[lokke]){
+ if(jack_port_flags(port)&JackPortIsOutput){
+ jack_disconnect(client,portname,ports[lokke]);
+ }else{
+ jack_disconnect(client,ports[lokke],portname);
+ }
+ }
+ lokke++;
+ }
+ free(ports);
+}
+
+
+static int add_pd_channel(bool is_input){
+ set_pd_channels(is_input==true?0:1,is_input==true?1:0);
+ return is_input==true
+ ?sys_get_outchannels()-1
+ :sys_get_inchannels()-1;
+}
+
+
+/* Returns the dac/adc index the jackport with name 'portname' has in pd. Create if it doesn't exist. */
+static int get_portindex(jack_client_t *client,const char *portname, bool is_input){
+ struct JackPort *jp=jackports;
+
+ while(jp!=NULL){
+ if(!strcmp(jp->name,portname)) break;
+ jp=jp->next;
+ }
+ if(jp==NULL){
+ char temp[500];
+ char temp2[500];
+ jp=calloc(1,sizeof(struct JackPort));
+ jp->name=strdup(portname);
+ jp->is_input=is_input;
+ jp->num=add_pd_channel(is_input);
+ jp->next=jackports;
+ jackports=jp;
+ disconnect_all_alsa(client,portname);
+ if(is_input){
+ sprintf(temp,"%soutput%d",clientname,jp->num);
+ jack_connect(client,temp,portname);
+ sprintf(temp2,"to_%s",portname);
+ }else{
+ sprintf(temp,"%sinput%d",clientname,jp->num);
+ jack_connect(client,portname,temp);
+ sprintf(temp2,"from_%s",portname);
+ }
+ while(strstr(temp2,":")) strstr(temp2,":")[0]='-';
+ if(strlen(temp2)+strlen(clientname)<32)
+ jack_port_set_name(jack_port_by_name(client,temp),temp2);
+ }
+ return jp->num;
+}
+
+
+
+
+/***********************************************************/
+/********************* PD part *****************************/
+/***********************************************************/
+
+typedef struct _k_jack
+{
+ t_object x_obj;
+ int num_recs;
+ int num_plays;
+ int *rec_nums;
+ int *play_nums;
+ float x_float;
+} t_k_jack;
+
+
+static t_class *k_jack_class;
+
+
+
+static t_int *k_jack_perform_add(t_int *w){
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ int lokke;
+
+ for(lokke=0;lokke<DEFDACBLKSIZE;lokke++){
+ out[lokke]+=in[lokke];
+ }
+
+ return (w+3);
+}
+
+
+static t_int *k_jack_perform_copy(t_int *w){
+ t_float *in = (t_float *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int lokke;
+
+ for(lokke=0;lokke<DEFDACBLKSIZE;lokke++){
+ out[lokke]=in[lokke];
+ }
+
+ return (w+3);
+}
+
+
+static void k_jack_dsp(t_k_jack *x, t_signal **sp)
+{
+ int lokke;
+ int ch=0;
+
+ for(lokke=0;lokke<x->num_recs;lokke++){
+ if(sp[ch]->s_n!=DEFDACBLKSIZE)
+ post("k_jack~ wrong framesize. Is this possible?");
+ else
+ dsp_add(
+ k_jack_perform_add,
+ 2,
+ sp[ch]->s_vec,
+ sys_soundout + x->rec_nums[lokke]*DEFDACBLKSIZE
+ );
+ ch++;
+ }
+ for(lokke=0;lokke<x->num_plays;lokke++){
+ if(sp[ch]->s_n!=DEFDACBLKSIZE)
+ post("k_jack~ wrong framesize. Is this possible?");
+ else
+ dsp_add(
+ k_jack_perform_copy,
+ 2,
+ sys_soundin + x->play_nums[lokke]*DEFDACBLKSIZE,
+ sp[ch]->s_vec
+ );
+ ch++;
+ }
+}
+
+
+static void k_jack_free(t_k_jack *x){
+ free(x->rec_nums);
+ free(x->play_nums);
+}
+
+
+static void *k_jack_new(t_symbol *s){
+ int num_recs=0;
+ int num_plays=0;
+ int lokke=0;
+ static jack_client_t *client=NULL;
+ const char **ports=NULL;
+ t_k_jack *x=NULL;
+
+
+ if(sys_audioapi!=API_JACK){
+ post("Error. k_jack~ will not work without jack as the sound API.");
+ goto exit;
+ }
+
+ if(client==NULL){
+ for(lokke=0;lokke<MAX_PD_JACKCLIENTS;lokke++){
+ char temp[500];
+ sprintf(temp,"k_jack_tilde_%d",lokke);
+ client=jack_client_new(temp);
+ if(client!=NULL) break;
+ }
+ }
+
+ if(client==NULL){
+ post("k_jack~: Could not make jack client.");
+ goto exit;
+ }
+
+ if(find_clientname(client)==false){
+ post("k_jack~: Could not find the name of pure data jack client.");
+ goto exit;
+ }
+
+ ports=my_jack_get_ports(client,s->s_name);
+ if(ports==NULL){
+ post("k_jack~: Client \"%s\" not found.\n",s->s_name);
+ goto exit;
+ }
+ while(ports[lokke]!=NULL){
+ jack_port_t* port=jack_port_by_name(client,ports[lokke]);
+ //post("%s, type: %s, flags: %d",ports[lokke],jack_port_type(port),jack_port_flags(port));
+ if(jack_port_flags(port)&JackPortIsInput){
+ num_recs++;
+ }else{
+ if(jack_port_flags(port)&JackPortIsOutput){
+ num_plays++;
+ }
+ }
+ lokke++;
+ }
+
+ if(num_plays==0 && num_recs==0){
+ post("Client(s) containing the name \"%s\" have no input or output ports.",s->s_name);
+ goto exit;
+ }
+
+ x = (t_k_jack *)pd_new(k_jack_class);
+
+ x->rec_nums=calloc(sizeof(int),num_recs);
+ x->play_nums=calloc(sizeof(int),num_plays);
+
+ //post("recs: %d, plays: %d\n",num_recs,num_plays);
+ lokke=0;
+ while(ports[lokke]!=NULL){
+ jack_port_t* port=jack_port_by_name(client,ports[lokke]);
+ if(jack_port_flags(port)&JackPortIsInput){
+ x->rec_nums[x->num_recs]=get_portindex(client,ports[lokke],true);
+ if(x->num_recs>0)
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ //post("Made inlet %s %d",ports[lokke],x->rec_nums[x->num_recs]);
+ x->num_recs++;
+ }else{
+ if(jack_port_flags(port)&JackPortIsOutput){
+ x->play_nums[x->num_plays]=get_portindex(client,ports[lokke],false);
+ outlet_new(&x->x_obj, gensym("signal"));
+ //post("Made outlet %s %d",ports[lokke],x->play_nums[x->num_plays]);
+ x->num_plays++;
+ }
+ }
+ lokke++;
+ }
+
+
+ exit:
+
+ if(ports!=NULL) free(ports);
+
+ /* Program crash if client is closed. (as a workaround, I made it static for reuse. -Kjetil) (I thought this gruesome bug was fixed!?!)*/
+ //if(client!=NULL) jack_client_close(client);
+
+ return (x);
+}
+
+
+
+void k_jack_tilde_setup(void){
+ k_jack_class = class_new(gensym("k_jack~"), (t_newmethod)k_jack_new, (t_method)k_jack_free,
+ sizeof(t_k_jack), 0, A_SYMBOL, 0);
+ CLASS_MAINSIGNALIN(k_jack_class, t_k_jack, x_float);
+ class_addmethod(k_jack_class, (t_method)k_jack_dsp, gensym("dsp"), 0);
+
+ class_sethelpsymbol(k_jack_class, gensym("help-k_jack~.pd"));
+
+ post(version);
+}
+
+
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..e382213
--- /dev/null
+++ b/makefile
@@ -0,0 +1,85 @@
+NAME=k_jack~
+CSYM=k_jack_tilde
+
+current: pd_linux
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll
+
+.SUFFIXES: .dll
+
+PDNTCFLAGS = /W3 /WX /O2 /G6 /DNT /DPD /nologo
+VC="C:\Programme\Microsoft Visual Studio\VC98"
+
+PDNTINCLUDE = /I. /Ic:\pd\tcl\include /Ic:\pd\src /I$(VC)\include /Iinclude
+
+PDNTLDIR = $(VC)\Lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ $(PDNTLDIR)\user32.lib \
+ $(PDNTLDIR)\uuid.lib \
+ c:\pd\bin\pd.lib \
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /export:$(CSYM)_setup $*.obj $(PDNTLIB)
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix5: $(NAME).pd_irix5
+
+.SUFFIXES: .pd_irix5
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+SGIINCLUDE = -I../../src
+
+.c.pd_irix5:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o
+ rm $*.o
+
+# ----------------------- IRIX 6.x -----------------------
+
+pd_irix6: $(NAME).pd_irix6
+
+.SUFFIXES: .pd_irix6
+
+SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -Ofast=ip32
+
+.c.pd_irix6:
+ cc $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -n32 -IPA -shared -rdata_shared -o $*.pd_irix6 $*.o
+ rm $*.o
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -DUNIX -O2 -funroll-loops -fomit-frame-pointer \
+ -Wall -W -Wshadow -Wstrict-prototypes \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+PDSRCDIR=../../../src
+LINUXINCLUDE = -I$(PDSRCDIR)
+
+.c.pd_linux:
+ gcc $(LINUXCFLAGS) $(LINUXINCLUDE) -g -o $*.o -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o -lc -lm -ljack
+ strip --strip-unneeded $*.pd_linux
+ rm -f $*.o ../$*.pd_linux
+ ln -s $*/$*.pd_linux ..
+
+# ----------------------------------------------------------
+
+install:
+ cp help-*.pd ../../doc/5.reference
+
+clean:
+ rm -f *.o *.pd_* so_locations *~