pd~.c 27.8 KB
Newer Older
1
2
3
4
5
6
7
/*
  pd~.c - embed a Pd process within Pd or Max.

  Copyright 2008 Miller Puckette
  BSD license; see README.txt in this distribution for details.
*/

8
9
10
11
12
13
14
15
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#include <process.h>
#include <windows.h>
typedef int socklen_t;
#define EADDRINUSE WSAEADDRINUSE
#else
16
17
18
19
20
21
22
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <sys/wait.h>
23
#include <fcntl.h>
24
25
26
#endif
#include <sys/types.h>
#include <sys/stat.h>
27

28
#ifdef _MSC_VER
29
#pragma warning (disable: 4305 4244)
30
31
#define snprintf sprintf_s
#define stat _stat
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#endif

#ifdef MSP
#include "ext.h"
#include "z_dsp.h"
#include "math.h"
#include "ext_support.h"
#include "ext_proto.h"
#include "ext_obex.h"

typedef double t_floatarg;
#define w_symbol w_sym
#define A_SYMBOL A_SYM
#define getbytes t_getbytes
#define freebytes t_freebytes
47
#define PDERROR error(
48
49
50
51
52
53
54
55
56
void *pd_tilde_class;
#define MAXPDSTRING 4096
#define DEFDACBLKSIZE 64
#endif /* MSP */

#ifdef PD
#include "m_pd.h"
#include "s_stuff.h"
static t_class *pd_tilde_class;
57
#define PDERROR pd_error(x, 
58
59
60

#endif

61
#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__)
62
63
64
65
66
67
68
69
70
71
72
73
#ifdef __x86_64__
static char pd_tilde_dllextent[] = ".l_ia64",
    pd_tilde_dllextent2[] = ".pd_linux";
#else
static char pd_tilde_dllextent[] = ".l_i386",
    pd_tilde_dllextent2[] = ".pd_linux";
#endif
#endif
#ifdef __APPLE__
static char pd_tilde_dllextent[] = ".d_fat",
    pd_tilde_dllextent2[] = ".pd_darwin";
#endif
74
75
76
#if defined(_WIN32) || defined(__CYGWIN__)
static char pd_tilde_dllextent[] = ".m_i386", pd_tilde_dllextent2[] = ".dll";
#endif
77

78
79
80
#define FOOFOO
#include "binarymsg.c"

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/* ------------------------ pd_tilde~ ----------------------------- */

#define MSGBUFSIZE 65536

typedef struct _pd_tilde
{
#ifdef PD
    t_object x_obj;
    t_clock *x_clock;
    t_outlet *x_outlet1;        /* for messages back from subproc */
    t_canvas *x_canvas;
#endif /* PD */
#ifdef MSP
    t_pxobject x_obj;
    void *x_outlet1;
    void *x_clock;
#endif /* MSP */
    FILE *x_infd;
    FILE *x_outfd;
100
    t_binbuf *x_binbuf;
101
102
103
104
    int x_childpid;
    int x_ninsig;
    int x_noutsig;
    int x_fifo;
105
106
    int x_binary;
    t_float x_sr;
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    t_symbol *x_pddir;
    t_symbol *x_schedlibdir;
    t_sample **x_insig;
    t_sample **x_outsig;
} t_pd_tilde;

#ifdef MSP
static void *pd_tilde_new(t_symbol *s, long ac, t_atom *av);
static void pd_tilde_tick(t_pd_tilde *x);
static t_int *pd_tilde_perform(t_int *w);
static void pd_tilde_dsp(t_pd_tilde *x, t_signal **sp);
void pd_tilde_assist(t_pd_tilde *x, void *b, long m, long a, char *s);
static void pd_tilde_free(t_pd_tilde *x);
void pd_tilde_setup(void);
int main();
void pd_tilde_minvel_set(t_pd_tilde *x, void *attr, long ac, t_atom *av);
char *strcpy(char *s1, const char *s2);
#endif

static void pd_tilde_tick(t_pd_tilde *x);
static void pd_tilde_close(t_pd_tilde *x)
{
129
130
131
#ifdef _WIN32
    int termstat;
#endif
132
133
134
135
136
    if (x->x_outfd)
        fclose(x->x_outfd);
    if (x->x_infd)
        fclose(x->x_infd);
    if (x->x_childpid > 0)
137
138
139
#ifdef _WIN32
        _cwait(&termstat, x->x_childpid, WAIT_CHILD);
#else
140
        waitpid(x->x_childpid, 0, 0);
141
142
#endif
    binbuf_clear(x->x_binbuf);
143
144
145
146
147
148
    x->x_infd = x->x_outfd = 0;
    x->x_childpid = -1;
}

static void pd_tilde_readmessages(t_pd_tilde *x)
{
149
150
151
    t_atom at;
    binbuf_clear(x->x_binbuf);
    if (x->x_binary)
152
    {
153
154
        int nonempty = 0;
        while (1)
155
        {
156
157
158
159
160
            pd_tilde_getatom(&at, x->x_infd);
            if (!nonempty && at.a_type == A_SEMI)
                break;
            nonempty = (at.a_type != A_SEMI);
            binbuf_add(x->x_binbuf, 1, &at);
161
        }
162
163
164
165
166
    }
    else    /* ASCII */
    {
        t_binbuf *tmpb = binbuf_new();
        while (1)
167
        {
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
            char msgbuf[MAXPDSTRING];
            int c, infill = 0, n;
            t_atom *vec;
            while (isspace((c = getc(x->x_infd))) && c != EOF)
                ;
            if (c == EOF)
                return;
            do
                msgbuf[infill++] = c;
            while (!isspace((c = getc(x->x_infd))) && c != ';' && c != EOF) ;
            binbuf_text(tmpb, msgbuf, infill);
            n = binbuf_getnatom(tmpb);
            vec = binbuf_getvec(tmpb);
            binbuf_add(x->x_binbuf, n, vec);
            if (!n)
183
            {
184
185
                bug("pd~");
                break;  /* shouldn't happen */
186
            }
187
            if (vec[0].a_type == A_SEMI)
188
189
                break;
        }
190
191
192
        binbuf_free(tmpb);
        /* if (binbuf_getnatom(x->x_binbuf) > 1)
            binbuf_print(x->x_binbuf); */
193
    }
194
    clock_delay(x->x_clock, 0);
195
196
}

197
198
199
200
201
202
203
204
#define FIXEDARG 14
#define MAXARG 100
#ifdef _WIN32
#define EXTENT ".com"
#else
#define EXTENT ""
#endif

205
static void pd_tilde_donew(t_pd_tilde *x, char *pddir, char *schedlibdir,
206
207
    char *patchdir, int argc, t_atom *argv, int ninsig, int noutsig,
    int fifo, t_float samplerate)
208
209
{
    int i, pid, pipe1[2], pipe2[2];
210
211
212
    char pdexecbuf[MAXPDSTRING], schedbuf[MAXPDSTRING], tmpbuf[MAXPDSTRING];
    char *execargv[FIXEDARG+MAXARG+1], ninsigstr[20], noutsigstr[20],
        sampleratestr[40];
213
214
215
    struct stat statbuf;
    x->x_infd = x->x_outfd = 0;
    x->x_childpid = -1;
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    if (argc > MAXARG)
    {
        post("pd~: args truncated to %d items", MAXARG);
        argc = MAXARG;
    }
    sprintf(ninsigstr, "%d", ninsig);
    sprintf(noutsigstr, "%d", noutsig);
    sprintf(sampleratestr, "%f", (float)samplerate);
    /* Windows is still relying on makefile.mingw which still uses the
       name "pd" for the executable, so we need an ifdef here. Yuck! */
#ifdef _WIN32
    snprintf(tmpbuf, MAXPDSTRING, "%s/bin/pd" EXTENT, pddir);
#else
    snprintf(tmpbuf, MAXPDSTRING, "%s/bin/pd-l2ork" EXTENT, pddir);
#endif
    sys_bashfilename(tmpbuf, pdexecbuf);
232
233
    if (stat(pdexecbuf, &statbuf) < 0)
    {
234
235
        snprintf(tmpbuf, MAXPDSTRING, "%s/../../../bin/pd" EXTENT, pddir);
        sys_bashfilename(tmpbuf, pdexecbuf);
236
237
        if (stat(pdexecbuf, &statbuf) < 0)
        {
238
239
            snprintf(tmpbuf, MAXPDSTRING, "%s/pd" EXTENT, pddir);
            sys_bashfilename(tmpbuf, pdexecbuf);
240
241
            if (stat(pdexecbuf, &statbuf) < 0)
            {
242
                PDERROR "pd~: can't stat %s", pdexecbuf);
243
244
245
246
                goto fail1;
            }
        }
    }
247
    snprintf(tmpbuf, MAXPDSTRING, "%s/pdsched%s", schedlibdir, 
248
        pd_tilde_dllextent);
249
    sys_bashfilename(tmpbuf, schedbuf);
250
251
    if (stat(schedbuf, &statbuf) < 0)
    {
252
        snprintf(tmpbuf, MAXPDSTRING, "%s/pdsched%s", schedlibdir, 
253
            pd_tilde_dllextent2);
254
        sys_bashfilename(tmpbuf, schedbuf);
255
256
        if (stat(schedbuf, &statbuf) < 0)
        {
257
            PDERROR "pd~: can't stat %s", schedbuf);
258
259
260
            goto fail1;
        }       
    }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
        /* but the sub-process wants the scheduler name without the suffix */
    snprintf(tmpbuf, MAXPDSTRING, "%s/pdsched", schedlibdir);
    sys_bashfilename(tmpbuf, schedbuf);
    /* We have to quote the executable under Windows to make sure spaces
       in the path don't confuse Windows. */
#ifdef _WIN32
    char quoted_execbuf[MAXPDSTRING];
    sprintf(quoted_execbuf, "\"%s\"", pdexecbuf);
    execargv[0] = quoted_execbuf;
#else
    execargv[0] = pdexecbuf;
#endif
    /* For the moment we must start the child Pd with the "-unique" flag
       to ensure its GUI gets its own instance. Hopefully at some point in
       the future we can figure out a usable way to get multiple Pds running
       connected to the same GUI process. */
    execargv[1] = "-unique";
    execargv[2] = "-schedlib";
#ifdef _WIN32
    char quoted_schedbuf[MAXPDSTRING];
    sprintf(quoted_schedbuf, "\"%s\"", schedbuf);
    execargv[3] = quoted_schedbuf;
#else
    execargv[3] = schedbuf;
#endif
    execargv[4] = "-extraflags";
    execargv[5] = (x->x_binary ? "b" : "a");
    execargv[6] = "-path";
#ifdef _WIN32
    char quoted_patchdir[MAXPDSTRING];
    strcpy(quoted_patchdir, "\"");
    sys_bashfilename(patchdir, tmpbuf);
    strcat(quoted_patchdir, tmpbuf);
    strcat(quoted_patchdir, "\"");
    execargv[7] = quoted_patchdir;
#else
    execargv[7] = patchdir;
#endif
    execargv[8] = "-inchannels";
    execargv[9] = ninsigstr;
    execargv[10] = "-outchannels";
    execargv[11] = noutsigstr;
    execargv[12] = "-r";
    execargv[13] = sampleratestr;

        /* convert atom arguments to strings (temporarily allocating space) */
    for (i = 0; i < argc; i++)
    {
309
#ifdef PD
310
311
312
313
314
315
316
317
318
319
320
321
322
        atom_string(&argv[i], tmpbuf, MAXPDSTRING);
#endif
#ifdef MSP
            /* because Mac pathnames sometimes have an evil preceeding
            colon character, we test for and silently eat them */
        if (argv[i].a_type == A_SYM)
            strncpy(tmpbuf, (*argv->a_w.w_sym->s_name == ':'?
                argv->a_w.w_sym->s_name+1 : argv->a_w.w_sym->s_name),
                MAXPDSTRING-3);
        else if (argv[i].a_type == A_LONG)
            sprintf(tmpbuf, "%ld", argv->a_w.w_long);
        else if (argv[i].a_type == A_FLOAT)
            sprintf(tmpbuf,  "%f", argv->a_w.w_float);
323
#endif
324
325
326
327
328
329
330
331
        execargv[FIXEDARG+i] = malloc(strlen(tmpbuf) + 1);
        strcpy(execargv[FIXEDARG+i], tmpbuf);
    }
    execargv[argc+FIXEDARG] = 0;
#if 1
    for (i = 0; i < argc+FIXEDARG; i++)
        fprintf(stderr, "%s ", execargv[i]);
    fprintf(stderr, "\n");
332
#endif
333
334
335
#ifdef _WIN32
    if (_pipe(pipe1, 65536, O_BINARY | O_NOINHERIT) < 0)   
#else
336
    if (pipe(pipe1) < 0)   
337
#endif
338
    {
339
        PDERROR "pd~: can't create pipe");
340
341
        goto fail1;
    }
342
343
344
#ifdef _WIN32
    if (_pipe(pipe2, 65536, O_BINARY | O_NOINHERIT) < 0)   
#else
345
    if (pipe(pipe2) < 0)   
346
#endif
347
    {
348
        PDERROR "pd~: can't create pipe");
349
350
        goto fail2;
    }
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
#ifdef _WIN32
    {
        int stdinwas = _dup(0), stdoutwas = _dup(1);
        if (pipe2[1] == 0)
            pipe2[1] = _dup(pipe2[1]);
        if (pipe1[0] != 0)
            _dup2(pipe1[0], 0);
        if (pipe2[1] != 1)
            _dup2(pipe2[1], 1);
        pid = _spawnv(P_NOWAIT, pdexecbuf, execargv);
        if (pid < 0)
        {
            post("%s: couldn't start subprocess (%s)\n", execargv[0],
                strerror(errno));
            goto fail1;
        }
        _dup2(stdinwas, 0);
        _dup2(stdoutwas, 1);
        _close(stdinwas);
        _close(stdoutwas);
    }
#else /* _WIN32 */
373
374
    if ((pid = fork()) < 0)
    {
375
        PDERROR "pd~: can't fork");
376
377
378
379
380
        goto fail3;
    }
    else if (pid == 0)
    {
        /* child process */
381
382
            /* the first dup2 below would bash pipe2[1] if it happens to be
                zero so in that case renumber it */
383
        if (pipe2[1] == 0)
384
385
386
387
388
389
390
            pipe2[1] = dup(pipe2[1]);
        if (pipe1[0] != 0)
        {
            dup2(pipe1[0], 0);
            close (pipe1[0]);
        }
        if (pipe2[1] != 1)
391
        {
392
393
            dup2(pipe2[1], 1);
            close (pipe2[1]);
394
395
396
397
398
        }
        if (pipe1[1] >= 2)
            close(pipe1[1]);
        if (pipe2[0] >= 2)
            close(pipe2[0]);
399
400
        execv("/usr/lib/pd-l2ork/bin/pd-l2ork", execargv);
        perror("pd~ execv:");
401
402
        _exit(1);
    }
403
404
#endif /* _WIN32 */
        /* done with fork/exec or spawn; parent continues here */
405
406
    close(pipe1[0]);
    close(pipe2[1]);
407
#ifndef _WIN32      /* this was done in windows via the O_NOINHERIT flag */
408
409
    fcntl(pipe1[1],  F_SETFD, FD_CLOEXEC);
    fcntl(pipe2[0],  F_SETFD, FD_CLOEXEC);
410
#endif
411
412
413
414
    x->x_outfd = fdopen(pipe1[1], "w");
    x->x_infd = fdopen(pipe2[0], "r");
    x->x_childpid = pid;
    for (i = 0; i < fifo; i++)
415
        if (x->x_binary)
416
    {
417
418
419
        putc(A_SEMI, x->x_outfd);
        pd_tilde_putfloat(0, x->x_outfd);
        putc(A_SEMI, x->x_outfd);
420
    }
421
422
423
424
    else fprintf(x->x_outfd, "%s", ";\n0;\n");

    fflush(x->x_outfd);
    binbuf_clear(x->x_binbuf);
425
426
    pd_tilde_readmessages(x);
    return;
427
#ifndef _WIN32
428
429
430
431
fail3:
    close(pipe2[0]);
    close(pipe2[1]);
    if (x->x_childpid > 0)
432
433
    waitpid(x->x_childpid, 0, 0);
#endif
434
435
436
437
438
439
440
441
442
443
444
445
fail2:
    close(pipe1[0]);
    close(pipe1[1]);
fail1:
    x->x_infd = x->x_outfd = 0;
    x->x_childpid = -1;
    return;
}

static t_int *pd_tilde_perform(t_int *w)
{
    t_pd_tilde *x = (t_pd_tilde *)(w[1]);
446
    int n = (int)(w[2]), i, j, nsigs, numbuffill = 0, c;
447
448
449
450
    char numbuf[80];
    FILE *infd = x->x_infd;
    if (!infd)
        goto zeroit;
451
    if (x->x_binary)
452
    {
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
        putc(A_SEMI, x->x_outfd);
        if (!x->x_ninsig)
            pd_tilde_putfloat(0, x->x_outfd);
        else for (i = 0; i < x->x_ninsig; i++)
        {
            t_sample *fp = x->x_insig[i];
            for (j = 0; j < n; j++)
                pd_tilde_putfloat(*fp++, x->x_outfd);
            for (; j < DEFDACBLKSIZE; j++)
                pd_tilde_putfloat(0, x->x_outfd);
        }
        putc(A_SEMI, x->x_outfd);
    }
    else
    {
        fprintf(x->x_outfd, ";\n");
        if (!x->x_ninsig)
470
            fprintf(x->x_outfd, "0\n");
471
472
473
474
475
476
477
478
479
        else for (i = 0; i < x->x_ninsig; i++)
        {
            t_sample *fp = x->x_insig[i];
            for (j = 0; j < n; j++)
                fprintf(x->x_outfd, "%g\n", *fp++);
            for (; j < DEFDACBLKSIZE; j++)
                fprintf(x->x_outfd, "0\n");
        }
        fprintf(x->x_outfd, ";\n");
480
481
    }
    fflush(x->x_outfd);
482
483
    nsigs = j = 0;
    if (x->x_binary)
484
485
486
    {
        while (1)
        {
487
488
            t_atom at;
            if (!pd_tilde_getatom(&at, infd))
489
490
            {
                if (errno)
491
492
                    PDERROR "pd~: %s", strerror(errno));
                else PDERROR "pd~: subprocess exited");
493
494
495
                pd_tilde_close(x);
                goto zeroit;
            }
496
497
498
            if (at.a_type == A_SEMI)
                break;
            else if (at.a_type == A_FLOAT)
499
            {
500
501
502
503
                if (nsigs < x->x_noutsig)
                    x->x_outsig[nsigs][j] = at.a_w.w_float;
                if (++j >= DEFDACBLKSIZE)
                    j = 0, nsigs++;
504
            }
505
506
507
508
509
510
511
512
            else PDERROR "pd~: subprocess returned malformed audio");
        }
    }
    else
    {
        while (1)
        {
            while (1)
513
            {
514
515
                c = getc(infd);
                if (c == EOF)
516
                {
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
                    if (errno)
                        PDERROR "pd~: %s", strerror(errno));
                    else PDERROR "pd~: subprocess exited");
                    pd_tilde_close(x);
                    goto zeroit;
                }
                else if (!isspace(c) && c != ';')
                {
                    if (numbuffill < (80-1))
                        numbuf[numbuffill++] = c;
                }
                else
                {
                    t_sample z;
                    if (numbuffill)
                    {
                        numbuf[numbuffill] = 0;
#if PD_FLOATSIZE == 32
                        if (sscanf(numbuf, "%f", &z) < 1)
#else
                        if (sscanf(numbuf, "%lf", &z) < 1)
#endif
                            continue;
                        if (nsigs < x->x_noutsig)
                            x->x_outsig[nsigs][j] = z;
                        if (++j >= DEFDACBLKSIZE)
                            j = 0, nsigs++;
                    }
                    numbuffill = 0;
                    break;
547
548
                }
            }
549
550
551
            /* message terminated */
            if (c == ';')
                break;
552
553
        }
    }
554
555
556
    if (nsigs < x->x_noutsig)
        post("sigs %d, j %d", nsigs, j);
    for (; nsigs < x->x_noutsig; nsigs++, j = 0)
557
558
    {
        for (; j < DEFDACBLKSIZE; j++)
559
            x->x_outsig[nsigs][j] = 0;
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
    }
    pd_tilde_readmessages(x);
    return (w+3);
zeroit:
    for (i = 0; i < x->x_noutsig; i++)
    {
        for (j = 0; j < DEFDACBLKSIZE; j++)
            x->x_outsig[i][j] = 0;
    }
    return (w+3);
}

static void pd_tilde_dsp(t_pd_tilde *x, t_signal **sp)
{
    int i, n = (x->x_ninsig || x->x_noutsig ? sp[0]->s_n : 1);
    t_sample **g;
        
    for (i = 0, g = x->x_insig; i < x->x_ninsig; i++, g++)
        *g = (*(sp++))->s_vec;
579
580
581
582
        /* if there were no input signals Pd still provided us with one,
        which we ignore: */
    if (!x->x_ninsig)
        sp++;
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
    for (i = 0, g = x->x_outsig; i < x->x_noutsig; i++, g++)
        *g = (*(sp++))->s_vec;
    
    dsp_add(pd_tilde_perform, 2, x, n);
}

static void pd_tilde_pdtilde(t_pd_tilde *x, t_symbol *s,
    int argc, t_atom *argv)
{
    t_symbol *sel = ((argc > 0 && argv->a_type == A_SYMBOL) ?
        argv->a_w.w_symbol : gensym("?")), *schedlibdir;
    char *patchdir;
    if (sel == gensym("start"))
    {
        char pdargstring[MAXPDSTRING];
        if (x->x_infd)
            pd_tilde_close(x);
        pdargstring[0] = 0;
        argc--; argv++;
#ifdef PD
        patchdir = canvas_getdir(x->x_canvas)->s_name;
#endif
#ifdef MSP
        patchdir = ".";
#endif
        schedlibdir = x->x_schedlibdir;
        if (schedlibdir == gensym(".") && x->x_pddir != gensym("."))
        {
            char *pds = x->x_pddir->s_name, scheddirstring[MAXPDSTRING];
            int l = strlen(pds);
            if (l >= 4 && (!strcmp(pds+l-3, "bin") || !strcmp(pds+l-4, "bin/")))
                snprintf(scheddirstring, MAXPDSTRING, "%s/../extra/pd~", pds);
            else snprintf(scheddirstring, MAXPDSTRING, "%s/extra/pd~", pds);
            schedlibdir = gensym(scheddirstring);
        }
        pd_tilde_donew(x, x->x_pddir->s_name, schedlibdir->s_name,
619
            patchdir, argc, argv, x->x_ninsig, x->x_noutsig, x->x_fifo,
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
                x->x_sr);
    }
    else if (sel == gensym("stop"))
    {
        if (x->x_infd)
            pd_tilde_close(x);
    }
    else if (sel == gensym("pddir"))
    {
        if ((argc > 1) && argv[1].a_type == A_SYMBOL)
        {
            t_symbol *sym = argv[1].a_w.w_symbol;
#ifdef MSP
            if (sym->s_name[0] == ':')
                sym = gensym(s->s_name+1);
#endif
            x->x_pddir = sym;
        }
638
        else PDERROR "pd~ pddir: needs symbol argument");
639
    }
640
    else PDERROR "pd~: unknown control message: %s", sel->s_name);
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
}

static void pd_tilde_free(t_pd_tilde *x)
{
#ifdef MSP
    dsp_free((t_pxobject *)x);
#endif
    pd_tilde_close(x);
    clock_free(x->x_clock);
}

/* -------------------------- Pd glue ------------------------- */
#ifdef PD

static void pd_tilde_tick(t_pd_tilde *x)
{
    int messstart = 0, i, n;
    t_atom *vec;
    /* binbuf_print(b); */
660
661
    n = binbuf_getnatom(x->x_binbuf);
    vec = binbuf_getvec(x->x_binbuf);
662
663
664
665
666
667
668
669
670
671
672
673
    for (i = 0; i < n; i++)
    {
        if (vec[i].a_type == A_SEMI)
        {
            if (i > messstart && vec[messstart].a_type == A_SYMBOL)
                outlet_anything(x->x_outlet1, vec[messstart].a_w.w_symbol,
                    i-(messstart+1), vec+(messstart+1));
            else if (i > messstart)
                outlet_list(x->x_outlet1, 0, i-messstart, vec+messstart);
            messstart = i+1;
        }
    }
674
    binbuf_clear(x->x_binbuf);
675
676
677
678
679
680
681
682
}

static void pd_tilde_anything(t_pd_tilde *x, t_symbol *s,
    int argc, t_atom *argv)
{
    char msgbuf[MAXPDSTRING];
    if (!x->x_outfd)
        return;
683
    if (x->x_binary)
684
    {
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
        pd_tilde_putsymbol(s, x->x_outfd);
        for (; argc--; argv++)
        {
            if (argv->a_type == A_FLOAT)
                pd_tilde_putfloat(argv->a_w.w_float, x->x_outfd);
            else if (argv->a_type == A_SYMBOL)
                pd_tilde_putsymbol(argv->a_w.w_symbol, x->x_outfd);
        }
        putc(A_SEMI, x->x_outfd);
    }
    else
    {
        fprintf(x->x_outfd, "%s ", s->s_name);
        while (argc--)
        {
            atom_string(argv++, msgbuf, MAXPDSTRING);
            fprintf(x->x_outfd, "%s ", msgbuf);
        }
        fprintf(x->x_outfd, ";\n");
704
705
706
    }
}

707
extern char *class_gethelpdir(t_class *c);
708
709
710
static void *pd_tilde_new(t_symbol *s, int argc, t_atom *argv)
{
    t_pd_tilde *x = (t_pd_tilde *)pd_new(pd_tilde_class);
711
712
    int ninsig = 2, noutsig = 2, j, fifo = 5, binary = 1;
    t_float sr = sys_getsr();
713
    t_sample **g;
714
    t_symbol *pddir = sys_libdir,
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
        *scheddir = gensym(class_gethelpdir(pd_tilde_class));
    /* fprintf(stderr, "pd %s, sched %s\n", pddir->s_name, scheddir->s_name); */
    while (argc > 0)
    {
        t_symbol *firstarg = atom_getsymbolarg(0, argc, argv);
        if (!strcmp(firstarg->s_name, "-sr") && argc > 1)
        {
            sr = atom_getfloatarg(1, argc, argv);
            argc -= 2; argv += 2;
        }
        else if (!strcmp(firstarg->s_name, "-ninsig") && argc > 1)
        {
            ninsig = atom_getfloatarg(1, argc, argv);
            argc -= 2; argv += 2;
        }
        else if (!strcmp(firstarg->s_name, "-noutsig") && argc > 1)
        {
            noutsig = atom_getfloatarg(1, argc, argv);
            argc -= 2; argv += 2;
        }
        else if (!strcmp(firstarg->s_name, "-fifo") && argc > 1)
        {
            fifo = atom_getfloatarg(1, argc, argv);
            argc -= 2; argv += 2;
        }
        else if (!strcmp(firstarg->s_name, "-pddir") && argc > 1)
        {
            pddir = atom_getsymbolarg(1, argc, argv);
            argc -= 2; argv += 2;
        }
        else if (!strcmp(firstarg->s_name, "-scheddir") && argc > 1)
        {
            scheddir = atom_getsymbolarg(1, argc, argv);
            argc -= 2; argv += 2;
        }
750
751
752
753
754
        else if (!strcmp(firstarg->s_name, "-ascii"))
        {
            binary = 0;
            argc--; argv++;
        }
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
        else break;
    }

    if (argc)
    {
        pd_error(x,
"usage: pd~ [-sr #] [-ninsig #] [-noutsig #] [-fifo #] [-pddir <>]");
        post(
"... [-scheddir <>]");
    }

    x->x_clock = clock_new(x, (t_method)pd_tilde_tick);
    x->x_insig = (t_sample **)getbytes(ninsig * sizeof(*x->x_insig));
    x->x_outsig = (t_sample **)getbytes(noutsig * sizeof(*x->x_outsig));
    x->x_ninsig = ninsig;
    x->x_noutsig = noutsig;
    x->x_fifo = fifo;
    x->x_sr = sr;
    x->x_pddir = pddir;
    x->x_schedlibdir = scheddir;
    x->x_infd = 0;
    x->x_outfd = 0;
    x->x_outfd = 0;
    x->x_childpid = -1;
    x->x_canvas = canvas_getcurrent();
780
781
    x->x_binbuf = binbuf_new();
    x->x_binary = binary;
782
783
784
785
786
    for (j = 1, g = x->x_insig; j < ninsig; j++, g++)
        inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
    x->x_outlet1 = outlet_new(&x->x_obj, 0);
    for (j = 0, g = x->x_outsig; j < noutsig; j++, g++)
        outlet_new(&x->x_obj, &s_signal);
787
#ifndef _WIN32
788
    signal(SIGPIPE, SIG_IGN);
789
#endif
790
791
792
793
794
795
796
797
798
799
800
    return (x);
}

void pd_tilde_setup(void)
{
    pd_tilde_class = class_new(gensym("pd~"), (t_newmethod)pd_tilde_new,
        (t_method)pd_tilde_free, sizeof(t_pd_tilde), 0, A_GIMME, 0);
    class_addmethod(pd_tilde_class, nullfn, gensym("signal"), 0);
    class_addmethod(pd_tilde_class, (t_method)pd_tilde_dsp, gensym("dsp"), 0);
    class_addmethod(pd_tilde_class, (t_method)pd_tilde_pdtilde, gensym("pd~"), A_GIMME, 0);
    class_addanything(pd_tilde_class, pd_tilde_anything);
801
    post("pd~ version 0.3");
802
803
804
}
#endif

805
/* -------------------------- Max/MSP glue ------------------------- */
806
807
808
809
810
811
812
813
814
815
#ifdef MSP

#define LOTS 10000

static void pd_tilde_tick(t_pd_tilde *x)
{
    int messstart = 0, i, n = 0;
    t_atom vec[LOTS];
    long z1 = 0, z2 = 0;
    /* binbuf_print(b); */
816
    while (!binbuf_getatom(x->x_binbuf, &z1, &z2, vec+n))
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
    if (++n >= LOTS)
        break;
    for (i = 0; i < n; i++)
    {
        if (vec[i].a_type == A_SEMI)
        {
            if (i > messstart + 1)
            {
                void *whom;
                if (vec[messstart].a_type == A_SYM)
                    outlet_anything(x->x_outlet1, vec[messstart].a_w.w_sym,
                        i-messstart-1, vec+(messstart+1));
                else if (vec[messstart].a_type == A_FLOAT && i == messstart+1)
                    outlet_float(x->x_outlet1, vec[messstart].a_w.w_float);
                else if (vec[messstart].a_type == A_LONG && i == messstart+1)
                    outlet_int(x->x_outlet1, vec[messstart].a_w.w_long);
                else outlet_list(x->x_outlet1, gensym("list"),
                    i-messstart, vec+(messstart));
            }
            messstart = i+1;
        }
    }
839
    binbuf_clear(b);
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
}

static void pd_tilde_anything(t_pd_tilde *x, t_symbol *s,
    long ac, t_atom *av)
{
    char msgbuf[MAXPDSTRING], *sp, *ep = msgbuf+MAXPDSTRING;
    if (!x->x_outfd)
        return;
    msgbuf[0] = 0;
    strncpy(msgbuf, s->s_name, MAXPDSTRING);
    msgbuf[MAXPDSTRING-1] = 0;
    sp = msgbuf + strlen(msgbuf);
    while (ac--)
    {
        if (sp < ep-1)
            sp[0] = ' ', sp[1] = 0, sp++;
        if (sp < ep - 80)
        {
            if (av->a_type == A_SYM && strlen(av->a_w.w_sym->s_name) < ep - sp-20)
                strcpy(sp, av->a_w.w_sym->s_name);
            else if (av->a_type == A_LONG)
                sprintf(sp, "%ld" ,av->a_w.w_long);
            else if (av->a_type == A_FLOAT)
                sprintf(sp, "%g" ,av->a_w.w_float);
        }
        sp += strlen(sp);
        av++;
    }
    fprintf(x->x_outfd, "%s;\n", msgbuf);
}

int main()
{       
    t_class *c;

    c = class_new("pd_tilde~", (method)pd_tilde_new, (method)pd_tilde_free, sizeof(t_pd_tilde), (method)0L, A_GIMME, 0);

    class_addmethod(c, (method)pd_tilde_dsp, "dsp", A_CANT, 0);
    class_addmethod(c, (method)pd_tilde_assist, "assist", A_CANT, 0);
    class_addmethod(c, (method)pd_tilde_pdtilde, "pd~", A_GIMME, 0);
    class_addmethod(c, (method)pd_tilde_anything, "anything", A_GIMME, 0);
    class_dspinit(c);

    class_register(CLASS_BOX, c);
    pd_tilde_class = c;
885
    post("pd~ version 0.3");
886
887
888
889
890
891
    return (0);
}

static void *pd_tilde_new(t_symbol *s, long ac, t_atom *av)
{
    int ninsig = 2, noutsig = 2, fifo = 5, j;
892
    t_float sr = sys_getsr();
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
    t_symbol *pddir = gensym("."), *scheddir = gensym(".");
    t_pd_tilde *x;

    if (x = (t_pd_tilde *)object_alloc(pd_tilde_class))
    {
        while (ac > 0 && av[0].a_type == A_SYM)
        {
            char *flag = av[0].a_w.w_sym->s_name;
            if (!strcmp(flag, "-sr") && ac > 1)
            {
                sr = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :
                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));
                ac -= 2; av += 2;
            }
            else if (!strcmp(flag, "-ninsig") && ac > 1)
            {
                ninsig = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :
                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));
                ac -= 2; av += 2;
            }
            else if (!strcmp(flag, "-noutsig") && ac > 1)
            {
                noutsig = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :
                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));
                ac -= 2; av += 2;
            }
            else if (!strcmp(flag, "-fifo") && ac > 1)
            {
                fifo = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float :
                    (av[1].a_type == A_LONG ? av[1].a_w.w_long : 0));
                ac -= 2; av += 2;
            }
            else if (!strcmp(flag, "-pddir") && ac > 1)
            {
                pddir = (av[1].a_type == A_SYM ? av[1].a_w.w_sym : gensym("."));
                ac -= 2; av += 2;
            }
            else if (!strcmp(flag, "-scheddir") && ac > 1)
            {
                scheddir = (av[1].a_type == A_SYM ? av[1].a_w.w_sym : gensym("."));
                ac -= 2; av += 2;
            }
            else break;
        }
        if (ac)
            post("pd~: warning: ignoring extra arguments");
        dsp_setup((t_pxobject *)x, ninsig);
        x->x_outlet1 = outlet_new(&x->x_obj, 0);
        for (j = 0; j < noutsig; j++)
            outlet_new((t_pxobject *)x, "signal");
        x->x_clock = clock_new(x, (method)pd_tilde_tick);
        x->x_insig = (t_sample **)getbytes(ninsig * sizeof(*x->x_insig));
        x->x_outsig = (t_sample **)getbytes(noutsig * sizeof(*x->x_outsig));
        x->x_ninsig = ninsig;
        x->x_noutsig = noutsig;
        x->x_fifo = fifo;
        x->x_sr = sr;
        x->x_pddir = pddir;
        x->x_schedlibdir = scheddir;
        x->x_infd = 0;
        x->x_outfd = 0;
        x->x_outfd = 0;
        x->x_childpid = -1;
956
957
        x->x_binbuf = binbuf_new();
        x->x_binary = binary;
958
959
960
961
962
963
964
965
966
    }
    return (x);
}

void pd_tilde_assist(t_pd_tilde *x, void *b, long m, long a, char *s)
{
}

#endif /* MSP */