pax_global_header 0000666 0000000 0000000 00000000064 14664073227 0014525 g ustar 00root root 0000000 0000000 52 comment=a2d489ba8b24f89439ae74ca9cec5c9ff1010a5d
osc-0.3/ 0000775 0000000 0000000 00000000000 14664073227 0012153 5 ustar 00root root 0000000 0000000 osc-0.3/.editorconfig 0000664 0000000 0000000 00000001143 14664073227 0014627 0 ustar 00root root 0000000 0000000 root = true
[*]
# please only ever use utf-8
charset = utf-8
; C and friends
[**.{c,cpp,h,hh,m,mm}]
# indent 4 spaces, BSD-style
indent_style = space
indent_size = 4
indent_brace_style = BSD
## let git handle proper EOL
#end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 80
#continuation_indent_size = 4
spaces_around_operators = true
; applies only to Makefiles
[makefile]
indent_style = tab
tab_width = 4
[**.am]
indent_style = tab
tab_width = 4
[**.tcl]
indent_style = space
indent_size = 4
#continuation_indent_size = 8
#curly_bracket_next_line = false
osc-0.3/LICENSE.txt 0000664 0000000 0000000 00000001332 14664073227 0013775 0 ustar 00root root 0000000 0000000 OpenSoundControl for Pure data
Copyright (C) 2006-2011 Martin Peach
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, see .
osc-0.3/Makefile 0000664 0000000 0000000 00000001410 14664073227 0013607 0 ustar 00root root 0000000 0000000 # Makefile for osc
lib.name = osc
class.sources = \
packOSC.c \
pipelist.c \
routeOSC.c \
unpackOSC.c
datafiles = \
LICENSE.txt \
README.md \
osc-meta.pd \
packOSC-help.pd \
packOSCstream-help.pd \
packOSCstream.pd \
pipelist-help.pd \
routeOSC-help.pd \
unpackOSC-help.pd \
unpackOSCstream-help.pd \
unpackOSCstream.pd
define forWindows
ldlibs = -lwsock32
endef
# This Makefile is based on the Makefile from pd-lib-builder written by
# Katja Vetter. You can get it from:
# https://github.com/pure-data/pd-lib-builder
PDLIBBUILDER_DIR=pd-lib-builder/
include $(firstword $(wildcard $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder Makefile.pdlibbuilder))
osc-0.3/OSC_timeTag.h 0000664 0000000 0000000 00000006241 14664073227 0014425 0 ustar 00root root 0000000 0000000 /* OSC_timeTag.h: handle OSC timetags
© Martin Peach
* based on:
OSC_timeTag.h: library for manipulating OSC time tags
Matt Wright, 5/29/97
Time tags in OSC have the same format as in NTP: 64 bit fixed point, with the
top 32 bits giving number of seconds sinve midnight 1/1/1900 and the bottom
32 bits giving fractional parts of a second. We represent this by an 8-byte
unsigned long if possible, or else a struct.
NB: On many architectures with 8-byte ints, it's illegal (like maybe a bus error)
to dereference a pointer to an 8 byte int that's not 8-byte aligned.
*/
#ifndef _OSC_timetag_h
#define _OSC_timetag_h
#ifdef _WIN32
# include
#else
# include
#endif /* _WIN32 */
#include
#include
typedef struct _OSCTimeTag
{
uint32_t seconds;
uint32_t fraction;
} OSCTimeTag;
#define SECONDS_FROM_1900_to_1970 2208988800LL /* 17 leap years */
#define TWO_TO_THE_32_OVER_ONE_MILLION 4295LL
static OSCTimeTag OSCTT_Immediately(void);
static OSCTimeTag OSCTT_Infinite(void);
static OSCTimeTag OSCTT_Now(void);
/* The next bit is modified from OSC-timetag.c. */
/*
OSC_timeTag.c: library for manipulating OSC time tags
Matt Wright, 5/29/97
Version 0.2 (9/11/98): cleaned up so no explicit type names in the .c file.
*/
static OSCTimeTag OSCTT_Immediately(void)
{
OSCTimeTag tt;
tt.fraction = 1;
tt.seconds = 0;
return tt;
}
static OSCTimeTag OSCTT_Infinite(void)
{
OSCTimeTag tt;
tt.fraction = 0xffffffffL;
tt.seconds = 0xffffffffL;
return tt;
}
static OSCTimeTag OSCTT_Now(void)
{
OSCTimeTag tt;
#ifdef _WIN32
struct _timeb tb;
_ftime(&tb);
/* First get the seconds right */
tt.seconds = (unsigned)SECONDS_FROM_1900_to_1970 + (unsigned)tb.time;
/* Now get the fractional part. */
tt.fraction = (unsigned)tb.millitm*1000;
#else
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
/* First get the seconds right */
tt.seconds = (unsigned)SECONDS_FROM_1900_to_1970 + (unsigned)tv.tv_sec;
/* Now get the fractional part. */
tt.fraction = (unsigned)tv.tv_usec;
#endif
tt.fraction *= (unsigned)TWO_TO_THE_32_OVER_ONE_MILLION; /* convert usec to 32-bit fraction of 1 sec */
return tt;
}
/* get offset between two timestamps in ms */
static double OSCTT_getoffsetms(const OSCTimeTag a, const OSCTimeTag reference)
{
const double fract2ms = 1000./4294967296.;
double d_sec = ((double)a.seconds -(double)reference.seconds);
double d_msec = ((double)a.fraction-(double)reference.fraction) * fract2ms;
return d_sec * 1000. + d_msec;
}
static OSCTimeTag OSCTT_offsetms(const OSCTimeTag org, double msec_offset)
{
OSCTimeTag tt;
const double fract2ms = 1000./4294967296.;
const double ms2fract = 4294967296./1000.;
double secs_off = floor(msec_offset*0.001);
msec_offset -= secs_off*1000.;
int64_t sec = org.seconds + (int64_t)secs_off;
int64_t fract = (int64_t)(((double)org.fraction * fract2ms + msec_offset)*ms2fract);
sec += (fract>>32);
fract %= 0xFFFFFFFF;
tt.seconds = sec%0xFFFFFFFF;
tt.fraction = fract%0xFFFFFFFF;
return tt;
}
#endif // _OSC_timetag_h
/* end of OSC_timetag.h */
osc-0.3/README.md 0000664 0000000 0000000 00000002403 14664073227 0013431 0 ustar 00root root 0000000 0000000 OpenSoundControl (OSC) for Pd
=============================
A collection of Pd object classes for OSC-messages.
These objects only convert between Pd-messages and OSC-messages (binary format),
so you will need a separate set of objects that implement the transport
(OSI-Layer 4), for instance [udpsend]/[udpreceive] for sending OSC over UDP.
Author: Martin Peach
## Object classes
- **[packOSC]**
convert a Pd-message to an OSC (binary) message
(useful if you want to transmit OSC over UDP or other protocols that
have the concept of variable length packets)
- **[unpackOSC]**
convert an OSC (binary) message to a Pd-message
(useful if you want to transmit OSC over UDP or other protocols that
have the concept of variable length packets)
- **[routeOSC]**
route OSC-like Pd-messages according to the first element in the path
- **[pipelist]**
delay lists (useful if you want to respect timestamps)
- **[packOSCstream]**
convert a Pd-message to an OSC (binary) message suitable for streaming transport
(useful if you want to transmit OSC over TCP/IP or a serial line)
- **[unpackOSCstream]**
convert an OSC (binary) message suitable for streaming transport to a Pd-message
(useful if you want to transmit OSC over TCP/IP or a serial line)
osc-0.3/osc-meta.pd 0000664 0000000 0000000 00000000421 14664073227 0014205 0 ustar 00root root 0000000 0000000 #N canvas 18 240 200 200 10;
#N canvas 100 78 356 167 META 0;
#X text 10 52 VERSION 0.3;
#X text 9 77 AUTHOR Martin Peach;
#X text 11 10 NAME osc;
#X text 9 102 LICENSE GNU GPL v2+;
#X text 10 30 DESCRIPTION classes for dealing with OSC messages;
#X restore 10 10 pd META;
osc-0.3/packOSC-help.pd 0000664 0000000 0000000 00000010675 14664073227 0014722 0 ustar 00root root 0000000 0000000 #N canvas 201 81 1158 759 12;
#X obj 491 524 cnv 15 100 40 empty empty empty 20 12 0 14 #00fc04 #404040 0;
#X obj 520 638 udpsend;
#X msg 513 611 disconnect;
#X msg 491 589 connect 127.0.0.1 9997;
#X obj 520 537 packOSC;
#X obj 520 673 tgl 15 0 empty empty 1=connected 20 8 0 8 #00fc04 #f8fc00 #000000 0 1;
#X msg 30 27 send /test/one/two/three zz 88 T;
#X msg 76 73 send /test 1 2 3;
#X msg 53 50 send /west 35;
#X msg 122 119 send /*/left 22;
#X msg 147 144 send /?est/ 1;
#X text 453 73 packOSC is like sendOSC except that it outputs a list of floats instead of directly connecting to the network;
#X text 263 27 send a type-guessed message;
#X obj 705 631 routeOSC;
#X text 626 630 see also:;
#X msg 508 505 typetags \$1;
#X obj 508 487 tgl 15 0 empty empty empty 17 7 0 10 #f8fc00 #fc0400 #000000 0 1;
#X text 604 504 typetags are on by default;
#X text 738 167 Useable types are:;
#X text 738 185 i: 32 bit integer;
#X text 738 203 f: 32-bit float;
#X text 738 221 s: string;
#X text 519 167 send a type-forced message;
#X msg 170 167 sendtyped /test/one/two/three sis zz 88 T;
#X msg 193 190 sendtyped /left/right TTiTIFNfisf 1.1 2.1 3.1 4.1 5.1;
#X text 738 239 T: true (no argument);
#X text 738 257 F: false (no argument);
#X text 738 275 I: infinitum (no argument);
#X text 738 292 N: Nil (no argument);
#X msg 457 454 bufsize 100;
#X text 556 454 set buffer size (default is 64000 bytes);
#X msg 239 236 prefix /test;
#X text 346 235 set the OSC path prefix for subsequent messages;
#X msg 99 96 /left one two;
#X msg 216 213 /right 88;
#X text 299 213 'send' prefix is not required;
#X msg 215 269 [;
#X msg 169 270 ];
#X text 63 271 close bundle;
#X msg 317 314 timetagoffset 0;
#X msg 340 337 timetagoffset -1;
#X text 479 314 send current time as timetag;
#X text 295 289 (timetags are sent in bundle messages only);
#X obj 169 246 t b a b;
#X msg 70 226 /test 5 6 7;
#X text 483 337 immediate time tag (default);
#X floatatom 566 566 5 0 0 0 - - - 0;
#X text 616 566 bundle depth;
#X obj 685 589 cnv 15 380 30 empty empty empty 20 12 0 14 #14e814 #404040 0;
#X text 779 630 and http://opensoundcontrol.org/;
#X msg 363 360 timetagoffset 1e+06;
#X text 689 596 <- First open routeOSC-help \, then connect;
#X text 248 270 open a bundle (and generate time tag);
#X obj 608 658 packOSCstream;
#X text 573 657 see:;
#X text 724 658 for a way to send OSC over TCP or serial connections.;
#X text 526 359 current time plus 1 second (delay is in microseconds);
#X text 738 308 b: blob (a list of bytes \, or floats on [0..255]);
#X text 902 421 send a blob;
#X msg 433 414 sendtyped /left b 0 1 2 3 124 125 126 127 128 129 255 254 253 252 -14 -15 -16 17 18 19 20 21 22 23 24 25 26 27 28;
#N canvas 496 61 548 344 META 0;
#X text 12 185 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.;
#X text 12 25 LICENSE GPL v2 or later;
#X text 12 165 AUTHOR Martin Peach;
#X text 12 5 KEYWORDS control network;
#X text 12 45 DESCRIPTION packOSC is like sendOSC except it outputs a list of floats instead of directly connecting to the network;
#X text 12 85 INLET_0 anything send sendtyped prefix timetagoffset bufsize typetags;
#X text 12 125 OUTLET_0 anything;
#X text 12 145 OUTLET_1 float;
#X restore 1022 685 pd META;
#X msg 310 125 sendtyped /a/midi/message mm 0 144 60 64 0 144 62 32;
#X obj 365 620 print >>;
#X text 738 323 m: MIDI (4 bytes per MIDI message:;
#X text 761 338 Port \, Status \, Param1 \, Param2;
#X text 85 630 2009-2016 Martin Peach;
#X obj 365 596 spigot;
#X obj 410 563 tgl 15 0 empty empty raw 17 7 0 12 #f8fc00 #fc0400 #000000 0 1;
#X msg 388 385 usepdtime \$1;
#X obj 484 385 tgl 20 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000 0 1;
#X text 516 378 Use Pd logical time (default) or system time. Setting to 1 \, re-syncs Pd's time to the system time.;
#X connect 1 0 5 0;
#X connect 2 0 1 0;
#X connect 3 0 1 0;
#X connect 4 0 1 0;
#X connect 4 0 66 0;
#X connect 4 1 46 0;
#X connect 6 0 4 0;
#X connect 7 0 4 0;
#X connect 8 0 4 0;
#X connect 9 0 4 0;
#X connect 10 0 4 0;
#X connect 15 0 4 0;
#X connect 16 0 15 0;
#X connect 23 0 4 0;
#X connect 24 0 4 0;
#X connect 29 0 4 0;
#X connect 31 0 4 0;
#X connect 33 0 4 0;
#X connect 34 0 4 0;
#X connect 36 0 4 0;
#X connect 37 0 4 0;
#X connect 39 0 4 0;
#X connect 40 0 4 0;
#X connect 43 0 37 0;
#X connect 43 1 4 0;
#X connect 43 2 36 0;
#X connect 44 0 43 0;
#X connect 50 0 4 0;
#X connect 59 0 4 0;
#X connect 61 0 4 0;
#X connect 66 0 62 0;
#X connect 67 0 66 1;
#X connect 68 0 4 0;
#X connect 69 0 68 0;
osc-0.3/packOSC.c 0000664 0000000 0000000 00000140254 14664073227 0013610 0 ustar 00root root 0000000 0000000 /* packOSC is like sendOSC but outputs a list of floats which are the bytes making up the OSC packet. */
/* This allows for the separation of the protocol and its transport. */
/* Started by Martin Peach 20060403 */
/* 20060425 version independent of libOSC */
/* 20070620 added packOSC_path and packOSC_anything methods by zmoelnig */
/* 20160713 added 'm' typetag for MIDI messages */
/* packOSC.c makes extensive use of code from OSC-client.c and sendOSC.c */
/* as well as some from OSC-timetag.c. These files have the following header: */
/*
Written by Matt Wright, The Center for New Music and Audio Technologies,
University of California, Berkeley. Copyright (c) 1996,97,98,99,2000,01,02,03
The Regents of the University of California (Regents).
Permission to use, copy, modify, distribute, and distribute modified versions
of this software and its documentation without fee and without a signed
licensing agreement, is hereby granted, provided that the above copyright
notice, this paragraph and the following two paragraphs appear in all copies,
modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The OSC webpage is http://opensoundcontrol.org/
*/
//#define DEBUG 1
#define SC_BUFFER_SIZE 64000
#include "packingOSC.h"
#include "OSC_timeTag.h"
/* This is from OSC-client.h :*/
/*
OSC-client.h: library for constructing OpenSoundControl messages.
Derived from SynthControl.h
Author: Matt Wright
Version 0.1: 6/13/97
Version 0.2: 7/21/2000: Support for type-tagged messages
General notes:
This library abstracts away the data format for the OpenSoundControl
protocol. Users of this library can construct OpenSoundControl packets
with a function call interface instead of knowing how to lay out the bits.
All issues of memory allocation are deferred to the user of this library.
There are two data structures that the user must allocate. The first
is the actual buffer that the message will be written into. This buffer
can be any size, but if it's too small there's a possibility that it
will become overfull. The other data structure is called an OSCbuf,
and it holds all the state used by the library as it's constructing
a buffer.
All procedures that have the possibility of an error condition return int,
with 0 indicating no error and nonzero indicating an error. The variable
OSC_errorMessage will be set to point to a string containing an error
message explaining what the problem is.
*/
/* Return the time tag 0x0000000000000001, indicating to the receiving device
that it should process the message immediately. */
/* these are globals so other packOSCs will be in lockstep */
int packOSCs;
/* packOSCLogicalStartTime is Pd's count of DSP ticks. Use clock_gettimesince() to measure intervals in milliseconds from here */
/* Don't ever manipulate the data in the OSCbuf struct directly. (It's
declared here in the header file only so your program will be able to
declare variables of type OSCbuf and have the right amount of memory
be allocated.) */
typedef struct OSCbuf_struct
{
char *buffer; /* The buffer to hold the OSC packet */
size_t size; /* Size of the buffer */
char *bufptr; /* Current position as we fill the buffer */
int state; /* State of partially-constructed message */
uint32_t *thisMsgSize; /* Pointer to count field before */
/* currently-being-written message */
uint32_t *prevCounts[MAX_BUNDLE_NESTING]; /* Pointers to count */
/* field before each currently open bundle */
int bundleDepth; /* How many sub-sub-bundles are we in now? */
char *typeStringPtr; /* This pointer advances through the type */
/* tag string as you add arguments. */
int gettingFirstUntypedArg; /* nonzero if this message doesn't have */
/* a type tag and we're waiting for the 1st arg */
} OSCbuf;
typedef struct
{
enum {INT_osc, FLOAT_osc, STRING_osc, BLOB_osc, NOTYPE_osc} type;
union
{
int i;
float f;
const char *s;
} datum;
} typedArg;
/* Here are the possible values of the state field: */
#define EMPTY 0 /* Nothing written to packet yet */
#define ONE_MSG_ARGS 1 /* Packet has a single message; gathering arguments */
#define NEED_COUNT 2 /* Just opened a bundle; must write message name or */
/* open another bundle */
#define GET_ARGS 3 /* Getting arguments to a message. If we see a message */
/* name or a bundle open/close then the current message */
/* will end. */
#define DONE 4 /* All open bundles have been closed, so can't write */
/* anything else */
static int OSC_strlen(const char *s);
static int OSC_padString(char *dest, const char *str);
static int OSC_padStringWithAnExtraStupidComma(char *dest, const char *str);
static int OSC_WriteStringPadding(char *dest, int i);
static int OSC_WriteBlobPadding(char *dest, int i);
static int CheckTypeTag(void *x, const OSCbuf *buf, char expectedType);
/* Initialize the given OSCbuf. The user of this module must pass in the
block of memory that this OSCbuf will use for a buffer, and the number of
bytes in that block. (It's the user's job to allocate the memory because
you do it differently in different systems.) */
static void OSC_initBuffer(OSCbuf *buf, size_t size, char *byteArray);
/* Reset the given OSCbuf. Do this after you send out the contents of
the buffer and want to start writing new data into it. */
static void OSC_resetBuffer(OSCbuf *buf);
/* Is the buffer empty? (I.e., would it be stupid to send the buffer
contents to the synth?) */
static int OSC_isBufferEmpty(const OSCbuf *buf);
/* How much space is left in the buffer? */
static size_t OSC_freeSpaceInBuffer(const OSCbuf *buf);
/* Does the buffer contain a valid OSC packet? (Returns nonzero if yes.) */
static int OSC_isBufferDone(const OSCbuf *buf);
/* When you're ready to send out the buffer (i.e., when OSC_isBufferDone()
returns true), call these two procedures to get the OSC packet that's been
assembled and its size in bytes. (And then call OSC_resetBuffer() if you
want to re-use this OSCbuf for the next packet.) */
static char *OSC_getPacket(const OSCbuf *buf);
static int OSC_packetSize(const OSCbuf *buf);
static int OSC_CheckOverflow(void *x, const OSCbuf *buf, size_t bytesNeeded);
/* Here's the basic model for building up OSC messages in an OSCbuf:
- Make sure the OSCbuf has been initialized with OSC_initBuffer().
- To open a bundle, call OSC_openBundle(). You can then write
messages or open new bundles within the bundle you opened.
Call OSC_closeBundle() to close the bundle. Note that a packet
does not have to have a bundle; it can instead consist of just a
single message.
- For each message you want to send:
- Call OSC_writeAddress() with the name of your message. (In
addition to writing your message name into the buffer, this
procedure will also leave space for the size count of this message.)
- Alternately, call OSC_writeAddressAndTypes() with the name of
your message and with a type string listing the types of all the
arguments you will be putting in this message.
- Now write each of the arguments into the buffer, by calling one of:
OSC_writeFloatArg()
OSC_writeIntArg()
OSC_writeStringArg()
OSC_writeNullArg()
- Now your message is complete; you can send out the buffer or you can
add another message to it.
*/
static int OSC_openBundle(void *x, OSCbuf *buf, OSCTimeTag tt);
static int OSC_closeBundle(void *x, OSCbuf *buf);
static int OSC_writeAddress(void *x, OSCbuf *buf, char *name);
static int OSC_writeAddressAndTypes(void *x, OSCbuf *buf, char *name, char *types);
static int OSC_writeFloatArg(void *x, OSCbuf *buf, float arg);
static int OSC_writeIntArg(void *x, OSCbuf *buf, uint32_t arg);
static int OSC_writeBlobArg(void *x, OSCbuf *buf, typedArg *arg, size_t nArgs);
static int OSC_writeStringArg(void *x, OSCbuf *buf, const char *arg);
static int OSC_writeNullArg(void *x, OSCbuf *buf, char type);
/* How many bytes will be needed in the OSC format to hold the given
string? The length of the string, plus the null char, plus any padding
needed for 4-byte alignment. */
static int OSC_effectiveStringLength(const char *string);
static t_class *packOSC_class;
typedef struct _packOSC
{
t_object x_obj;
int x_typetags; /* typetag flag */
int x_timeTagOffset;
int x_bundle; /* bundle open flag */
OSCbuf x_oscbuf[1]; /* OSCbuffer */
t_outlet *x_bdpthout; /* bundle-depth floatoutlet */
t_outlet *x_listout; /* OSC packet list ouput */
size_t x_buflength; /* number of elements in x_bufferForOSCbuf and x_bufferForOSClist */
char *x_bufferForOSCbuf; /*[SC_BUFFER_SIZE];*/
t_atom *x_bufferForOSClist; /*[SC_BUFFER_SIZE];*/
const char *x_prefix;
int x_reentry_count;
int x_use_pd_time;
OSCTimeTag x_pd_timetag;
double x_pd_timeref;
} t_packOSC;
static void *packOSC_new(void);
static void packOSC_path(t_packOSC *x, t_symbol*s);
static void packOSC_openbundle(t_packOSC *x);
static void packOSC_closebundle(t_packOSC *x);
static void packOSC_settypetags(t_packOSC *x, t_floatarg f);
static void packOSC_setbufsize(t_packOSC *x, t_floatarg f);
static void packOSC_usepdtime(t_packOSC *x, t_floatarg f);
static void packOSC_setTimeTagOffset(t_packOSC *x, t_floatarg f);
static void packOSC_sendtyped(t_packOSC *x, t_symbol *s, int argc, t_atom *argv);
static void packOSC_send_type_forced(t_packOSC *x, t_symbol *s, int argc, t_atom *argv);
static void packOSC_send(t_packOSC *x, t_symbol *s, int argc, t_atom *argv);
static void packOSC_anything(t_packOSC *x, t_symbol *s, int argc, t_atom *argv);
static void packOSC_free(t_packOSC *x);
void packOSC_setup(void);
static typedArg packOSC_parseatom(t_atom *a, t_packOSC *x);
static typedArg packOSC_packMIDI(t_atom *a, t_packOSC *x);
static typedArg packOSC_forceatom(t_atom *a, char ctype, t_packOSC *x);
static typedArg packOSC_blob(t_atom *a, t_packOSC *x);
static int packOSC_writetypedmessage(t_packOSC *x, OSCbuf *buf, char *messageName, int numArgs, typedArg *args, char *typeStr);
static int packOSC_writemessage(t_packOSC *x, OSCbuf *buf, char *messageName, int numArgs, typedArg *args);
static void packOSC_sendbuffer(t_packOSC *x);
static void *packOSC_new(void)
{
t_packOSC *x = (t_packOSC *)pd_new(packOSC_class);
x->x_typetags = 1; /* set typetags to 1 by default */
x->x_bundle = 0; /* bundle is closed */
x->x_buflength = SC_BUFFER_SIZE;
x->x_bufferForOSCbuf = (char *)getbytes(sizeof(char)*x->x_buflength);
if(x->x_bufferForOSCbuf == NULL)
{
pd_error(x, "packOSC: unable to allocate %lu bytes for x_bufferForOSCbuf", (long)(sizeof(char)*x->x_buflength));
goto fail;
}
x->x_bufferForOSClist = (t_atom *)getbytes(sizeof(t_atom)*x->x_buflength);
if(x->x_bufferForOSClist == NULL)
{
pd_error(x, "packOSC: unable to allocate %lu bytes for x_bufferForOSClist", (long)(sizeof(t_atom)*x->x_buflength));
goto fail;
}
OSC_initBuffer(x->x_oscbuf, x->x_buflength, x->x_bufferForOSCbuf);
x->x_listout = outlet_new(&x->x_obj, &s_list);
x->x_bdpthout = outlet_new(&x->x_obj, &s_float);
x->x_timeTagOffset = -1; /* immediately */
x->x_reentry_count = 0;
packOSC_usepdtime(x, 1.);
return (x);
fail:
if(x->x_bufferForOSCbuf != NULL) freebytes(x->x_bufferForOSCbuf, (long)(sizeof(char)*x->x_buflength));
if(x->x_bufferForOSClist != NULL) freebytes(x->x_bufferForOSClist, (long)(sizeof(char)*x->x_buflength));
return NULL;
}
static void packOSC_path(t_packOSC *x, t_symbol*s)
{
/* Set a default prefix to the OSC path */
if(s == gensym(""))
{
x->x_prefix = 0;
return;
}
if ((*s->s_name) != '/')
{
pd_error(x, "packOSC: bad path: '%s'", s->s_name);
return;
}
x->x_prefix = s->s_name;
}
static void packOSC_openbundle(t_packOSC *x)
{
int result;
t_float bundledepth=(t_float)x->x_oscbuf->bundleDepth;
OSCTimeTag tt = OSCTT_Immediately();
if (x->x_timeTagOffset == -1) {
/* immediately */
} else {
double delta = x->x_timeTagOffset*0.001;
if (x->x_use_pd_time) {
delta += clock_gettimesince(x->x_pd_timeref);
tt = OSCTT_offsetms(x->x_pd_timetag, delta);
} else {
tt = OSCTT_offsetms(OSCTT_Now(), delta);
}
}
result = OSC_openBundle(x, x->x_oscbuf, tt);
if (result != 0)
{ /* reset the buffer */
OSC_initBuffer(x->x_oscbuf, x->x_buflength, x->x_bufferForOSCbuf);
x->x_bundle = 0;
}
else x->x_bundle = 1;
outlet_float(x->x_bdpthout, bundledepth);
}
static void packOSC_closebundle(t_packOSC *x)
{
t_float bundledepth=(t_float)x->x_oscbuf->bundleDepth;
if (OSC_closeBundle(x, x->x_oscbuf))
{
pd_error(x, "packOSC: Problem closing bundle.");
return;
}
outlet_float(x->x_bdpthout, bundledepth);
/* in bundle mode we send when bundle is closed */
if((!OSC_isBufferEmpty(x->x_oscbuf)) && OSC_isBufferDone(x->x_oscbuf))
{
x->x_bundle = 0; /* call this before _sendbuffer() to be ready for recursive calls */
packOSC_sendbuffer(x);
return;
}
}
static void packOSC_settypetags(t_packOSC *x, t_floatarg f)
{
x->x_typetags = (f != 0)?1:0;
logpost(x, 3, "packOSC: setting typetags %d", x->x_typetags);
}
static void packOSC_setbufsize(t_packOSC *x, t_floatarg f)
{
if (x->x_bufferForOSCbuf != NULL) freebytes((void *)x->x_bufferForOSCbuf, sizeof(char)*x->x_buflength);
if (x->x_bufferForOSClist != NULL) freebytes((void *)x->x_bufferForOSClist, sizeof(t_atom)*x->x_buflength);
logpost(x, 3, "packOSC: bufsize arg is %f (%lu)", f, (long)f);
x->x_buflength = (long)f;
x->x_bufferForOSCbuf = (char *)getbytes(sizeof(char)*x->x_buflength);
if(x->x_bufferForOSCbuf == NULL)
pd_error(x, "packOSC unable to allocate %lu bytes for x_bufferForOSCbuf", (long)(sizeof(char)*x->x_buflength));
x->x_bufferForOSClist = (t_atom *)getbytes(sizeof(t_atom)*x->x_buflength);
if(x->x_bufferForOSClist == NULL)
pd_error(x, "packOSC unable to allocate %lu bytes for x_bufferForOSClist", (long)(sizeof(t_atom)*x->x_buflength));
OSC_initBuffer(x->x_oscbuf, x->x_buflength, x->x_bufferForOSCbuf);
logpost(x, 3, "packOSC: bufsize is now %ld", (long unsigned int)x->x_buflength);
}
static void packOSC_usepdtime(t_packOSC *x, t_floatarg f)
{
x->x_use_pd_time = (int)f;
if(x->x_use_pd_time) {
x->x_pd_timetag = OSCTT_Now();
x->x_pd_timeref = clock_getlogicaltime();
} else {
x->x_pd_timetag.seconds = x->x_pd_timetag.fraction = 0;
x->x_pd_timeref = 0;
}
}
static void packOSC_setTimeTagOffset(t_packOSC *x, t_floatarg f)
{
x->x_timeTagOffset = (int)f;
}
/* this is the real and only sending routine now, for both typed and */
/* undtyped mode. */
static void packOSC_sendtyped(t_packOSC *x, t_symbol *s, int argc, t_atom *argv)
{
char messageName[MAXPDSTRING];
unsigned int nTypeTags = 0, typeStrTotalSize = 0;
unsigned int argsSize = sizeof(typedArg)*argc;
char* typeStr = NULL; /* might not be used */
typedArg* args = (typedArg*)getbytes(argsSize);
unsigned int i, nTagsWithData, nArgs, blobCount;
unsigned int m, tagIndex, typedArgIndex, argvIndex;
char c;
(void)s;
debugprint("*** packOSC_sendtyped bundle %d reentry %d\n", x->x_bundle, x->x_reentry_count);
x->x_reentry_count++;
if (args == NULL)
{
pd_error(x, "packOSC: unable to allocate %lu bytes for args", (long)argsSize);
return;
}
messageName[0] = '\0'; /* empty */
if(x->x_prefix) /* if there is a prefix, prefix it to the path */
{
size_t len = strlen(x->x_prefix);
if(len >= MAXPDSTRING)
len = MAXPDSTRING-1;
strncpy(messageName, x->x_prefix, MAXPDSTRING);
atom_string(&argv[0], messageName+len, (unsigned)(MAXPDSTRING-len));
}
else
atom_string(&argv[0], messageName, MAXPDSTRING); /* the OSC address string */
if (x->x_typetags & 2)
{ /* second arg is typestring */
/* we need to find out how long the type string is before we copy it*/
nTypeTags = (unsigned int)strlen(atom_getsymbol(&argv[1])->s_name);
typeStrTotalSize = nTypeTags + 2;
typeStr = (char*)getzbytes(typeStrTotalSize);
if (typeStr == NULL)
{
pd_error(x, "packOSC: unable to allocate %u bytes for typeStr", nTypeTags);
return;
}
typeStr[0] = ',';
atom_string(&argv[1], &typeStr[1], typeStrTotalSize);
debugprint("packOSC_sendtyped typeStr: %s, nTypeTags %u\n", typeStr, nTypeTags);
nArgs = argc-2;
for (m = nTagsWithData = blobCount = 0; m < nTypeTags; ++m)
{
debugprint("packOSC_sendtyped typeStr[%d] %c\n", m+1, typeStr[m+1]);
if ((c = typeStr[m+1]) == 0) break;
if (!(c == 'T' || c == 'F' || c == 'N' || c == 'I'))
{
++nTagsWithData; /* anything other than these tags have at least one data byte */
if (c == 'm') nTagsWithData += 3; // MIDI tag should have four data bytes
/*
OSC-blob
An int32 size count, followed by that many 8-bit bytes of arbitrary binary data,
followed by 0-3 additional zero bytes to make the total number of bits a multiple of 32.
*/
if (c == 'b') blobCount++; /* b probably has more than one byte, so set a flag */
}
}
if (((blobCount == 0)&&(nTagsWithData != nArgs)) || ((blobCount != 0)&&(nTagsWithData > nArgs)))
{
pd_error(x, "packOSC: Tags count %d doesn't match argument count %d", nTagsWithData, nArgs);
goto cleanup;
}
if (blobCount > 1)
{
pd_error(x, "packOSC: Only one blob per packet at the moment...");
goto cleanup;
}
for (tagIndex = typedArgIndex = 0, argvIndex = 2; tagIndex < m; ++tagIndex) /* m is the number of tags */
{
c = typeStr[tagIndex+1];
if (c == 'b')
{ /* A blob has to be the last item, until we get more elaborate. */
if (tagIndex != m-1)
{
pd_error(x, "packOSC: Since I don't know how big the blob is, Blob must be the last item in the list");
goto cleanup;
}
/* Pack all the remaining arguments as a blob */
for (; typedArgIndex < nArgs; ++typedArgIndex, ++argvIndex)
{
debugprint("packOSC_blob %d:\n", nArgs);
args[typedArgIndex] = packOSC_blob(&argv[argvIndex], x);
/* Make sure it was blobbable */
if (args[typedArgIndex].type != BLOB_osc) goto cleanup;
}
}
else if (!(c == 'T' || c == 'F' || c == 'N' || c == 'I')) /* not no data */
{
if (c == 'm')
{ // pack the next four arguments into one int
args[typedArgIndex++] = packOSC_packMIDI(&argv[argvIndex], x);
argvIndex += 4;
}
else args[typedArgIndex++] = packOSC_forceatom(&argv[argvIndex++], c, x);
}
}
//if(packOSC_writetypedmessage(x, x->x_oscbuf, messageName, nArgs, args, typeStr))
if(packOSC_writetypedmessage(x, x->x_oscbuf, messageName, typedArgIndex, args, typeStr))
{
pd_error(x, "packOSC: usage error, packOSC_writetypedmessage failed.");
goto cleanup;
}
}
else
{
for (i = 0; i < (unsigned)(argc-1); i++)
{
args[i] = packOSC_parseatom(&argv[i+1], x);
#if DEBUG
switch (args[i].type)
{
case INT_osc:
debugprint("packOSC: cell-cont: %d\n", args[i].datum.i);
break;
case FLOAT_osc:
debugprint("packOSC: cell-cont: %f\n", args[i].datum.f);
break;
case STRING_osc:
debugprint("packOSC: cell-cont: %s\n", args[i].datum.s);
break;
case BLOB_osc:
debugprint("packOSC: blob\n");
break;
case NOTYPE_osc:
debugprint("packOSC: unknown type\n");
break;
}
debugprint("packOSC: type-id: %d\n", args[i].type);
#endif
}
if(packOSC_writemessage(x, x->x_oscbuf, messageName, i, args))
{
pd_error(x, "packOSC: usage error, packOSC_writemessage failed.");
goto cleanup;
}
}
if(!x->x_bundle)
{
packOSC_sendbuffer(x);
}
cleanup:
if (typeStr != NULL) freebytes(typeStr, typeStrTotalSize);
if (args != NULL) freebytes(args, argsSize);
x->x_reentry_count--;
}
static void packOSC_send_type_forced(t_packOSC *x, t_symbol *s, int argc, t_atom *argv)
{ /* typetags are the argument following the OSC path */
x->x_typetags |= 2;/* tell packOSC_sendtyped to use the specified typetags... */
packOSC_sendtyped(x, s, argc, argv);
x->x_typetags &= ~2;/* ...this time only */
}
static void packOSC_send(t_packOSC *x, t_symbol *s, int argc, t_atom *argv)
{
if(!argc)
{
pd_error(x, "packOSC: not sending empty message.");
return;
}
packOSC_sendtyped(x, s, argc, argv);
}
static void packOSC_anything(t_packOSC *x, t_symbol *s, int argc, t_atom *argv)
{
/* If the message starts with '/', assume it's an OSC path and send it */
t_atom*ap = 0;
if ((*s->s_name)!='/')
{
pd_error(x, "packOSC: bad path: '%s'", s->s_name);
return;
}
ap = (t_atom*)getbytes((argc+1)*sizeof(t_atom));
SETSYMBOL(ap, s);
memcpy(ap+1, argv, argc * sizeof(t_atom));
packOSC_send(x, gensym("send"), argc+1, ap);
freebytes(ap, (argc+1)*sizeof(t_atom));
}
static void packOSC_free(t_packOSC *x)
{
if (x->x_bufferForOSCbuf != NULL) freebytes((void *)x->x_bufferForOSCbuf, sizeof(char)*x->x_buflength);
if (x->x_bufferForOSClist != NULL) freebytes((void *)x->x_bufferForOSClist, sizeof(t_atom)*x->x_buflength);
}
void packOSC_setup(void)
{
packOSC_class = class_new(gensym("packOSC"), (t_newmethod)packOSC_new,
(t_method)packOSC_free,
sizeof(t_packOSC), 0, A_DEFFLOAT, 0);
class_addmethod(packOSC_class, (t_method)packOSC_path,
gensym("prefix"), A_DEFSYM, 0);
class_addmethod(packOSC_class, (t_method)packOSC_settypetags,
gensym("typetags"), A_DEFFLOAT, 0);
class_addmethod(packOSC_class, (t_method)packOSC_setbufsize,
gensym("bufsize"), A_DEFFLOAT, 0);
class_addmethod(packOSC_class, (t_method)packOSC_usepdtime,
gensym("usepdtime"), A_FLOAT, 0);
class_addmethod(packOSC_class, (t_method)packOSC_setTimeTagOffset,
gensym("timetagoffset"), A_DEFFLOAT, 0);
class_addmethod(packOSC_class, (t_method)packOSC_send,
gensym("send"), A_GIMME, 0);
class_addmethod(packOSC_class, (t_method)packOSC_send,
gensym("senduntyped"), A_GIMME, 0);
class_addmethod(packOSC_class, (t_method)packOSC_send_type_forced,
gensym("sendtyped"), A_GIMME, 0);
class_addmethod(packOSC_class, (t_method)packOSC_openbundle,
gensym("["), 0, 0);
class_addmethod(packOSC_class, (t_method)packOSC_closebundle,
gensym("]"), 0, 0);
class_addanything(packOSC_class, (t_method)packOSC_anything);
}
static typedArg packOSC_parseatom(t_atom *a, t_packOSC *x)
{
typedArg returnVal;
t_float f;
int i;
t_symbol s;
char buf[MAXPDSTRING];
atom_string(a, buf, MAXPDSTRING);
debugprint("packOSC: atom type %d (%s)\n", a->a_type, buf);
/* It might be an int, a float, or a string */
switch (a->a_type)
{
case A_FLOAT:
f = atom_getfloat(a);
i = atom_getint(a);
if (f == (t_float)i)
{ /* assume that if the int and float are the same, it's an int */
returnVal.type = INT_osc;
returnVal.datum.i = i;
}
else
{
returnVal.type = FLOAT_osc;
returnVal.datum.f = f;
}
return returnVal;
case A_SYMBOL:
s = *atom_getsymbol(a);
returnVal.type = STRING_osc;
returnVal.datum.s = s.s_name;
return returnVal;
default:
atom_string(a, buf, MAXPDSTRING);
pd_error(x, "packOSC: atom type %d not implemented (%s)", a->a_type, buf);
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
return returnVal;
}
}
static typedArg packOSC_blob(t_atom *a, t_packOSC *x)
{ /* ctype is one of i,f,s,T,F,N,I*/
typedArg returnVal;
t_float f;
int i;
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
/* the atoms must all be bytesl */
if(a->a_type != A_FLOAT)
{
pd_error(x, "packOSC_blob: all values must be floats");
return returnVal;
}
f = atom_getfloat(a);
i = (int)f;
if (i != f)
{
pd_error(x, "packOSC_blob: all values must be whole numbers");
return returnVal;
}
if ((i < -128) || (i > 255))
{
pd_error(x, "packOSC_blob: all values must be bytes");
return returnVal;
}
returnVal.type = BLOB_osc;
returnVal.datum.i = i;
return returnVal;
}
static typedArg packOSC_packMIDI(t_atom *a, t_packOSC *x)
{ /* pack four bytes at a into one int32 */
int i;
typedArg returnVal;
int m[4];
for (i = 0; i < 4; ++i, ++a)
{
#if DEBUG
do {
char buf[MAXPDSTRING];
atom_string(a, buf, MAXPDSTRING);
debugprint("packOSC: atom type %d (%s)\n", a->a_type, buf);
} while (0);
#endif
if ((a->a_type != A_FLOAT))
{
pd_error(x, "packOSC: MIDI parameters must be floats");
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
return returnVal;
}
m[i] = atom_getint(a);
debugprint("packOSC_packMIDI: float to integer %d\n", m[i]);
if ((i < 2) && (m[i] != (m[i] & 0x0FF)))
{
pd_error(x, "packOSC: MIDI parameters must be less than 256");
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
return returnVal;
}
else if ((i > 1) && (m[i] != (m[i] & 0x07F)))
{
pd_error(x, "packOSC: MIDI parameters must be less than 128");
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
return returnVal;
}
}
returnVal.type = INT_osc;
returnVal.datum.i = (m[0]<<24) + (m[1]<<16) + (m[2]<<8) + m[3];
return returnVal;
}
static typedArg packOSC_forceatom(t_atom *a, char ctype, t_packOSC *x)
{ /* ctype is one of i,f,s,T,F,N,I*/
typedArg returnVal;
t_float f;
int i;
t_symbol s;
static char buf[MAXPDSTRING];
#ifdef DEBUG
do {
atom_string(a, buf, MAXPDSTRING);
debugprint("packOSC: atom type %d (%s)\n", a->a_type, buf);
} while(0);
#endif
/* the atom might be a float, or a symbol */
switch (a->a_type)
{
case A_FLOAT:
switch (ctype)
{
case 'i':
returnVal.type = INT_osc;
returnVal.datum.i = atom_getint(a);
debugprint("packOSC_forceatom: float to integer %d\n", returnVal.datum.i);
break;
case 'f':
returnVal.type = FLOAT_osc;
returnVal.datum.f = atom_getfloat(a);
debugprint("packOSC_forceatom: float to float %f\n", returnVal.datum.f);
break;
case 's':
f = atom_getfloat(a);
sprintf(buf, "%f", f);
returnVal.type = STRING_osc;
returnVal.datum.s = buf;
debugprint("packOSC_forceatom: float to string %s\n", returnVal.datum.s);
break;
default:
pd_error(x, "packOSC: unknown OSC type %c", ctype);
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
break;
}
break;
case A_SYMBOL:
s = *atom_getsymbol(a);
switch (ctype)
{
case 'i':
i = atoi(s.s_name);
returnVal.type = INT_osc;
returnVal.datum.i = i;
debugprint("packOSC_forceatom: symbol to integer %d\n", returnVal.datum.i);
break;
case 'f':
f = atof(s.s_name);
returnVal.type = FLOAT_osc;
returnVal.datum.f = f;
debugprint("packOSC_forceatom: symbol to float %f\n", returnVal.datum.f);
break;
case 's':
returnVal.type = STRING_osc;
returnVal.datum.s = s.s_name;
debugprint("packOSC_forceatom: symbol to string %s\n", returnVal.datum.s);
break;
default:
pd_error(x, "packOSC: unknown OSC type %c", ctype);
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
break;
}
break;
default:
atom_string(a, buf, MAXPDSTRING);
pd_error(x, "packOSC: atom type %d not implemented (%s)", a->a_type, buf);
returnVal.type = NOTYPE_osc;
returnVal.datum.s = NULL;
break;
}
return returnVal;
}
static int packOSC_writetypedmessage
(t_packOSC *x, OSCbuf *buf, char *messageName, int numArgs, typedArg *args, char *typeStr)
{
int i, j, returnVal;
debugprint("packOSC_writetypedmessage: messageName %p (%s) typeStr %p (%s)\n",
messageName, messageName, typeStr, typeStr);
returnVal = OSC_writeAddressAndTypes(x, buf, messageName, typeStr);
if (returnVal)
{
pd_error(x, "packOSC: Problem writing address. (%d)", returnVal);
return returnVal;
}
for (j = i = 0; (typeStr[i+1]!= 0) || (j < numArgs); j++, i++)
{
while (typeStr[i+1] == 'T' || typeStr[i+1] == 'F' || typeStr[i+1] == 'I' || typeStr[i+1] == 'N')
{
debugprint("packOSC_writetypedmessage: NULL [%c]\n", typeStr[i+1]);
returnVal = OSC_writeNullArg(x, buf, typeStr[i+1]);
++i;
}
if (j < numArgs)
{
switch (args[j].type)
{
case INT_osc:
debugprint("packOSC_writetypedmessage: int [%d]\n", args[j].datum.i);
returnVal = OSC_writeIntArg(x, buf, args[j].datum.i);
break;
case FLOAT_osc:
debugprint("packOSC_writetypedmessage: float [%f]\n", args[j].datum.f);
returnVal = OSC_writeFloatArg(x, buf, args[j].datum.f);
break;
case STRING_osc:
debugprint("packOSC_writetypedmessage: string [%s]\n", args[j].datum.s);
returnVal = OSC_writeStringArg(x, buf, args[j].datum.s);
break;
case BLOB_osc:
/* write all the blob elements at once */
debugprint("packOSC_writetypedmessage calling OSC_writeBlobArg\n");
return OSC_writeBlobArg(x, buf, &args[j], numArgs-j);
default:
break; /* types with no data */
}
}
}
return returnVal;
}
static int packOSC_writemessage(t_packOSC *x, OSCbuf *buf, char *messageName, int numArgs, typedArg *args)
{
int j, returnVal = 0, numTags;
debugprint("packOSC_writemessage buf %p bufptr %p messageName %s %d args typetags %d\n", buf, buf->bufptr, messageName, numArgs, x->x_typetags);
if (!x->x_typetags)
{
debugprint("packOSC_writemessage calling OSC_writeAddress with x->x_typetags %d\n", x->x_typetags);
returnVal = OSC_writeAddress(x, buf, messageName);
if (returnVal)
{
pd_error(x, "packOSC: Problem writing address.");
}
}
else
{
char *typeTags;
/* First figure out the type tags */
for (numTags = 0; numTags < numArgs; numTags++)
{
if (args[numTags].type == BLOB_osc) break; /* blob has one type tag and is the last element */
}
typeTags=(char*)getbytes(sizeof(char)*(numTags+2)); /* number of args + ',' + '\0' */
typeTags[0] = ',';
for (j = 0; j < numTags; ++j)
{
switch (args[j].type)
{
case INT_osc:
typeTags[j+1] = 'i';
break;
case FLOAT_osc:
typeTags[j+1] = 'f';
break;
case STRING_osc:
typeTags[j+1] = 's';
break;
case BLOB_osc:
typeTags[j+1] = 'b';
break;
default:
pd_error(x, "packOSC: arg %d type is unrecognized(%d)", j, args[j].type);
break;
}
}
typeTags[j+1] = '\0';
debugprint("packOSC_writemessage calling OSC_writeAddressAndTypes with x->x_typetags %d typeTags %p (%s)\n", x->x_typetags, typeTags, typeTags);
returnVal = OSC_writeAddressAndTypes(x, buf, messageName, typeTags);
if (returnVal)
{
pd_error(x, "packOSC: Problem writing address.");
}
freebytes(typeTags, sizeof(char)*(numTags+2));
}
for (j = 0; j < numArgs; j++)
{
switch (args[j].type)
{
case INT_osc:
returnVal = OSC_writeIntArg(x, buf, args[j].datum.i);
break;
case FLOAT_osc:
returnVal = OSC_writeFloatArg(x, buf, args[j].datum.f);
break;
case STRING_osc:
returnVal = OSC_writeStringArg(x, buf, args[j].datum.s);
break;
case BLOB_osc:
debugprint("packOSC_writemessage calling OSC_writeBlobArg\n");
return OSC_writeBlobArg(x, buf, &args[j], numArgs-j); /* All the remaining args are blob */
default:
break; /* just skip bad types (which we won't get anyway unless this code is buggy) */
}
}
return returnVal;
}
static void packOSC_sendbuffer(t_packOSC *x)
{
int i;
int length;
unsigned char *buf;
int reentry_count=x->x_reentry_count; /* must be on stack for recursion */
size_t bufsize=sizeof(t_atom)*x->x_buflength; /* must be on stack for recursion */
t_atom *atombuffer=x->x_bufferForOSClist; /* must be on stack in the case of recursion */
if(reentry_count>0) /* if we are recurse, let's move atombuffer to the stack */
atombuffer=(t_atom *)getbytes(bufsize);
if(!atombuffer) {
pd_error(x, "packOSC: unable to allocate %lu bytes for atombuffer", (long)bufsize);
return;
}
debugprint("packOSC_sendbuffer: Sending buffer...\n");
if (OSC_isBufferEmpty(x->x_oscbuf))
{
pd_error(x, "packOSC_sendbuffer() called but buffer empty");
return;
}
if (!OSC_isBufferDone(x->x_oscbuf))
{
pd_error(x, "packOSC_sendbuffer() called but buffer not ready!, not exiting");
return;
}
length = OSC_packetSize(x->x_oscbuf);
buf = (unsigned char *)OSC_getPacket(x->x_oscbuf);
debugprint("packOSC_sendbuffer: length: %u\n", length);
/* convert the bytes in the buffer to floats in a list */
for (i = 0; i < length; ++i) SETFLOAT(&atombuffer[i], buf[i]);
/* cleanup the OSCbuffer structure (so we are ready for recursion) */
OSC_initBuffer(x->x_oscbuf, x->x_buflength, x->x_bufferForOSCbuf);
/* send the list out the outlet */
outlet_list(x->x_listout, &s_list, length, atombuffer);
/* cleanup our 'stack'-allocated atombuffer in the case of reentrancy */
if(reentry_count>0)
freebytes(atombuffer, bufsize);
}
/* The next part is copied and morphed from OSC-client.c. */
/*
Author: Matt Wright
Version 2.2: Calls htonl in the right places 20000620
Version 2.3: Gets typed messages right.
*/
/*
pd
-------------
raf@interaccess.com:
rev. for Win32 build (verified under Win-2ooo) 11-April-2002
-- changed licence part (20040820) jdl
-- Version 2.4 changes not in here (20040820) jdl
*/
static void OSC_initBuffer(OSCbuf *buf, size_t size, char *byteArray)
{
buf->buffer = byteArray;
buf->size = size;
OSC_resetBuffer(buf);
}
static void OSC_resetBuffer(OSCbuf *buf)
{
buf->bufptr = buf->buffer;
buf->state = EMPTY;
buf->bundleDepth = 0;
buf->prevCounts[0] = 0;
buf->gettingFirstUntypedArg = 0;
buf->typeStringPtr = 0;
}
static int OSC_isBufferEmpty(const OSCbuf *buf)
{
return buf->bufptr == buf->buffer;
}
static size_t OSC_freeSpaceInBuffer(const OSCbuf *buf)
{
return buf->size - (buf->bufptr - buf->buffer);
}
static int OSC_isBufferDone(const OSCbuf *buf)
{
return (buf->state == DONE || buf->state == ONE_MSG_ARGS);
}
static char *OSC_getPacket(const OSCbuf *buf)
{
return buf->buffer;
}
static int OSC_packetSize(const OSCbuf *buf)
{
return (buf->bufptr - buf->buffer);
}
static int OSC_CheckOverflow(void *x, const OSCbuf *buf, size_t bytesNeeded)
{
if ((bytesNeeded) > OSC_freeSpaceInBuffer(buf))
{
pd_error(x, "packOSC: buffer overflow");
return 1;
}
return 0;
}
static void PatchMessageSize(OSCbuf *buf)
{
uint32_t size = buf->bufptr - ((char *) buf->thisMsgSize) - 4;
*(buf->thisMsgSize) = htonl(size);
}
static int OSC_openBundle(void *x, OSCbuf *buf, OSCTimeTag tt)
{
if (buf->state == ONE_MSG_ARGS)
{
pd_error(x, "packOSC: Can't open a bundle in a one-message packet");
return 3;
}
if (buf->state == DONE)
{
pd_error(x, "packOSC: This packet is finished; can't open a new bundle");
return 4;
}
if (++(buf->bundleDepth) >= MAX_BUNDLE_NESTING)
{
pd_error(x, "packOSC: Bundles nested too deeply: maybe change MAX_BUNDLE_NESTING from %d and recompile", MAX_BUNDLE_NESTING);
return 2;
}
if (CheckTypeTag(x, buf, '\0')) return 9;
if (buf->state == GET_ARGS)
{
PatchMessageSize(buf);
}
if (buf->state == EMPTY)
{
/* Need 16 bytes for "#bundle" and time tag */
if(OSC_CheckOverflow(x, buf, 16)) return 1;
}
else
{
/* This bundle is inside another bundle, so we need to leave
a blank size count for the size of this current bundle. */
if(OSC_CheckOverflow(x, buf, 20))return 1;
*((uint32_t *)buf->bufptr) = 0xaaaaaaaa;
buf->prevCounts[buf->bundleDepth] = (uint32_t *)buf->bufptr;
buf->bufptr += 4;
}
buf->bufptr += OSC_padString(buf->bufptr, "#bundle");
*((OSCTimeTag *) buf->bufptr) = tt;
if (htonl(1) != 1)
{
/* Byte swap the 8-byte integer time tag */
uint32_t *intp = (uint32_t *)buf->bufptr;
intp[0] = htonl(intp[0]);
intp[1] = htonl(intp[1]);
/* tt is a struct of two 32-bit words, and even though
each word was wrong-endian, they were in the right order
in the struct.) */
}
buf->bufptr += sizeof(OSCTimeTag);
buf->state = NEED_COUNT;
buf->gettingFirstUntypedArg = 0;
buf->typeStringPtr = 0;
return 0;
}
static int OSC_closeBundle(void *x, OSCbuf *buf)
{
if (buf->bundleDepth == 0)
{
/* This handles EMPTY, ONE_MSG, ARGS, and DONE */
pd_error(x, "packOSC: Can't close bundle: no bundle is open!");
return 5;
}
if (CheckTypeTag(x, buf, '\0')) return 9;
if (buf->state == GET_ARGS)
{
PatchMessageSize(buf);
}
if (buf->bundleDepth == 1)
{
/* Closing the last bundle: No bundle size to patch */
buf->state = DONE;
}
else
{
/* Closing a sub-bundle: patch bundle size */
int size = buf->bufptr - ((char *) buf->prevCounts[buf->bundleDepth]) - 4;
*(buf->prevCounts[buf->bundleDepth]) = htonl(size);
buf->state = NEED_COUNT;
}
--buf->bundleDepth;
buf->gettingFirstUntypedArg = 0;
buf->typeStringPtr = 0;
return 0;
}
static int OSC_writeAddress(void *x, OSCbuf *buf, char *name)
{
uint32_t paddedLength;
debugprint("-->OSC_writeAddress buf %p bufptr %p name %s\n", buf, buf->bufptr, name);
if (buf->state == ONE_MSG_ARGS)
{
pd_error(x, "packOSC: This packet is not a bundle, so you can't write another address");
return 7;
}
if (buf->state == DONE)
{
pd_error(x, "packOSC: This packet is finished; can't write another address");
return 8;
}
if (CheckTypeTag(x, buf, '\0')) return 9;
paddedLength = OSC_effectiveStringLength(name);
debugprint("OSC_writeAddress paddedLength %d\n", paddedLength);
if (buf->state == EMPTY)
{
/* This will be a one-message packet, so no sizes to worry about */
if(OSC_CheckOverflow(x, buf, paddedLength))return 1;
buf->state = ONE_MSG_ARGS;
}
else
{
/* GET_ARGS or NEED_COUNT */
if(OSC_CheckOverflow(x, buf, 4+paddedLength))return 1;
if (buf->state == GET_ARGS)
{
/* Close the old message */
PatchMessageSize(buf);
}
buf->thisMsgSize = (uint32_t *)buf->bufptr;
*(buf->thisMsgSize) = 0xbbbbbbbb;
buf->bufptr += 4;
buf->state = GET_ARGS;
}
/* Now write the name */
buf->bufptr += OSC_padString(buf->bufptr, name);
buf->typeStringPtr = 0;
buf->gettingFirstUntypedArg = 1;
return 0;
}
static int OSC_writeAddressAndTypes(void *x, OSCbuf *buf, char *name, char *types)
{
int result;
uint32_t paddedLength;
debugprint("OSC_writeAddressAndTypes buf %p name %s types %s\n", buf, name, types);
if (buf == NULL) return 10;
if (CheckTypeTag(x, buf, '\0')) return 9;
result = OSC_writeAddress(x, buf, name);
if (result) return result;
paddedLength = OSC_effectiveStringLength(types);
if(OSC_CheckOverflow(x, buf, paddedLength))return 1;
buf->typeStringPtr = buf->bufptr + 1; /* skip comma */
buf->bufptr += OSC_padString(buf->bufptr, types);
debugprint("OSC_writeAddressAndTypes buf->typeStringPtr now %p (%s) buf->bufptr now %p (%s)\n",
buf->typeStringPtr, buf->typeStringPtr, buf->bufptr, buf->bufptr);
buf->gettingFirstUntypedArg = 0;
buf->typeStringPtr = 0;// ready for a new type string
return 0;
}
static int CheckTypeTag(void *x, const OSCbuf *buf, char expectedType)
{
char c;
const char*typeStringPtr = buf->typeStringPtr;
if (typeStringPtr)
{
debugprint("CheckTypeTag typeStringPtr %p (%s)\n", typeStringPtr, typeStringPtr);
c = *typeStringPtr;
debugprint("CheckTypeTag buf %p expectedType %c c is %c\n", buf, expectedType, c);
if (c != expectedType)
{
if (expectedType == '\0')
{
pd_error(x, "packOSC: According to the type tag (%c) I expected more arguments.", c);
}
else if (*typeStringPtr == '\0')
{
pd_error(x, "packOSC: According to the type tag I didn't expect any more arguments.");
}
else
{
pd_error(x, "packOSC: According to the type tag I expected an argument of a different type.");
pd_error(x, "* Expected %c, string now %s\n", expectedType, typeStringPtr);
}
return 9;
}
++typeStringPtr;
}
return 0;
}
static int OSC_writeFloatArg(void *x, OSCbuf *buf, float arg)
{
union intfloat32
{
int i;
float f;
};
union intfloat32 if32;
if(OSC_CheckOverflow(x, buf, 4))return 1;
if (CheckTypeTag(x, buf, 'f')) return 9;
/* Pretend arg is a long int so we can use htonl() */
if32.f = arg;
*((uint32_t *) buf->bufptr) = htonl(if32.i);
buf->bufptr += 4;
buf->gettingFirstUntypedArg = 0;
return 0;
}
static int OSC_writeIntArg(void *x, OSCbuf *buf, uint32_t arg)
{
if(OSC_CheckOverflow(x, buf, 4))return 1;
if (CheckTypeTag(x, buf, 'i')) return 9;
*((uint32_t *) buf->bufptr) = htonl(arg);
buf->bufptr += 4;
buf->gettingFirstUntypedArg = 0;
return 0;
}
static int OSC_writeBlobArg(void *x, OSCbuf *buf, typedArg *arg, size_t nArgs)
{
size_t i;
unsigned char b;
/* pack all the args as single bytes following a 4-byte length */
if(OSC_CheckOverflow(x, buf, nArgs+4))return 1;
if (CheckTypeTag(x, buf, 'b')) return 9;
*((uint32_t *) buf->bufptr) = htonl(nArgs);
debugprint("OSC_writeBlobArg length : %lu\n", nArgs);
buf->bufptr += 4;
for (i = 0; i < nArgs; i++)
{
if (arg[i].type != BLOB_osc)
{
pd_error(x, "packOSC: blob element %lu not blob type", (long unsigned)i);
return 9;
}
b = (unsigned char)((arg[i].datum.i)&0x0FF);/* force int to 8-bit byte */
debugprint("OSC_writeBlobArg : %d, %d\n", arg[i].datum.i, b);
buf->bufptr[i] = b;
}
i = OSC_WriteBlobPadding(buf->bufptr, i);
buf->bufptr += i;
buf->gettingFirstUntypedArg = 0;
return 0;
}
static int OSC_writeStringArg(void *x, OSCbuf *buf, const char *arg)
{
int len;
if (CheckTypeTag(x, buf, 's')) return 9;
len = OSC_effectiveStringLength(arg);
if (buf->gettingFirstUntypedArg && arg[0] == ',')
{
/* This un-type-tagged message starts with a string
that starts with a comma, so we have to escape it
(with a double comma) so it won't look like a type
tag string. */
if(OSC_CheckOverflow(x, buf, len+4))return 1; /* Too conservative */
buf->bufptr +=
OSC_padStringWithAnExtraStupidComma(buf->bufptr, arg);
}
else
{
if(OSC_CheckOverflow(x, buf, len))return 1;
buf->bufptr += OSC_padString(buf->bufptr, arg);
}
buf->gettingFirstUntypedArg = 0;
return 0;
}
static int OSC_writeNullArg(void *x, OSCbuf *buf, char type)
{ /* Don't write any data, just check the type tag */
if(OSC_CheckOverflow(x, buf, 4))return 1;
if (CheckTypeTag(x, buf, type)) return 9;
buf->gettingFirstUntypedArg = 0;
return 0;
}
/* String utilities */
static int OSC_strlen(const char *s)
{
int i;
for (i = 0; s[i] != '\0'; i++) /* Do nothing */ ;
return i;
}
#define STRING_ALIGN_PAD 4
static int OSC_effectiveStringLength(const char *string)
{
int len = OSC_strlen(string) + 1; /* We need space for the null char. */
/* Round up len to next multiple of STRING_ALIGN_PAD to account for alignment padding */
if ((len % STRING_ALIGN_PAD) != 0)
{
len += STRING_ALIGN_PAD - (len % STRING_ALIGN_PAD);
}
return len;
}
static int OSC_padString(char *dest, const char *str)
{
int i;
for (i = 0; str[i] != '\0'; i++) dest[i] = str[i];
return OSC_WriteStringPadding(dest, i);
}
static int OSC_padStringWithAnExtraStupidComma(char *dest, const char *str)
{
int i;
dest[0] = ',';
for (i = 0; str[i] != '\0'; i++) dest[i+1] = str[i];
return OSC_WriteStringPadding(dest, i+1);
}
static int OSC_WriteStringPadding(char *dest, int i)
{
/* pad with at least one zero to fit 4-byte */
dest[i] = '\0';
i++;
for (; (i % STRING_ALIGN_PAD) != 0; i++) dest[i] = '\0';
return i;
}
static int OSC_WriteBlobPadding(char *dest, int i)
{
/* pad if necessary to fit 4-byte */
for (; (i % STRING_ALIGN_PAD) != 0; i++) dest[i] = '\0';
return i;
}
/* end packOSC.c*/
osc-0.3/packOSCstream-help.pd 0000664 0000000 0000000 00000004201 14664073227 0016122 0 ustar 00root root 0000000 0000000 #N canvas 34 60 633 578 10;
#X obj 109 119 packOSCstream;
#X obj 109 451 tcpsend;
#X text 16 496 check also:;
#X obj 18 513 unpackOSCstream;
#X obj 133 513 packOSC;
#X msg 78 403 connect localhost 9995;
#X obj 248 513 tcpreceive;
#X obj 191 513 tcpsend;
#X obj 326 513 tcpserver;
#X obj 397 513 tcpclient;
#X msg 98 423 disconnect;
#X obj 109 473 tgl 15 0 empty empty empty 17 7 0 10 #00fc04 #f8fc00 #000000 0 1;
#X obj 221 403 cnv 15 380 30 empty empty empty 20 12 0 14 #14e814 #404040 0;
#X text 236 408 <- First open unpackOSCstream-help \, then connect;
#X text 480 501 Author: Roman Haefeli;
#X text 480 517 Version: 2008-09-09;
#X text 114 254 [packOSCstream] uses the same methods as [packOSC]. Please consult packOSC-help.pd for the complete documentation.;
#X text 115 189 [packOSstream] puts a frame length header (int32) in front of every OSC packet or bundle \, so that the receiving side knows \, how to split the incoming stream into OSC packets again.;
#X msg 16 9 /first/message 1 \, /second/message 2;
#X text 119 330 reference:;
#X text 120 347 https://opensoundcontrol.stanford.edu/spec-1_0.html : Section "OSC Packets";
#X msg 36 29 send /test/one/two/three zz 88 T;
#X msg 56 49 sendtyped /test/one/two/three sis zz 88 T;
#X msg 76 69 prefix /test;
#X msg 96 89 [ \, /previous/page 666 \, /next/page 999 \, ];
#X text 112 142 [packOSCstream] is meant to be a replacement for [packOSC] \, when sending over a stream based protocol \, such as TCP.;
#N canvas 499 120 494 344 META 0;
#X text 12 135 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.;
#X text 12 25 LICENSE GPL v2 or later;
#X text 12 115 AUTHOR Roman Haefeli;
#X text 12 75 INLET_0 anything send sendtyped prefix connect disconnect;
#X text 12 45 DESCRIPTION meant to be a replacement for [packOSC] \, when sending over a stream based protocol \, such as TCP;
#X text 12 95 OUTLET_0 float;
#X text 12 5 KEYWORDS control network abstraction;
#X restore 572 544 pd META;
#X connect 0 0 1 0;
#X connect 1 0 11 0;
#X connect 5 0 1 0;
#X connect 10 0 1 0;
#X connect 18 0 0 0;
#X connect 21 0 0 0;
#X connect 22 0 0 0;
#X connect 23 0 0 0;
#X connect 24 0 0 0;
osc-0.3/packOSCstream.pd 0000664 0000000 0000000 00000000457 14664073227 0015205 0 ustar 00root root 0000000 0000000 #N canvas 642 471 204 215 10;
#X obj 9 35 packOSC;
#X obj 9 14 inlet;
#X obj 9 139 outlet;
#X text 36 157 Author: Roman Haefeli;
#X obj 135 139 outlet;
#X obj 9 80 mrpeach/slipenc 65536;
#X text 36 173 version: 2011-02-01;
#X connect 0 0 5 0;
#X connect 0 1 4 0;
#X connect 1 0 0 0;
#X connect 5 0 2 0;
osc-0.3/packingOSC.h 0000664 0000000 0000000 00000001310 14664073227 0014300 0 ustar 00root root 0000000 0000000 /* packingOSC.h */
#ifndef _PACKINGOSC
#include "m_pd.h"
#include
#include
#include
#ifdef _WIN32
# include
#else
# include
#endif /* _WIN32 */
/* Declarations */
#define MAX_MESG 65536 /* same as MAX_UDP_PACKET */
/* The maximum depth of bundles within bundles within bundles within...
This is the size of a static array. If you exceed this limit you'll
get an error message. */
#define MAX_BUNDLE_NESTING 32
typedef union
{
int i;
float f;
} intfloat32;
#undef debug
#if DEBUG
# define debugprint printf
#else
static void debugprint(const char*fmt, ...) {(void)fmt;}
#endif
#endif // _PACKINGOSC
/* end of packingOSC.h */
osc-0.3/pd-lib-builder/ 0000775 0000000 0000000 00000000000 14664073227 0014746 5 ustar 00root root 0000000 0000000 osc-0.3/pd-lib-builder/CHANGELOG.txt 0000664 0000000 0000000 00000007401 14664073227 0017000 0 ustar 00root root 0000000 0000000 Changelog for Makefile.pdlibbuilder.
v0.7.0, dated 2023-07-06
- build double-precision externals with the 'floatsize' variable
- allow building multiple flavours of an external side-by-side (#78)
- facilitate multiple platform co-installation of shared lib (#58)
- fix use of shared.ldflags with helper-library (#64)
- fix broken armv6l platform detection (#71)
- improve documentation
v0.6.0, dated 2019-12-21
- detect target platform (OS and architecture) rather than build platform (#55)
- introduce optional user variable 'PLATFORM' for cross compilation
- no longer build OSX/MacOS fat binaries by default (#21, #50)
- do build fat binaries when 'extension=d_fat' is specified for OSX/MacOS
- fix bug where minimum OSX/MacOS version wasn't defined, and set it to 10.6
v0.5.1, dated 2018-03-15
Fixes and improvements for Windows builds:
- properly evaluate variables 'PDDIR' and 'PDBINDIR' to find pd.dll
- define default path of 32 bit Pd on 64 bit Windows
- link C++ externals with standard C libs on Windows, they don't load otherwise
- strip installed Windows binaries by default
(issues #34, #39, #41, #42 respectively)
Warning for all platforms: variable 'PD_PATH' is no longer supported, use the
equivalent 'PDDIR'.
v0.5.0, dated 2018-01-23
Implement target architecture detection for Windows builds,
and set appropriate options for 32 and 64 bit (used to be for 32 bit only).
(feature, issue #37 #38, merge commit 215bf3e)
v0.4.4, dated 2016-11-22
Use variable 'system' when evaluating 'for{Linux,Darwin,Windows}'
(bugfix, issue #31, commit 2c14110)
v0.4.3, dated 2016-11-02
Replace flags '-fpic' by 'fPIC'.
(bugfix, issue #29, commit 426b38b)
v0.4.2, dated 2016-10-30
Fix issue where incorrect message about m_pd.h is given.
(bugfix, commit 2e13d8f)
v0.4.1, dated 2016-10-27
Respect cflag for minimum OSX version when defined by lib makefile.
(bugfix, pull request #22, commit 48c4127)
v0.4.0, dated 2016-10-14
Introduced path variables PDDIR, PDINCLUDEDIR, PDBINDIR, PDLIBDIR which can
also be defined in environment.
(feature, issue #27, commit b0dab72)
v0.3.1, dated 2016-10-13
Fix bug where pd.dll wouldn't be found.
(bugfix, commit a0c87be)
v0.3.0, dated 2016-10-09
Variable 'PD_PATH' introduced for pd-extended / pd-l2ork compatibility.
(feature, issue #26, commit 41e9743)
v0.2.8, dated 2016-10-09
Allow installed files to contain weird characters (notably '$').
(bugfix, pull request #20, commit 5b920b1)
v0.2.7, dated 2016-10-04
Remove all default pd search paths except vanilla's.
(discussion, issue #25, commit a6a89dc)
v0.2.6, dated 2016-09-20
Redefined dependency checking so it won't stall rebuilds on OSX.
(bugfix, issue #16, commit 9fd1795)
v0.2.5, dated 2016-06-26
Fixed dependency checking for object files in other directories.
(bugfix, commit f06e550)
v0.2.4, dated 2016-06-25
Fixed regression bug that disabled all dependency checking.
(bugfix, commit 1d7bb5e)
v0.2.3, dated 2016-03-29
Disabled dependency checking for OSX <= 10.5 because it stalled rebuilds.
(bugfix, issue #16, commit eb614fd)
v0.2.2, dated 2016-03-28
Removed target 'pre' because it forced rebuild of everything in 'all'.
(bugfix, issue #17, commit c989c8e)
v0.2.1, dated 2015-12-27
Implement / respect 'CPPFLAGS','CFLAGS'and 'LDFLAGS'.
(bugfix, issue #5, commit 98f3582)
v0.2.0, dated 2015-12-19
Added per-platform multiline defines 'forLinux', 'forDarwin', 'forWindows'.
(feature, pull request #9, commit 3946ea5)
v0.1.0, dated 2015-12-08
Added targets 'pre' and 'post' to automatically run before and after 'all'.
(feature, pull request #4, commit a5678ac)
v0.0.2, dated 2015-12-06
Improved methods for searching pd paths.
(bugfix, commit ed37e6b)
v0.0.1, dated 2015-10-31
Fixed expansion of variable 'lib.version'.
(bugfix, issue #1, commit 974b617)
v0.0.0, dated 2015-06-24
Initial version.
(commit 16517a2)
osc-0.3/pd-lib-builder/Makefile.pdlibbuilder 0000664 0000000 0000000 00000131452 14664073227 0021054 0 ustar 00root root 0000000 0000000 # Makefile.pdlibbuilder dated 2019-12-21
version = 0.7.0
# Helper makefile for Pure Data external libraries.
# Written by Katja Vetter March-June 2015 for the public domain. No warranties.
# Inspired by Hans Christoph Steiner's Makefile Template and Stephan Beal's
# ShakeNMake.
#
# Grab the newest version of Makefile.pdlibbuilder from
# https://github.com/pure-data/pd-lib-builder/
#
# GNU make version >= 3.81 required.
#
#
#=== characteristics ===========================================================
#
#
# - defines build settings based on autodetected OS and architecture
# - defines rules to build Pd class- or lib executables from C or C++ sources
# - defines rules for libdir installation
# - defines convenience targets for developer and user
# - evaluates implicit dependencies for non-clean builds
#
#
#=== basic usage ===============================================================
#
#
# In your Makefile, define your Pd lib name and class files, and include
# Makefile.pdlibbuilder at the end of the Makefile. Like so:
#
# ________________________________________________________________________
#
# # Makefile for mylib
#
# lib.name = mylib
#
# class.sources = myclass1.c myclass2.c
#
# datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt
#
# include Makefile.pdlibbuilder
# ________________________________________________________________________
#
#
# For files in class.sources it is assumed that class basename == source file
# basename. The default target builds all classes as individual executables
# with Pd's default extension for the platform. For anything more than the
# most basic usage, continue reading.
#
#
#=== list of Makefile.pdlibbuilder API variables ===============================
#
#
# Variables available for definition in your library Makefile:
#
# - lib.name
# - lib.setup.sources
# - class.sources
# - common.sources
# - shared.sources
# - .class.sources
# - .class.ldflags
# - .class.ldlibs
# - cflags
# - ldflags
# - ldlibs
# - datafiles
# - datadirs
# - makefiles
# - makefiledirs
# - externalsdir
#
# Optional multiline defines evaluated per operating system:
#
# - forLinux
# - forDarwin
# - forWindows
#
# Variables available for your makefile or make command line:
#
# - make-lib-executable
# - suppress-wunused
#
# Path variables for make command line or environment:
#
# - PDDIR
# - PDINCLUDEDIR
# - PDBINDIR
# - PDLIBDIR
#
# Standard make variables for make command line or environment:
#
# - CPPFLAGS
# - CFLAGS
# - LDFLAGS
# - CC
# - CXX
# - INSTALL
# - STRIP
# - DESTDIR
#
# Optional user variables for make command line or environment:
#
# - PLATFORM
# - extension
# - floatsize
#
# Deprecated path variables:
#
# - pdincludepath
# - pdbinpath
# - objectsdir
#
#
#=== descriptions of Makefile.pdlibbuilder API variables =======================
#
#
# lib.name:
# Name of the library directory as it will be installed / distributed. Also the
# name of the lib executable in the case where all classes are linked into
# a single binary.
#
# lib.setup.sources:
# Source file(s) (C or C++) which must be compiled only when linking all classes
# into a single lib binary.
#
# class.sources:
# All sources files (C or C++) for which the condition holds that
# class name == source file basename.
#
# .class.sources:
# Source file(s) (C or C++) specific to class . Use this for
# multiple-source classes or when class name != source file basename.
#
# common.sources:
# Source file(s) which must be statically linked to each class in the library.
#
# shared.sources:
# Source file(s) (C or C++) to build a shared dynamic link lib, to be linked
# with all class executables.
#
# cflags, ldflags, ldlibs:
# Define cflags (preprocessor&compiler), ldflags (linker) and ldlibs (dynamic
# link libs) for the whole library. These flags are added to platform-specific
# flags defined by Makefile.pdlibbuilder.
#
# .class.ldflags and .class.ldlibs:
# Define ldflags resp. ldlibs specific to class . These flags are
# added to platform-specific flags defined by Makefile.pdlibbuilder, and flags
# defined in your Makefile for the whole library. Note: cflags can not be
# defined per class in the current implementation.
#
# datafiles and datadirs:
# All extra files you want to include in binary distributions of the
# library: abstractions and help patches, example patches, meta patch, readme
# and license texts, manuals, sound files, etcetera. Use 'datafiles' for all
# files that should go into your lib rootdir and 'datadirs' for complete
# directories you want to copy from source to distribution.
#
# forLinux, forDarwin, forWindows:
# Shorthand for 'variable definitions for Linux only' etc. Use like:
# define forLinux
# cflags += -DLINUX
# class.sources += linuxthing.c
# endef
#
# makefiles and makefiledirs:
# Extra makefiles or directories with makefiles that should be made in sub-make
# processes.
#
# make-lib-executable:
# When this variable is defined 'yes' in your makefile or as command argument,
# Makefile.pdlibbuilder will try to build all classes into a single library
# executable (but it will force exit if lib.setup.sources is undefined).
# If your makefile defines 'make-lib-executable=yes' as the library default,
# this can still be overridden with 'make-lib-executable=no' as command argument
# to build individual class executables (the Makefile.pdlibbuilder default.)
#
# suppress-wunused:
# When this variable is defined ('yes' or any other value), -Wunused-variable,
# -Wunused-parameter, -Wunused-value and -Wunused-function are suppressed,
# but the other warnings from -Wall are retained.
#
# PDDIR:
# Root directory of 'portable' pd package. When defined, PDINCLUDEDIR and
# PDBINDIR will be evaluated as $(PDDIR)/src and $(PDDIR)/bin.
#
# PDINCLUDEDIR:
# Directory where Pd API m_pd.h should be found, and other Pd header files.
# Overrides the default search path.
#
# PDBINDIR:
# Directory where pd.dll should be found for linking (Windows only). Overrides
# the default search path.
#
# PDLIBDIR:
# Root directory for installation of Pd library directories. Overrides the
# default install location.
#
# DESTDIR:
# Prepended path component for staged install.
#
# PLATFORM:
# Target platform for cross compilation in the form of GNU triplet:
# cpu-vendor-os. Example: x86_64-w64-mingw32. This specifies the tool chain that
# pdlibbuilder will use, if installed and locatable. System and architecture
# will then be autodefined accordingly. In most cases no other variables need to
# be overridden.
#
# extension:
# Extension for the external to use. Example: m_amd64
# A sane default is picked, but it is useful if you want to provide
# co-installable externals for multiple platforms (for the same operating
# systems)
#
# floatsize:
# the size of the t_float in bits. Example: 32
# t_float are usually single precision (32bit), which is the default.
# For double precision use floatsize=64
# When building double precision externals, you will want to set the extension
# as well, e.g. extension=windows-amd64-64.dll (--.)
#
# CPPFLAGS:
# Preprocessor flags which are not strictly required for building.
#
# CFLAGS:
# Compiler flags which are not strictly required for building. Compiler flags
# defined by Makefile.pdlibbuilder for warning, optimization and architecture
# specification are overriden by CFLAGS.
#
# LDFLAGS:
# Linker flags which are not strictly required for building. Linker flags
# defined by Makefile.pdlibbuilder for architecture specification are overriden
# by LDFLAGS.
#
# CC and CXX:
# C and C++ compiler programs as defined in your build environment.
#
# INSTALL
# Definition of install program.
#
# STRIP
# Name of strip program. Default 'strip' can be overridden in cross compilation
# environments.
#
# objectsdir:
# Root directory for installation of Pd library directories, like PDLIBDIR but
# not overridable by environment. Supported for compatibility with pd-extended
# central makefile, but deprecated otherwise.
#
# pdincludepath, pdbinpath:
# As PDINCLUDEDIR and PDBINDIR but not overridable by environment. Deprecated
# as user variables.
#
#
#=== paths =====================================================================
#
#
# Source files in directories other than current working directory must be
# prefixed with their relative path. Do not rely on VPATH or vpath.
# Object (.o) files are built in the directory of their source files.
# Executables are built in current working directory.
#
# Default search path for m_pd.h and other API header files is platform
# dependent, and overridable by PDINCLUDEDIR:
#
# Linux: /usr/include/pd
#
# OSX: /Applications/Pd*.app/Contents/Resources/src
#
# Windows: %PROGRAMFILES%/Pd/src
# %PROGRAMFILES(X86)%/Pd/src (32 bit builds on 64 bit Windows)
#
# Default search path for binary pd.dll (Windows), overridable by PDBINDIR
#
# %PROGRAMFILES%/Pd/bin
# %PROGRAMFILES(X86)%/Pd/bin (32 bit builds on 64 bit Windows)
#
# Default location to install pd libraries is platform dependent, and
# overridable by PDLIBDIR:
#
# Linux: /usr/local/lib/pd-externals
# OSX: ~/Library/Pd
# Windows: %APPDATA%/Pd
#
# https://puredata.info/docs/faq/how-do-i-install-externals-and-help-files
# The rationale for not installing to ~/pd-externals by default on Linux
# is that some people share the home dir between 32 and 64 bit installations.
#
#
#=== targets ===================================================================
#
#
# all: build $(executables) plus optional post target
# post: target to build after $(executables)
# alldebug: build all with -g option turned on for debug symbols
# : force clean build of an individual class
# .pre: make preprocessor output file in current working directory
# .lst: make asm/source output file in current working directory
#
# install: install executables and data files
# clean: remove build products from source tree
#
# help: print help text
# vars: print makefile variables
# allvars: print all variables
# depend: print generated prerequisites
# dumpmachine: print compiler output of option '-dumpmachine'
# coffee: dummy target
#
# Variable $(executables) expands to class executables plus optional shared lib,
# or alternatively to single lib executable when make-lib-executable=true.
# Targets pre and post can be defined by library makefile. Make sure to include
# Makefile.pdlibbuilder first so default target all will not be redefined.
#
#
#=== Pd-extended libdir concept ================================================
#
#
# For libdir layout as conceived by Hans-Christoph Steiner, see:
#
# https://puredata.info/docs/developer/Libdir
#
# Files README.txt, LICENSE.txt and -meta.pd are part of the libdir
# convention. Help patches for each class and abstraction are supposed to be
# available. Makefile.pdlibbuilder does not force the presence of these files
# however. It does not automatically include such files in libdir installations.
# Data files you want to include in distributions must be defined explicitly in
# your Makefile.
#
#
#=== Makefile.pdlibbuilder syntax conventions ==================================
#
#
# Makefile.pdlibbuilder variable names are lower case. Default make variables,
# environment variables, and standard user variables (CC, CXX, CFLAGS, DESTDIR)
# are upper case. Use target 'allvars' to print all variables and their values.
#
# 'Fields' in data variables are separated by dots, like in 'foo.class.sources'.
# Words in variables expressing a function or command are separated by dashes,
# like in 'make-lib-executable'.
#
#
#=== useful make options =======================================================
#
#
# Use 'make -d ' to print debug details of the make process.
# Use 'make -p ' to print make's database.
#
#
#=== TODO ======================================================================
#
#
# - decide whether to use -static-libgcc or shared dll in MinGW
# - cygwin support
# - android support
# - figure out how to handle '$' in filenames
# - add makefile template targets dpkg-source dist libdir distclean tags?
#
#
#=== end of documentation sections =============================================
#
#
################################################################################
################################################################################
################################################################################
# GNU make version 3.81 (2006) or higher is required because of the following:
# - function 'info'
# - variable '.DEFAULT_GOAL'
# force exit when make version is < 3.81
ifneq ($(firstword $(sort 3.81 $(MAKE_VERSION))), 3.81)
$(error GNU make version 3.81 or higher is required)
endif
# Relative path to externals root dir in multi-lib source tree like
# pd-extended SVN. Default is parent of current working directory. May be
# defined differently in including makefile.
externalsdir ?= ..
# variable you can use to check if Makefile.pdlibbuilder is already included
Makefile.pdlibbuilder = true
################################################################################
### target platform detection ##################################################
################################################################################
#=== target platform ===========================================================
# PLATFORM: optional user variable to define target platform for cross
# compilation. Redefine build tools accordingly. PLATFORM should match
# the exact target prefix of tools present in $PATH, like x86_64-w64-mingw32,
# x86_64-apple-darwin12 etc. Tool definitions are exported to ensure submakes
# will get the same.
ifneq ($(PLATFORM),)
ifneq ($(findstring darwin, $(PLATFORM)),)
export CC = $(PLATFORM)-cc
export CXX = $(PLATFORM)-c++
export CPP = $(PLATFORM)-cc
else
export CC = $(PLATFORM)-gcc
export CXX = $(PLATFORM)-g++
export CPP = $(PLATFORM)-cpp
endif
STRIP = $(PLATFORM)-strip
endif
# Let (native or cross-) compiler report target triplet and isolate individual
# words therein to facilitate later processing.
target.triplet := $(subst -, ,$(shell $(CC) -dumpmachine))
#=== operating system ==========================================================
# The following systems are defined: Linux, Darwin, Windows. GNU and
# GNU/kFreeBSD are treated as Linux to get the same options.
ifneq ($(filter linux gnu% kfreebsd, $(target.triplet)),)
system = Linux
endif
ifneq ($(filter darwin%, $(target.triplet)),)
system = Darwin
endif
ifneq ($(filter mingw% cygwin%, $(target.triplet)),)
system = Windows
endif
# evaluate possible system-specific multiline defines from library makefile
$(eval $(for$(system)))
# TODO: Cygwin, Android
#=== architecture ==============================================================
# The following CPU names can be processed by pdlibbuilder:
# i*86 Intel 32 bit
# x86_64 Intel 64 bit
# arm ARM 32 bit
# aarch64 ARM 64 bit
target.arch := $(firstword $(target.triplet))
################################################################################
### variables per platform #####################################################
################################################################################
#=== flags per floatsize == ====================================================
floatsize = 32
ifneq ($(filter-out 32,$(floatsize)),)
floatsize.flags = -DPD_FLOATSIZE=$(floatsize)
else
floatsize.flags =
endif
#=== flags per architecture ====================================================
# Set architecture-dependent cflags, mainly for Linux. For Mac and Windows,
# arch.c.flags are overriden below. To see gcc's default architecture flags:
# $ gcc -Q --help=target
# ARMv6: Raspberry Pi 1st gen, not detectable from target.arch
ifeq ($(shell uname -m), armv6l)
arch.c.flags = -march=armv6 -mfpu=vfp -mfloat-abi=hard
# ARMv7: Beagle, Udoo, RPi2 etc.
else ifeq ($(target.arch), arm)
arch.c.flags = -march=armv7-a -mfpu=vfpv3 -mfloat-abi=hard
# ARMv8 64 bit, not tested yet
else ifeq ($(target.arch), aarch64)
arch.c.flags = -mcpu=cortex-a53
# Intel 32 bit, build with SSE and SSE2 instructions
else ifneq ($(filter i%86, $(target.arch)),)
arch.c.flags = -march=pentium4 -mfpmath=sse -msse -msse2
# Intel/AMD 64 bit, build with SSE, SSE2 and SSE3 instructions
else ifeq ($(target.arch), x86_64)
arch.c.flags = -march=core2 -mfpmath=sse -msse -msse2 -msse3
# if none of the above architectures detected
else
arch.c.flags =
endif
#=== flags and paths for Linux =================================================
ifeq ($(system), Linux)
prefix = /usr/local
libdir := $(prefix)/lib
pkglibdir = $(libdir)/pd-externals
pdincludepath := $(wildcard /usr/include/pd)
extension = pd_linux
cpp.flags := -DUNIX
c.flags := -fPIC
c.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags
c.ldlibs := -lc -lm
cxx.flags := -fPIC -fcheck-new
cxx.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags
cxx.ldlibs := -lc -lm -lstdc++
shared.extension = so
shared.ldflags = -rdynamic -fPIC -shared -Wl,-soname,$(shared.lib)
endif
#=== flags and paths for Darwin ================================================
# LLVM-clang doesn't support -fcheck-new, therefore this flag is only used when
# compiling with g++.
ifeq ($(system), Darwin)
pkglibdir = $(HOME)/Library/Pd
pdincludepath := $(firstword $(wildcard \
/Applications/Pd*.app/Contents/Resources/src))
extension = pd_darwin
cpp.flags := -DUNIX -DMACOSX -I /sw/include
c.flags :=
c.ldflags := -undefined suppress -flat_namespace -bundle
c.ldlibs := -lc
cxx.ldflags := -undefined suppress -flat_namespace -bundle
cxx.ldlibs := -lc
shared.extension = dylib
shared.ldflags = -dynamiclib -undefined dynamic_lookup \
-install_name @loader_path/$(shared.lib) \
-compatibility_version 1 -current_version 1.0
ifneq ($(filter %g++, $(CXX)),)
cxx.flags := -fcheck-new
endif
ifeq ($(extension), d_fat)
arch := i386 x86_64
else
arch := $(target.arch)
endif
ifneq ($(filter -mmacosx-version-min=%, $(cflags)),)
version.flag := $(filter -mmacosx-version-min=%, $(cflags))
else
version.flag = -mmacosx-version-min=10.6
endif
arch.c.flags := $(addprefix -arch , $(arch)) $(version.flag)
arch.ld.flags := $(arch.c.flags)
endif
#=== flags and paths for Windows ===============================================
# Standard paths on Windows contain spaces, and GNU make functions treat such
# paths as lists, with unintended effects. Therefore we must use shell function
# ls instead of make's wildcard when probing for a path, and use double quotes
# when specifying a path in a command argument.
# Default paths in Mingw / Mingw-w64 environments. 'PROGRAMFILES' is standard
# location for builds with native architecture, 'ProgramFiles(x86)' for i686
# builds on x86_64 Windows (detection method by Lucas Cordiviola). Curly braces
# required because of parentheses in variable name.
ifeq ($(system), Windows)
pkglibdir := $(APPDATA)/Pd
ifeq ($(target.arch), i686)
programfiles := ${ProgramFiles(x86)}
else
programfiles := $(PROGRAMFILES)
endif
pdbinpath := $(programfiles)/Pd/bin
pdincludepath := $(programfiles)/Pd/src
endif
# Store default path to pd.dll in PDBINDIR if the latter is not user-defined.
# For include path this is done in the platform-independent paths section below,
# but for PDBINDIR it is done here so ld flags can be evaluated as immediate
# variables.
ifeq ($(system), Windows)
ifdef PDDIR
PDBINDIR := $(PDDIR)/bin
endif
PDBINDIR ?= $(pdbinpath)
endif
# TODO: decide whether -mms-bitfields should be specified.
ifeq ($(system), Windows)
cpp.flags := -DMSW -DNT
ifeq ($(target.arch), i686)
arch.c.flags := -march=pentium4 -msse -msse2 -mfpmath=sse
else ifeq ($(target.arch), x86_64)
cpp.flags := -DMSW -DNT -DPD_LONGINTTYPE=__int64
arch.c.flags := -march=core2 -msse -msse2 -msse3 -mfpmath=sse
else
arch.c.flags =
endif
extension = dll
c.flags :=
c.ldflags := -static-libgcc -shared \
-Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
c.ldlibs :=
cxx.flags := -fcheck-new
cxx.ldflags := -static-libgcc -static-libstdc++ -shared \
-Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
cxx.ldlibs :=
shared.extension = dll
shared.ldflags := -static-libgcc -shared "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
stripflags = --strip-all
endif
#=== paths =====================================================================
# Platform-dependent default paths are specified above, but overridable.
# Path variables in upper case can be defined as make command argument or in the
# environment. Variable 'objectsdir' is supported for compatibility with
# the build system that pd-l2ork has inherited from pd-extended.
PDINCLUDEDIR ?= $(pdincludepath)
PDLIBDIR ?= $(firstword $(objectsdir) $(pkglibdir))
ifdef PDDIR
PDINCLUDEDIR := $(wildcard $(PDDIR)/src)
endif
# base path where all components of the lib will be installed by default
installpath := $(DESTDIR)$(PDLIBDIR)/$(lib.name)
# check if include path contains spaces (as is often the case on Windows)
# if so, store the path so we can later do checks with it
pdincludepathwithspaces := $(if $(word 2, $(PDINCLUDEDIR)), $(PDINCLUDEDIR))
#=== accumulated build flags ===================================================
# From GNU make docs: 'Users expect to be able to specify CFLAGS freely
# themselves.' So we use CFLAGS to define options which are not strictly
# required for compilation: optimizations, architecture specifications, and
# warnings. CFLAGS can be safely overriden using a make command argument.
# Variables cflags, ldflags and ldlibs may be defined in including makefile.
optimization.flags = -O3 -ffast-math -funroll-loops -fomit-frame-pointer
warn.flags = -Wall -Wextra -Wshadow -Winline -Wstrict-aliasing
# suppress -Wunused-variable & Co if you don't want to clutter a build log
ifdef suppress-wunused
warn.flags += $(addprefix -Wno-unused-, function parameter value variable)
endif
CFLAGS = $(warn.flags) $(optimization.flags) $(arch.c.flags)
# preprocessor flags
cpp.flags := -DPD -I "$(PDINCLUDEDIR)" $(floatsize.flags) $(cpp.flags) $(CPPFLAGS)
# flags for dependency checking (cflags from makefile may define -I options)
depcheck.flags := $(cpp.flags) $(cflags)
# architecture specifications for linker are overridable by LDFLAGS
LDFLAGS := $(arch.ld.flags)
# now add the same ld flags to shared dynamic lib
shared.ldflags += $(LDFLAGS)
# accumulated flags for C compiler / linker
c.flags := $(cpp.flags) $(c.flags) $(cflags) $(CFLAGS)
c.ldflags := $(c.ldflags) $(ldflags) $(LDFLAGS)
c.ldlibs := $(c.ldlibs) $(ldlibs)
# accumulated flags for C++ compiler / linker
cxx.flags := $(cpp.flags) $(cxx.flags) $(cflags) $(CFLAGS)
cxx.ldflags := $(cxx.ldflags) $(ldflags) $(LDFLAGS)
cxx.ldlibs := $(cxx.ldlibs) $(ldlibs)
################################################################################
### variables: library name and version ########################################
################################################################################
# strip possibles spaces from lib.name, they mess up calculated file names
lib.name := $(strip $(lib.name))
# if meta file exists, check library version
metafile := $(wildcard $(lib.name)-meta.pd)
ifdef metafile
lib.version := $(shell sed -n \
's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' \
$(metafile))
endif
################################################################################
### variables: files ###########################################################
################################################################################
object.extension = $(extension).o
#=== sources ===================================================================
# (re)define .class.sources using file names in class.sources
define add-class-source
$(notdir $(basename $v)).class.sources += $v
endef
$(foreach v, $(class.sources), $(eval $(add-class-source)))
# derive class names from .class.sources variables
sourcevariables := $(filter %.class.sources, $(.VARIABLES))
classes := $(basename $(basename $(sourcevariables)))
# accumulate all source files specified in makefile
classes.sources := $(sort $(foreach v, $(sourcevariables), $($v)))
all.sources := $(classes.sources) $(lib.setup.sources) \
$(shared.sources) $(common.sources)
#=== object files ==============================================================
# construct object filenames from all C and C++ source file names
classes.objects := $(addsuffix .$(object.extension), $(basename $(classes.sources)))
common.objects := $(addsuffix .$(object.extension), $(basename $(common.sources)))
shared.objects := $(addsuffix .$(object.extension), $(basename $(shared.sources)))
lib.setup.objects := $(addsuffix .$(object.extension), $(basename $(lib.setup.sources)))
all.objects = $(classes.objects) $(common.objects) $(shared.objects) \
$(lib.setup.objects)
#=== executables ===============================================================
# construct class executable names from class names
classes.executables := $(addsuffix .$(extension), $(classes))
# Construct shared lib executable name if shared sources are defined.
# If extension does not end with shared extension, use both to facilitate co-
# installation for different platforms, like .m_i386.dll and .linux-amd64-32.so
ifdef shared.sources
ifneq ($(filter %.$(shared.extension), .$(extension)), )
# $(extension) already ends with $(shared.extension), no need to duplicate it
shared.lib = lib$(lib.name).$(extension)
else
shared.lib = lib$(lib.name).$(extension).$(shared.extension)
endif
else
shared.lib :=
endif
################################################################################
### variables: tools ###########################################################
################################################################################
# aliases so we can later define 'compile-$1' and set 'c' or 'cxx' as argument
compile-c := $(CC)
compile-cxx := $(CXX)
################################################################################
### checks #####################################################################
################################################################################
# At this point most variables are defined. Now do some checks and info's
# before rules begin.
# print Makefile.pdlibbuilder version before possible termination
$(info ++++ info: using Makefile.pdlibbuilder version $(version))
# Terminate if target triplet remained empty, to avoid all sorts of confusing
# scenarios and spurious bugs.
ifeq ($(target.triplet),)
$(error Command "$(CC) -dumpmachine" did not return a target triplet, \
needed for a build. \
Is compiler "$(CC)" installed in your PATH? ($(PATH)). \
Does compiler "$(CC)" support option "-dumpmachine"?)
endif
# 'forward declaration' of default target, needed to do checks
all:
# To avoid unpredictable results, make sure the default target is not redefined
# by including makefile.
ifneq ($(.DEFAULT_GOAL), all)
$(error Default target must be 'all'.)
endif
# find out which target(s) will be made
ifdef MAKECMDGOALS
goals := $(MAKECMDGOALS)
else
goals := all
endif
# store path to Pd API m_pd.h if it is found
ifdef PDINCLUDEDIR
mpdh := $(shell ls "$(PDINCLUDEDIR)/m_pd.h")
endif
# store path to pd.dll; if not found, ls will give a useful error
ifeq ($(system), Windows)
pddll := $(shell ls "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll")
endif
# when making target all, check if m_pd.h is found and print info about it
ifeq ($(goals), all)
$(if $(mpdh), \
$(info ++++ info: using Pd API $(mpdh)), \
$(warning Where is Pd API m_pd.h? Do 'make help' for info.))
endif
# print target info
$(info ++++ info: making target $(goals) $(if $(lib.name),in lib $(lib.name)))
# when installing, print installpath info
$(if $(filter install install-lib, $(goals)), $(info ++++ info: \
installpath is '$(installpath)'))
#=== define executables ========================================================
# By default we build class executables, and optionally a shared dynamic link
# lib. When make-lib-executable=yes we build all classes into a single lib
# executable, on the condition that variable lib.setup.sources is defined.
ifeq ($(make-lib-executable),yes)
$(if $(lib.setup.sources), ,\
$(error Can not build library blob because lib.setup.sources is undefined))
executables := $(lib.name).$(extension)
else
executables := $(classes.executables) $(shared.lib)
endif
################################################################################
### rules: special targets #####################################################
################################################################################
# Disable built-in rules. If some target can't be built with the specified
# rules, it should not be built at all.
MAKEFLAGS += --no-builtin-rules
.PRECIOUS:
.SUFFIXES:
.PHONY: all post build-lib \
$(classes) $(makefiledirs) $(makefiles) \
install install-executables install-datafiles install-datadirs \
force clean vars allvars depend help
################################################################################
### rules: build targets #######################################################
################################################################################
# Target all forces the build of targets [$(executables) post] in
# deterministic order. Target $(executables) builds class executables plus
# optional shared lib or alternatively a single lib executable when
# make-lib-executable=true. Target post is optionally defined by
# library makefile.
all: post
post: $(executables)
all:
$(info ++++info: target all in lib $(lib.name) completed)
# build all with -g option turned on for debug symbols
alldebug: c.flags += -g
alldebug: cxx.flags += -g
alldebug: all
#=== class executable ==========================================================
# recipe for linking objects in class executable
# argument $1 = compiler type (c or cxx)
# argument $2 = class basename
define link-class
$(compile-$1) \
$($1.ldflags) $($2.class.ldflags) \
-o $2.$(extension) \
$(addsuffix .$(object.extension), $(basename $($2.class.sources))) \
$(addsuffix .$(object.extension), $(basename $(common.sources))) \
$($1.ldlibs) $($2.class.ldlibs) $(shared.lib)
endef
# general rule for linking object files in class executable
%.$(extension): $(shared.lib)
$(info ++++ info: linking objects in $@ for lib $(lib.name))
$(if $(filter %.cc %.cpp, $($*.class.sources)), \
$(call link-class,cxx,$*), \
$(call link-class,c,$*))
#=== library blob ==============================================================
# build all classes into single executable
build-lib: $(lib.name).$(extension)
$(info ++++ info: library blob $(lib.name).$(extension) completed)
# recipe for linking objects in lib executable
# argument $1 = compiler type (c or cxx)
define link-lib
$(compile-$1) \
$($1.ldflags) $(lib.ldflags) \
-o $(lib.name).$(extension) $(all.objects) \
$($1.ldlibs) $(lib.ldlibs)
endef
# rule for linking objects in lib executable
# declared conditionally to avoid name clashes
ifeq ($(make-lib-executable),yes)
$(lib.name).$(extension): $(all.objects)
$(if $(filter %.cc %.cpp, $(all.sources)), \
$(call link-lib,cxx), \
$(call link-lib,c))
endif
#=== shared dynamic lib ========================================================
# recipe for linking objects in shared executable
# argument $1 = compiler type (c or cxx)
define link-shared
$(compile-$1) \
$(shared.ldflags) \
-o $(shared.lib) $(shared.objects) \
$($1.ldlibs) $(shared.ldlibs)
endef
# rule for linking objects in shared executable
# build recipe is in macro 'link-shared'
$(shared.lib): $(shared.objects)
$(info ++++ info: linking objects in shared lib $@)
$(if $(filter %.cc %.cpp, $(shared.sources)), \
$(call link-shared,cxx), \
$(call link-shared,c))
#=== object files ==============================================================
# recipe to make .o file from source
# argument $1 is compiler type (c or cxx)
define make-object-file
$(info ++++ info: making $@ in lib $(lib.name))
$(compile-$1) \
$($1.flags) \
-o $@ -c $<
endef
# Three rules to create .o files. These are double colon 'terminal' rules,
# meaning they are the last in a rules chain.
%.$(object.extension):: %.c
$(call make-object-file,c)
%.$(object.extension):: %.cc
$(call make-object-file,cxx)
%.$(object.extension):: %.cpp
$(call make-object-file,cxx)
#=== explicit prerequisites for class executables ==============================
# For class executables, prerequisite rules are declared in run time. Target
# 'depend' prints these rules for debugging purposes.
# declare explicit prerequisites rule like 'class: class.extension'
# argument $v is class basename
define declare-class-target
$v: $v.$(extension)
endef
# declare explicit prerequisites rule like 'class.extension: object1.o object2.o'
# argument $v is class basename
define declare-class-executable-target
$v.$(extension): $(addsuffix .$(object.extension), $(basename $($v.class.sources))) \
$(addsuffix .$(object.extension), $(basename $(common.sources)))
endef
# evaluate explicit prerequisite rules for all classes
$(foreach v, $(classes), $(eval $(declare-class-target)))
$(foreach v, $(classes), $(eval $(declare-class-executable-target)))
#=== implicit prerequisites for class executables ==============================
# Evaluating implicit prerequisites (header files) with help from the
# preprocessor is 'expensive' so this is done conditionally and selectively.
# Note that it is also possible to trigger a build via install targets, in
# which case implicit prerequisites are not checked.
# When the Pd include path contains spaces it will mess up the implicit
# prerequisites rules.
disable-dependency-tracking := $(strip $(pdincludepathwithspaces))
ifndef disable-dependency-tracking
must-build-everything := $(filter all, $(goals))
must-build-class := $(filter $(classes), $(goals))
must-build-sources := $(foreach v, $(must-build-class), $($v.class.sources))
endif
# declare implicit prerequisites rule like 'object.o: header1.h header2.h ...'
# argument $1 is input source file(s)
# dir is explicitly added because option -MM strips it by default
define declare-object-target
$(dir $1)$(patsubst %.o:,%.$(object.extension):,$(filter %.o: %.h, $(shell $(CPP) $(depcheck.flags) -MM $1))) $(MAKEFILE_LIST)
endef
# evaluate implicit prerequisite rules when rebuilding everything
ifdef must-build-everything
$(if $(wildcard $(all.objects)), \
$(info ++++ info: evaluating implicit prerequisites in lib $(lib.name).....) \
$(foreach v, $(all.sources), $(eval $(call declare-object-target, $v))))
endif
# evaluate implicit prerequisite rules when selectively building classes
ifdef must-build-class
$(foreach v, $(must-build-sources), \
$(eval $(call declare-object-target, $v)))
$(foreach v, $(shared.sources), \
$(eval $(call declare-object-target, $v)))
endif
################################################################################
### rules: preprocessor and assembly files #####################################
################################################################################
# Preprocessor and assembly output files for bug tracing etc. They are not part
# of the build processes for executables. By default these files are created in
# the current working directory. Dependency tracking is not performed, the build
# is forced instead to make sure it's up to date.
force:
#=== preprocessor file =========================================================
# make preprocessor output file with extension .pre
# argument $1 = compiler type (c or cxx)
define make-preprocessor-file
$(info ++++ info: making preprocessor output file $(notdir $*.pre) \
in current working directory)
$(compile-$1) -E $< $(c.flags) $($1.flags) -o $(notdir $*.pre)
endef
%.pre:: %.c force
$(call make-preprocessor-file,c)
%.pre:: %.cc force
$(call make-preprocessor-file,cxx)
%.pre:: %.cpp force
$(call make-preprocessor-file,cxx)
#=== assembly file =============================================================
# make C / assembly interleaved output file with extension .lst
# argument $1 = compiler type (c or cxx)
define make-assembly-file
$(info ++++ info: making assembly output file $(notdir $*.lst) \
in current working directory)
$(compile-$1) \
-c -Wa,-a,-ad -fverbose-asm \
$($1.flags) \
$< > $(notdir $*.lst)
endef
%.lst:: %.c force
$(call make-assembly-file,c)
%.lst:: %.cc force
$(call make-assembly-file,cxx)
%.lst:: %.cpp force
$(call make-assembly-file,cxx)
################################################################################
### rules: installation targets ################################################
################################################################################
#=== strip =====================================================================
# Stripping of installed binaries will only be done when variable 'stripflags'
# is defined non-empty. No default definition is provided except for Windows
# where the unstripped binaries are large, especially in the case of Mingw-w64.
# Note: while stripping all symbols ('-s' or '--strip-all') is possible for
# Linux and Windows, in the case of OSX only non-global symbols can be stripped
# (option '-x' or '--discard-all').
# Make definition of strip command overridable so it can be defined in an
# environment for cross-compilation.
STRIP ?= strip
# Commands in 'strip-executables' will be executed conditionally in the rule for
# target 'install-executables'.
strip-executables = cd "$(installpath)" && \
$(foreach v, $(executables), $(STRIP) $(stripflags) '$v';)
#=== install ===================================================================
# Install targets depend on successful exit status of target all because nothing
# must be installed in case of a build error.
# -p = preserve time stamps
# -m = set permission mode (as in chmod)
# -d = create all components of specified directories
INSTALL = install
INSTALL_PROGRAM := $(INSTALL) -p -m 644
INSTALL_DATA := $(INSTALL) -p -m 644
INSTALL_DIR := $(INSTALL) -m 755 -d
# strip spaces from file names
executables := $(strip $(executables))
datafiles := $(strip $(datafiles))
datadirs := $(strip $(datadirs))
# Do not make any install sub-target with empty variable definition because the
# install program would exit with an error.
install: $(if $(executables), install-executables)
install: $(if $(datafiles), install-datafiles)
install: $(if $(datadirs), install-datadirs)
install-executables: all
$(INSTALL_DIR) -v "$(installpath)"
$(foreach v, $(executables), \
$(INSTALL_PROGRAM) '$v' "$(installpath)";)
$(info ++++ info: executables of lib $(lib.name) installed \
from $(CURDIR) to $(installpath))
$(if $(stripflags), $(strip-executables),)
install-datafiles: all
$(INSTALL_DIR) -v "$(installpath)"
$(foreach v, $(datafiles), \
$(INSTALL_DATA) '$(v)' "$(installpath)";)
$(info ++++ info: data files of lib $(lib.name) installed \
from $(CURDIR) to $(installpath))
install-datadirs: all
$(foreach v, $(datadirs), $(INSTALL_DIR) "$(installpath)/$v";)
$(foreach v, $(datadirs), \
$(INSTALL_DATA) $(wildcard $v/*) "$(installpath)/$v";)
$(info ++++ info: data directories of lib $(lib.name) installed \
from $(CURDIR) to $(installpath))
################################################################################
### rules: distribution targets ################################################
################################################################################
# TODO
# These targets are implemented in Makefile Template, but I have to figure out
# how to do it under the not-so-strict conditions of Makefile.pdlibbuilder.
# make source package
dist:
@echo "target dist not yet implemented"
# make Debian source package
dpkg-source:
@echo "target dpkg-source not yet implemented"
$(ORIGDIR):
$(DISTDIR):
################################################################################
### rules: clean targets #######################################################
################################################################################
# delete build products from build tree
clean:
rm -f $(all.objects)
rm -f $(classes.executables) $(lib.name).$(extension) $(shared.lib)
rm -f *.pre *.lst
# remove distribution directories and tarballs from build tree
distclean: clean
@echo "target distclean not yet implemented"
################################################################################
### rules: submake targets #####################################################
################################################################################
# Iterate over sub-makefiles or makefiles in other directories.
# When 'continue-make=yes' is set, sub-makes will report 'true' to the parent
# process regardless of their real exit status. This prevents the parent make
# from being aborted by a sub-make error. Useful when you want to quickly find
# out which sub-makes from a large set will succeed.
ifeq ($(continue-make),yes)
continue = || true
endif
# These targets will trigger sub-make processes for entries in 'makefiledirs'
# and 'makefiles'.
all alldebug install clean distclean dist dkpg-source: \
$(makefiledirs) $(makefiles)
# this expands to identical rules for each entry in 'makefiledirs'
$(makefiledirs):
$(MAKE) --directory=$@ $(MAKECMDGOALS) $(continue)
# this expands to identical rules for each entry in 'makefiles'
$(makefiles):
$(MAKE) --directory=$(dir $@) --makefile=$(notdir $@) $(MAKECMDGOALS) $(continue)
################################################################################
### rules: convenience targets #################################################
################################################################################
#=== show variables ============================================================
# Several 'function' macro's cause errors when expanded within a rule or without
# proper arguments. Variables which are set with the define directive are only
# shown by name for that reason.
functions = \
add-class-source \
declare-class-target \
declare-class-executable-target \
declare-object-target \
link-class \
link-lib \
link-shared \
make-object-file \
make-preprocessor-file \
make-assembly-file
# show variables from makefiles
vars:
$(info ++++ info: showing makefile variables:)
$(foreach v,\
$(sort $(filter-out $(functions) functions, $(.VARIABLES))),\
$(if $(filter file, $(origin $v)),\
$(info variable $v = $($v))))
$(foreach v, $(functions), $(info 'function' name: $v))
@echo
# show all variables
allvars:
$(info ++++ info: showing default, automatic and makefile variables:)
$(foreach v, \
$(sort $(filter-out $(functions) functions, $(.VARIABLES))), \
$(info variable ($(origin $v)) $v = $($v)))
$(foreach v, $(functions), $(info 'function' name: $v))
@echo
#=== show dependencies =========================================================
# show generated prerequisites rules
depend:
$(info ++++ info: generated prerequisite rules)
$(foreach v, $(classes), $(info $(declare-class-target)))
$(foreach v, $(classes), $(info $(declare-class-executable-target)))
$(foreach v, $(all.sources), $(info $(call declare-object-target, $v)))
@echo
#=== show help text ============================================================
# brief info about targets and paths
ifdef mpdh
mpdhinfo := $(mpdh)
else
mpdhinfo := m_pd.h was not found. Is Pd installed?
endif
help:
@echo
@echo " Main targets:"
@echo " all: build executables (default target)"
@echo " install: install all components of the library"
@echo " vars: print makefile variables for troubleshooting"
@echo " allvars: print all variables for troubleshooting"
@echo " help: print this help text"
@echo
@echo " Pd API m_pd.h:"
@echo " $(mpdhinfo)"
@echo " You may specify your preferred Pd include directory as argument"
@echo " to the make command, like 'PDINCLUDEDIR=path/to/pd/src'."
@echo
@echo " Path for installation of your libdir(s):"
@echo " $(PDLIBDIR)"
@echo " Alternatively you may specify your path for installation as argument"
@echo " to the make command, like 'PDLIBDIR=path/to/pd-externals'."
@echo
@echo " Default paths are listed in the doc sections in Makefile.pdlibbuilder."
@echo
#=== platform test =============================================================
# This target can be used to test if the compiler for specified PLATFORM is
# correctly defined and available.
dumpmachine:
@$(CC) -dumpmachine
#=== dummy target ==============================================================
coffee:
@echo "Makefile.pdlibbuilder: Can not make coffee. Sorry."
################################################################################
### end of rules sections ######################################################
################################################################################
# for syntax highlighting in vim and github
# vim: set filetype=make:
osc-0.3/pd-lib-builder/README.md 0000664 0000000 0000000 00000013146 14664073227 0016232 0 ustar 00root root 0000000 0000000
### Makefile.pdlibbuilder ###
Helper makefile for Pure Data external libraries. Written by Katja Vetter
March-June 2015 for the public domain and since then developed as a Pd
community project. No warranties. Inspired by Hans Christoph Steiner's Makefile
Template and Stephan Beal's ShakeNMake.
GNU make version >= 3.81 required.
### characteristics ###
* defines build settings based on autodetected target platform
* defines rules to build Pd class- or lib executables from C or C++ sources
* defines rules for libdir installation
* defines convenience targets for developer and user
* evaluates implicit dependencies for non-clean builds
### basic usage ###
In your Makefile, define your Pd lib name and class files, and include
Makefile.pdlibbuilder at the end of the Makefile. Like so:
# Makefile for mylib
lib.name = mylib
class.sources = myclass1.c myclass2.c
datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt
PDLIBBUILDER_DIR=.
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
For files in class.sources it is assumed that class name == source file
basename. The default target builds all classes as individual executables
with Pd's default extension for the platform. For anything more than the
most basic usage, read the documentation sections in Makefile.pdlibbuilder.
### paths ###
Makefile.pdlibbuilder >= v0.4.0 supports pd path variables which can be
defined not only as make command argument but also in the environment, to
override platform-dependent defaults:
PDDIR:
Root directory of 'portable' pd package. When defined, PDINCLUDEDIR and
PDBINDIR will be evaluated as $(PDDIR)/src and $(PDDIR)/bin.
PDINCLUDEDIR:
Directory where Pd API m_pd.h should be found, and other Pd header files.
Overrides the default search path.
PDBINDIR:
Directory where pd.dll should be found for linking (Windows only). Overrides
the default search path.
PDLIBDIR:
Root directory for installation of Pd library directories. Overrides the
default install location.
### platform detection and predefined variables ###
Makefile.pdlibbuilder tries to detect architecture and operating system in
order to define platform-specific variables. Since v0.6.0 we let the compiler
report target platform, rather than taking the build machine as reference. This
simplifies cross compilation. The kind of build options that are predefined:
- optimizations useful for realtime DSP processing
- options strictly required for the platform
- options to make the build work accross a range of CPU's and OS versions
The exact choice and definition predefined variables changes over time, as new
platforms arrive and older platforms become obsolete. The easiest way to get an
overview for your platform is by checking the flags categories in the output of
target `vars`. Variables written in capitals (like `CFLAGS`) are intentionally
exposed as user variables, although technically all makefile variables can be
overridden by make command arguments.
### specific language versions ###
Makefile.pdlibbuilder handles C and C++, but can not detect if your code uses
features of a specific version (like C99, C++11, C++14 etc.). In such cases
your makefile should specify that version as compiler option:
cflags = -std=c++11
Also you may need to be explicit about minimum OSX version. For example, C++11
needs OSX 10.9 or higher:
define forDarwin
cflags = -mmacosx-version-min=10.9
endef
### documentation ###
This README.md provides only basic information. A large comment section inside
Makefile.pdlibbuilder lists and explains the available user variables, default
paths, and targets. The internal documentation reflects the exact functionality
of the particular version. For suggestions about project maintenance and
advanced compilation see tips-tricks.md.
### versioning ###
The project is versioned in MAJOR.MINOR.BUGFIX format (see http://semver.org),
and maintained at https://github.com/pure-data/pd-lib-builder. Pd lib developers
are invited to regulary check for updates, and to contribute and discuss
improvements here. If you really need to distribute a personalized version with
your library, rename Makefile.pdlibbuilder to avoid confusion.
### examples ###
The list of projects using pd-lib-builder can be helpful if you are looking for
examples, from the simplest use case to more complex implementations.
- helloworld: traditional illustration of simplest use case
- pd-windowing: straightforward real world use case of a small library
- pd-nilwind / pd-cyclone: more elaborate source tree
- zexy: migrated from autotools to pd-lib-builder
### projects using pd-lib-builder ###
non-exhaustive list
https://github.com/pure-data/helloworld
https://github.com/electrickery/pd-nilwind
https://github.com/electrickery/pd-maxlib
https://github.com/electrickery/pd-sigpack
https://github.com/electrickery/pd-tof
https://github.com/electrickery/pd-windowing
https://github.com/electrickery/pd-smlib
https://github.com/porres/pd-cyclone
https://github.com/porres/pd-else
https://github.com/porres/pd-psycho
https://git.iem.at/pd/comport
https://git.iem.at/pd/hexloader
https://git.iem.at/pd/iemgui
https://git.iem.at/pd/iemguts
https://git.iem.at/pd/iemlib
https://git.iem.at/pd/iemnet
https://git.iem.at/pd/iem_ambi
https://git.iem.at/pd/iem_tab
https://git.iem.at/pd/iem_adaptfilt
https://git.iem.at/pd/iem_roomsim
https://git.iem.at/pd/iem_spec2
https://git.iem.at/pd/mediasettings
https://git.iem.at/pd/zexy
https://git.iem.at/pd-gui/punish
https://github.com/residuum/PuRestJson
https://github.com/libpd/abl_link
https://github.com/wbrent/timbreID
https://github.com/MetaluNet/moonlib
osc-0.3/pd-lib-builder/tips-tricks.md 0000664 0000000 0000000 00000021012 14664073227 0017540 0 ustar 00root root 0000000 0000000 pd-lib-builder cheatsheet
=========================
# Creating special builds
## Building for non-native platform
Using pd-lib-builder >=0.6.0 we can define variable `PLATFORM` to specify a
target triplet for cross-compilation. Assuming a W32 package for Pd is unzipped
into path `${PDWIN32}`, to build for Windows 32 bit:
make PLATFORM=i686-w64-mingw32 PDDIR="${PDWIN32}"
#### Older pd-lib-builder versions
Using pd-lib-builder < 0.6.0, in the absence of variable `PLATFORM`, you would
instead override variables `system`, `target.arch`, `CC` and / or `CXX`,
`STRIP`. Example:
make system=Windows target.arch=i686 CC=i686-w64-mingw32-gcc STRIP=i686-w64-mingw32-strip PDDIR="${PDWIN32}"
#### Toolchains
To build for non-native OS and/or architecture you need a cross toolchain. On
Linux such toolchains are relatively easy to get. For example Debian Buster
amd64 provides them for the following platforms (install g++ with dependencies
for a given platform to get the whole toolchain):
- `arm-linux-gnueabihf`
- `aarch64-linux-gnu`
- `i686-linux-gnu`
- `i686-w64-mingw32` and `x86_64-w64-mingw32` (install `mingw-w64`)
Cross toolchains for OSX/MacOS are not generally distributed. Project
`osxcross` from Thomas Poechtraeger can create them for Linux.
## Universal binaries on macOS
The compiler, by default, builds for the native architecture of the build
machine. To make a "universal" multi-arch build, specify the desired
archtectures on the command line using the "arch" pd-lib-builder Makefile
variable.
For example, to build a "fat" external for both 64-bit Intel and Arm (Apple
Silicon):
make arch="x86_64 arm64"
If the build is successful, the compiled architectures in the built external can
be confirmed via the `file` command:
~~~sh
% file vbap.pd_darwin
vbap.pd_darwin: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit bundle x86_64] [arm64:Mach-O 64-bit bundle arm64]
vbap.pd_darwin (for architecture x86_64): Mach-O 64-bit bundle x86_64
vbap.pd_darwin (for architecture arm64): Mach-O 64-bit bundle arm64
~~~
Note: The available architectures depend on which macOS version & command line
tools/Xcode combination the build system has. For example, any newer macOS
10.15+ will support both x86_64 (Intel 64-bit) and arm64 (Apple Silicon) while
OSX 10.6 - macOS 10.14 can build for x86_64 and i386 (Intel 32-bit).
## Building double-precision externals
At the time of writing (2023-07-06) there is no official Pd that supports
double-precision numbers yet.
However, if you do get hold of an experimental double-precision Pd, you can
easily build your externals for 64-bit numbers, by passing `floatsize=64`
as an argument to `make`.
Starting with Pd>=0.54, double precision externals use different extensions
from traditional (single-precision) externals.
The extension consists of the OS ("linux", "darwin", "windows"), the CPU
architecture ("amd64" (x86_64), "i386" (x86), "arm64",...) and the floatsize
in bits ("64" for double-precision), followed by the system's native extension
for dynamic libraries (".dll" on Windows, ".so" on macOS/Linux/un*xes).
As of pd-lib-builder==0.7.0, you have to manually pass this extension:
make floatsize=64 extension=windows-amd64-64.dll
make floatsize=64 extension=linux-arm64-64.so
make floatsize=64 extension=darwin-fat-64.so arch="x86_64 arm64"
# Project management
In general it is advised to put the `Makefile.pdlibbuilder` into a separate
subdirectory (e.g. `pd-lib-builder/`).
This makes it much easier to update the `Makefile.pdlibbuilder` later
You *should* also use a variable to the actual path of the Makefile.pdlibbuilder
(even if you keep it in the root-directory), as this allows easy experimenting
with newer (or older) (or site-specific) versions of the pd-lib-builder
Makefile.
~~~make
PDLIBBUILDER_DIR=pd-lib-builder/
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
~~~
## Keeping pd-lib-builder up-to-date
### `git subtree`
With git-subtrees, you make the pd-lib-builder repository (or any other
repository for that matter) part of your own repository - with full history and
everything - put nicely into a distinct subdirectory.
Support for *manipulating* subtrees has been added with Git-v1.7.11 (May 2012).
The nice thing however is, that from "outside" the subtree is part of your
repository like any other directory. E.g. older versions of Git can clone your
repository with the full subtree (and all it's history) just fine.
You can also use git-archive to make a complete snapshot of your repository
(including the subtree) - nice, if you e.g. want self-contained downloads of
your project from git hosting platforms (like Github, Gitlab, Bitbucket,...)
In short, `git subtree` is the better `git submodule`.
So here's how to do it:
#### Initial setup/check-out
This will create a `pd-lib-builder/` directory containing the full history of
the pd-lib-builder repository up to its release `v0.5.0`
~~~sh
git subtree add --prefix=pd-lib-builder/ https://github.com/pure-data/pd-lib-builder v0.5.0
~~~
This will automatically merge the `pd-lib-builder/` history into your current
branch, so everything is ready to go.
#### Cloning your repository with the subtree
Nothing special, really.
Just clone your repository as always:
~~~sh
git clone https://git.example.org/pd/superbonk~.git
~~~
#### Updating the subtree
Time passes and sooner or later you will find, that there is a shiny new
pd-lib-builder with plenty of bugfixes and new features.
To update your local copy to pd-lib-builder's current `master`, simply run:
~~~sh
git subtree pull --prefix pd-lib-builder/ https://github.com/pure-data/pd-lib-builder master
~~~
#### Pulling the updated subtree into existing clones
Again, nothing special.
Just pull as always:
~~~sh
git pull
~~~
#### Further reading
More on the power of `git subtree` can be found online
- https://medium.com/@v/git-subtrees-a-tutorial-6ff568381844
- https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree
- ...
### ~~`git submodule`~~ [DISCOURAGED]
#### Initial setup/check-out
To add a new submodule to your repository, just run `git submodule add` and
commit the changes:
~~~sh
git submodule add https://github.com/pure-data/pd-lib-builder
git commit .gitmodules pd-lib-builder/ -m "Added pd-lib-builder as git-submodule"
~~~
#### Cloning your repository with the submodule
When doing a fresh clone of your repository, pass the `--recursive` option to
automatically fetch all submodules:
~~~sh
git clone --recursive https://git.example.org/pd/superbonk~.git
~~~
If you've cloned non-recursively, you can initialize and update the submodules
manually:
~~~sh
git submodule init
git submodule update
~~~
#### Updating the submodule
Submodules are usually fixed to a given commit in their repository.
To update the `pd-lib-builder` submodule to the current `master` do something
like:
~~~sh
cd pd-lib-builder
git checkout master
git pull
cd ..
git status pd-lib-builder
git commit pd-lib-builder -m "Updated pd-lib-builder to current master"
~~~
#### Pulling the updated submodule into existing clones
After you have pushed the submodule updates in your repository, other clones of
the repository can be updated as follows:
~~~sh
git pull
~~~
The above will make your repository aware, that the submodule is out-of-sync.
~~~sh
$ LANG=C git status pd-lib-builder
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: pd-lib-builder (new commits)
$
~~~
In order to sync the submodule to the correct commit, run the following:
~~~sh
git submodule update
~~~
#### Drawbacks
`git submodule` has a number of drawbacks:
- it requires special commands to synchronize the submodules, in addition to
synching your repository.
- you must make sure to use an URL for the submodule that is accessible to your
potential users. e.g. using `git@github.com:pure-data/pd-lib-builder` is bad,
because it requires everybody who wants to checkout your sources to have a
github-account - even if they could checkout *your* repository anonymously.
- submodules will be excluded from `git archive`. This means, that if you use a
mainstream git provider (like Github, GitLab, Bitbucket,...) and make releases
by creating a `git tag`, the automatically generated zipfiles with the sources
will lack the submodule - and your users will not be able to compile your
source code.
In general, I would suggest to **avoid** `git submodule`, and instead use the
better `git subtree` (above).
osc-0.3/pipelist-help.pd 0000664 0000000 0000000 00000002654 14664073227 0015266 0 ustar 00root root 0000000 0000000 #N canvas 1 53 558 300 10;
#X obj 112 159 pipelist 1000;
#X msg 198 113 5000;
#X floatatom 198 137 5 0 0 0 - - - 0;
#X floatatom 222 72 5 0 0 0 - - - 0;
#X floatatom 181 72 5 0 0 0 - - - 0;
#X obj 112 92 pack s s 3 4 5;
#X obj 112 30 bng 15 250 50 0 empty empty empty 17 7 0 10 #fcfcfc #000000 #000000;
#X msg 135 29 tik;
#X msg 57 43 alpha;
#X msg 50 71 beta;
#X floatatom 63 120 5 0 0 0 - - - 0;
#X obj 112 226 print;
#X obj 130 137 symbol;
#X msg 135 113 bb;
#X text 195 31 pipelist delays lists.;
#X text 237 136 Delay time is in milliseconds.;
#X text 208 161 Argument sets initial delay time.;
#X text 242 248 2007/07/11 Martin Peach;
#X text 229 212 see also:;
#X obj 305 213 pipe;
#X obj 339 213 delay;
#X obj 379 213 timer;
#X obj 135 51 symbol;
#N canvas 500 149 494 344 META 0;
#X text 12 145 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.;
#X text 12 25 LICENSE GPL v2 or later;
#X text 12 125 AUTHOR Roman Haefeli;
#X text 12 5 KEYWORDS control list_op;
#X text 12 45 DESCRIPTION delay lists;
#X text 12 65 INLET_0 anything;
#X text 12 85 INLET_1 float;
#X text 12 105 OUTLET_0 list;
#X restore 492 265 pd META;
#X connect 0 0 11 0;
#X connect 1 0 2 0;
#X connect 2 0 0 1;
#X connect 3 0 5 4;
#X connect 4 0 5 3;
#X connect 5 0 0 0;
#X connect 6 0 5 0;
#X connect 7 0 22 0;
#X connect 8 0 5 0;
#X connect 9 0 5 0;
#X connect 10 0 0 0;
#X connect 12 0 0 0;
#X connect 13 0 12 0;
#X connect 22 0 5 1;
osc-0.3/pipelist.c 0000664 0000000 0000000 00000012446 14664073227 0014157 0 ustar 00root root 0000000 0000000 /* pipelist.c 20070711 Martin Peach based on pipe from x_time.c */
/* 20080706 added anything method for meta-messages */
#include "m_pd.h"
/* -------------------------- pipe -------------------------- */
static t_class *pipelist_class;
typedef struct _hang
{
t_clock *h_clock;
struct _hang *h_next;
struct _pipelist *h_owner;
int h_n; /* number of atoms in h_list */
t_atom *h_atoms; /* pointer to a list of h_n t_atoms */
} t_hang;
typedef struct _pipelist
{
t_object x_obj;
t_float x_deltime;
t_outlet *x_pipelistout;
t_hang *x_hang;
} t_pipelist;
static void *pipelist_new(t_symbol *s, int argc, t_atom *argv);
static void pipelist_hang_free(t_hang *h);
static void pipelist_hang_tick(t_hang *h);
static void pipelist_any_hang_tick(t_hang *h);
static void pipelist_list(t_pipelist *x, t_symbol *s, int ac, t_atom *av);
static void pipelist_anything(t_pipelist *x, t_symbol *s, int ac, t_atom *av);
static void pipelist_flush(t_pipelist *x);
static void pipelist_clear(t_pipelist *x);
void pipelist_setup(void);
static void *pipelist_new(t_symbol *s, int argc, t_atom *argv)
{
t_pipelist *x = (t_pipelist *)pd_new(pipelist_class);
t_float deltime;
if (argc)
{ /* We accept one argument to set the delay time, ignore any further args */
if (argv[0].a_type != A_FLOAT)
{
char stupid[80];
atom_string(&argv[argc-1], stupid, 79);
pd_error(x, "%s: %s: bad time delay value", s->s_name, stupid);
deltime = 0;
}
else deltime = argv[argc-1].a_w.w_float;
}
else deltime = 0;
x->x_pipelistout = outlet_new(&x->x_obj, &s_list);
floatinlet_new(&x->x_obj, &x->x_deltime);
x->x_hang = NULL;
x->x_deltime = deltime;
return (x);
}
static void pipelist_hang_free(t_hang *h)
{
freebytes(h->h_atoms, h->h_n*sizeof(t_atom));
clock_free(h->h_clock);
freebytes(h, sizeof(t_hang));
}
static void pipelist_hang_tick(t_hang *h)
{
t_pipelist *x = h->h_owner;
t_hang *h2, *h3;
/* excise h from the linked list of hangs */
if (x->x_hang == h) x->x_hang = h->h_next;
else for (h2 = x->x_hang; ((h3 = h2->h_next)!=NULL); h2 = h3)
{
if (h3 == h)
{
h2->h_next = h3->h_next;
break;
}
}
/* output h's list */
outlet_list(x->x_pipelistout, &s_list, h->h_n, h->h_atoms);
/* free h */
pipelist_hang_free(h);
}
static void pipelist_any_hang_tick(t_hang *h)
{
t_pipelist *x = h->h_owner;
t_hang *h2, *h3;
/* excise h from the linked list of hangs */
if (x->x_hang == h) x->x_hang = h->h_next;
else for (h2 = x->x_hang; ((h3 = h2->h_next)!=NULL); h2 = h3)
{
if (h3 == h)
{
h2->h_next = h3->h_next;
break;
}
}
/* output h's atoms */
outlet_anything(x->x_pipelistout, h->h_atoms[0].a_w.w_symbol, h->h_n-1, &h->h_atoms[1]);
/* free h */
pipelist_hang_free(h);
}
static void pipelist_list(t_pipelist *x, t_symbol *s, int ac, t_atom *av)
{
(void)s;
if (x->x_deltime > 0)
{ /* if delay is real, save the list for output in delay milliseconds */
t_hang *h;
int i;
h = (t_hang *)getbytes(sizeof(t_hang));
h->h_n = ac;
h->h_atoms = (t_atom *)getbytes(h->h_n*sizeof(t_atom));
for (i = 0; i < h->h_n; ++i)
h->h_atoms[i] = av[i];
h->h_next = x->x_hang;
x->x_hang = h;
h->h_owner = x;
h->h_clock = clock_new(h, (t_method)pipelist_hang_tick);
clock_delay(h->h_clock, (x->x_deltime >= 0 ? x->x_deltime : 0));
}
/* otherwise just pass the list straight through */
else outlet_list(x->x_pipelistout, &s_list, ac, av);
}
static void pipelist_anything(t_pipelist *x, t_symbol *s, int ac, t_atom *av)
{
if (x->x_deltime > 0)
{ /* if delay is real, save the list for output in delay milliseconds */
t_hang *h;
int i;
h = (t_hang *)getbytes(sizeof(t_hang));
h->h_n = ac+1;
h->h_atoms = (t_atom *)getbytes(h->h_n*sizeof(t_atom));
SETSYMBOL(&h->h_atoms[0], s);
for (i = 1; i < h->h_n; ++i)
h->h_atoms[i] = av[i-1];
h->h_next = x->x_hang;
x->x_hang = h;
h->h_owner = x;
h->h_clock = clock_new(h, (t_method)pipelist_any_hang_tick);
clock_delay(h->h_clock, (x->x_deltime >= 0 ? x->x_deltime : 0));
}
/* otherwise just pass it straight through */
else outlet_anything(x->x_pipelistout, s, ac, av);
}
static void pipelist_flush(t_pipelist *x)
{
while (x->x_hang) pipelist_hang_tick(x->x_hang);
}
static void pipelist_clear(t_pipelist *x)
{
t_hang *hang;
while ((hang = x->x_hang) != NULL)
{
x->x_hang = hang->h_next;
pipelist_hang_free(hang);
}
}
void pipelist_setup(void)
{
pipelist_class = class_new(gensym("pipelist"),
(t_newmethod)pipelist_new, (t_method)pipelist_clear,
sizeof(t_pipelist), 0, A_GIMME, 0);
class_addlist(pipelist_class, pipelist_list);
class_addanything(pipelist_class, pipelist_anything);
class_addmethod(pipelist_class, (t_method)pipelist_flush, gensym("flush"), 0);
class_addmethod(pipelist_class, (t_method)pipelist_clear, gensym("clear"), 0);
}
/* end of pipelist.c*/
osc-0.3/routeOSC-help.pd 0000664 0000000 0000000 00000007210 14664073227 0015131 0 ustar 00root root 0000000 0000000 #N canvas 81 134 1070 758 12;
#X obj 111 524 cnv 15 820 130 empty empty empty 20 12 0 14 #fcc458 #404040 0;
#X obj 69 10 udpreceive 9997;
#X floatatom 260 82 3 0 0 0 - - - 0;
#X floatatom 291 82 3 0 0 0 - - - 0;
#X floatatom 322 82 3 0 0 0 - - - 0;
#X floatatom 353 82 3 0 0 0 - - - 0;
#X text 220 81 from;
#X obj 69 134 unpackOSC;
#X text 469 78 see also:;
#X obj 545 79 packOSC;
#X floatatom 628 154 10 0 0 1 millisecond_delay - - 0;
#X obj 69 161 pipelist;
#X text 629 529 arguments are OSC addresses to be routed;
#X text 188 677 see:;
#X text 348 678 for a way to send OSC over TCP or serial connections.;
#X obj 223 678 unpackOSCstream;
#X obj 35 188 print unpacked;
#X obj 260 58 unpack 0 0 0 0 0;
#X floatatom 385 82 8 0 0 0 - - - 0;
#X obj 186 35 route received from;
#X floatatom 186 103 5 0 0 0 - - - 0;
#X text 229 103 bytes;
#X text 198 9 Open [packOSC-help] to send packets to this patch.;
#X text 139 156 If the OSC packet has a timetag \, [pipelist] will delay it until the time occurs;
#X obj 417 623 print match_1;
#X obj 493 601 print match_2;
#X obj 646 557 print no_match;
#N canvas 499 254 494 344 META 0;
#X text 12 155 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.;
#X text 12 25 LICENSE GPL v2 or later;
#X text 12 5 KEYWORDS control list_op;
#X text 12 75 INLET_0 list verbosity paths set;
#X text 12 95 OUTLET_N list;
#X text 12 115 OUTLET_R list;
#X text 12 135 AUTHOR Martin Peach;
#X text 12 45 DESCRIPTION routes lists as OSC packets.;
#X restore 933 655 pd META;
#X text 757 627 2012/03/13 Martin Peach;
#X text 144 119 [unpackOSC] parses lists of floats (only integers on [0..255]) as OSC packets.;
#X obj 111 219 cnv 15 600 160 empty empty empty 20 12 0 14 #c4fcc4 #404040 0;
#X obj 272 382 cnv 15 600 140 empty empty empty 20 12 0 14 #fcc4c4 #404040 0;
#X msg 221 313 set /left;
#X msg 198 290 set /left /right;
#X text 326 311 (but new outlets can't be created);
#X msg 172 264 paths;
#X msg 149 241 verbosity \$1;
#X obj 149 223 tgl 15 0 empty empty empty 17 7 0 10 #fcfcfc #000000 #000000 0 1;
#X text 167 219 set to 1 for debugging;
#X text 395 335 routeOSC will match deep paths;
#X text 332 289 [set( reassigns outputs from left to right;
#X text 226 264 [paths( will print the current OSC addresses to Pd console;
#X msg 290 382 /test any2 4 5;
#X msg 402 494 1 2 3;
#X msg 244 336 set /one/two/three;
#X msg 267 359 set /*;
#X text 320 358 this will match any OSC message;
#X msg 357 449 /* zap!;
#X msg 380 472 zorro trope;
#X text 479 472 not a valid OSC message;
#X text 450 493 not a valid OSC message;
#X text 408 382 input is a message whose selector is an OSC path;
#X text 511 404 or a list whose first element is an OSC path;
#X obj 569 579 print match_3;
#X obj 417 529 routeOSC /test /west /one/two;
#X msg 334 426 /one/two/three yikes!;
#X msg 312 404 list /west/rate any1 2 3;
#X text 111 556 [routeOSC] routes Open Sound Control* messages \, or lists of anything beginning with a /path;
#X text 113 599 *See http://opensoundcontrol.org/spec-1_0.html;
#X connect 1 0 7 0;
#X connect 1 1 19 0;
#X connect 7 0 11 0;
#X connect 7 0 16 0;
#X connect 7 1 10 0;
#X connect 7 1 11 1;
#X connect 11 0 54 0;
#X connect 17 0 2 0;
#X connect 17 1 3 0;
#X connect 17 2 4 0;
#X connect 17 3 5 0;
#X connect 17 4 18 0;
#X connect 19 0 20 0;
#X connect 19 1 17 0;
#X connect 32 0 54 0;
#X connect 33 0 54 0;
#X connect 35 0 54 0;
#X connect 36 0 54 0;
#X connect 37 0 36 0;
#X connect 42 0 54 0;
#X connect 43 0 54 0;
#X connect 44 0 54 0;
#X connect 45 0 54 0;
#X connect 47 0 54 0;
#X connect 48 0 54 0;
#X connect 54 0 24 0;
#X connect 54 1 25 0;
#X connect 54 2 53 0;
#X connect 54 3 26 0;
#X connect 55 0 54 0;
#X connect 56 0 54 0;
osc-0.3/routeOSC.c 0000664 0000000 0000000 00000054037 14664073227 0014033 0 ustar 00root root 0000000 0000000 /* routeOSC.c 20060424 by Martin Peach, based on OSCroute and OSC-pattern-match.c. */
/* OSCroute.c header follows: */
/*
Written by Adrian Freed, The Center for New Music and Audio Technologies,
University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04
The Regents of the University of California (Regents).
Permission to use, copy, modify, distribute, and distribute modified versions
of this software and its documentation without fee and without a signed
licensing agreement, is hereby granted, provided that the above copyright
notice, this paragraph and the following two paragraphs appear in all copies,
modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The OSC webpage is http://opensoundcontrol.org
*/
/* OSC-route.c
Max object for OSC-style dispatching
To-do:
Match a pattern against a pattern?
[Done: Only Slash-Star is allowed, see MyPatternMatch.]
Declare outlet types / distinguish leaf nodes from other children
More sophisticated (2-pass?) allmessages scheme
set message?
pd
-------------
-- tweaks for Win32 www.zeggz.com/raf 13-April-2002
*/
/* OSC-pattern-match.c header follows: */
/*
Copyright © 1998. The Regents of the University of California (Regents).
All Rights Reserved.
Written by Matt Wright, The Center for New Music and Audio Technologies,
University of California, Berkeley.
Permission to use, copy, modify, distribute, and distribute modified versions
of this software and its documentation without fee and without a signed
licensing agreement, is hereby granted, provided that the above copyright
notice, this paragraph and the following two paragraphs appear in all copies,
modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The OpenSound Control WWW page is
http://opensoundcontrol.org
*/
/*
OSC-pattern-match.c
Matt Wright, 3/16/98
Adapted from oscpattern.c, by Matt Wright and Amar Chaudhury
*/
/* the required include files */
#include "m_pd.h"
#define MAX_NUM 128 // maximum number of paths (prefixes) we can route
typedef struct _routeOSC
{
t_object x_obj; /* required header */
int x_num; /* Number of prefixes we store */
int x_verbosity; /* level of debug output required */
const char **x_prefixes; /* the OSC addresses to be matched */
int *x_prefix_depth; /* the number of slashes in each prefix */
void **x_outlets; /* one for each prefix plus one for everything else */
} t_routeOSC;
/* prototypes */
void routeOSC_setup(void);
static void routeOSC_free(t_routeOSC *x);
static int MyPatternMatch (void *x, const char *pattern, const char *test);
static void routeOSC_doanything(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv);
static void routeOSC_bang(t_routeOSC *x);
static void routeOSC_float(t_routeOSC *x, t_floatarg f);
static void routeOSC_symbol(t_routeOSC *x, t_symbol *s);
static void routeOSC_list(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv);
static void *routeOSC_new(t_symbol *s, int argc, t_atom *argv);
static void routeOSC_set(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv);
static void routeOSC_paths(t_routeOSC *x);
static void routeOSC_verbosity(t_routeOSC *x, t_floatarg v);
static int routeOSC_count_slashes(const char *prefix);
static const char *NextSlashOrNull(const char *p);
static const char *NthSlashOrNull(const char *p, int n);
static void StrCopyUntilSlash(char *target, const char *source);
static void StrCopyUntilNthSlash(char *target, const char *source, int n);
/* from
OSC-pattern-match.c
*/
static const char *theWholePattern; /* Just for warning messages */
static int MatchBrackets (void *x, const char *pattern, const char *test);
static int MatchList (void *x, const char *pattern, const char *test);
static int PatternMatch (void *x, const char *pattern, const char *test);
static t_class *routeOSC_class;
t_symbol *ps_list, *ps_complain, *ps_emptySymbol;
static int MyPatternMatch (void *x, const char *pattern, const char *test)
{
// This allows the special case of "routeOSC /* " to be an outlet that
// matches anything; i.e., it always outputs the input with the first level
// of the address stripped off.
if (test[0] == '*' && test[1] == '\0') return 1;
else return PatternMatch(x, pattern, test);
}
static void routeOSC_free(t_routeOSC *x)
{
freebytes(x->x_prefixes, x->x_num*sizeof(char *)); /* the OSC addresses to be matched */
freebytes(x->x_prefix_depth, x->x_num*sizeof(int)); /* the number of slashes in each prefix */
freebytes(x->x_outlets, (x->x_num+1)*sizeof(void *)); /* one for each prefix plus one for everything else */
}
/* initialization routine */
void routeOSC_setup(void)
{
routeOSC_class = class_new(gensym("routeOSC"), (t_newmethod)routeOSC_new,
(t_method)routeOSC_free, sizeof(t_routeOSC), 0, A_GIMME, 0);
class_addanything(routeOSC_class, routeOSC_doanything);
class_addbang(routeOSC_class, routeOSC_bang);
class_addfloat(routeOSC_class, routeOSC_float);
class_addsymbol(routeOSC_class, routeOSC_symbol);
class_addlist(routeOSC_class, routeOSC_list);
class_addmethod(routeOSC_class, (t_method)routeOSC_set, gensym("set"), A_GIMME, 0);
class_addmethod(routeOSC_class, (t_method)routeOSC_paths, gensym("paths"), 0);
class_addmethod(routeOSC_class, (t_method)routeOSC_verbosity, gensym("verbosity"), A_DEFFLOAT, 0);
ps_emptySymbol = gensym("");
// post("routeOSC object version 1.0 by Martin Peach, based on OSCroute by Matt Wright. pd: jdl Win32 raf.");
// post("OSCroute Copyright © 1999 Regents of the Univ. of California. All Rights Reserved.");
}
/* instance creation routine */
static void *routeOSC_new(t_symbol *s, int argc, t_atom *argv)
{
t_routeOSC *x = (t_routeOSC *)pd_new(routeOSC_class); // get memory for a new object & initialize
int i;
if (argc > MAX_NUM)
{
pd_error(x, "* %s: too many arguments: %d (max %d)", s->s_name, argc, MAX_NUM);
return 0;
}
x->x_num = 0;
/* first verify that all arguments are symbols whose first character is '/' */
for (i = 0; i < argc; ++i)
{
if (argv[i].a_type == A_SYMBOL)
{
if (argv[i].a_w.w_symbol->s_name[0] == '/')
{ /* Now that's a nice prefix */
++(x->x_num);
}
else
{
pd_error(x, "%s: argument %d does not begin with a slash(/).", s->s_name, i);
return(0);
}
}
else
{
pd_error(x, "%s: argument %d is not a symbol.", s->s_name, i);
return 0;
}
}
/* now allocate the storage for each path */
x->x_prefixes = (const char **)getzbytes(x->x_num*sizeof(const char *)); /* the OSC addresses to be matched */
x->x_prefix_depth = (int *)getzbytes(x->x_num*sizeof(int)); /* the number of slashes in each prefix */
x->x_outlets = (void **)getzbytes((x->x_num+1)*sizeof(void *)); /* one for each prefix plus one for everything else */
/* put the pointer to the path in x_prefixes */
/* put the number of levels in x_prefix_depth */
for (i = 0; i < x->x_num; ++i)
{
x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name;
x->x_prefix_depth[i] = routeOSC_count_slashes(x->x_prefixes[i]);
}
/* Have to create the outlets in reverse order */
/* well, not in pd ? */
for (i = 0; i <= x->x_num; i++)
{
x->x_outlets[i] = outlet_new(&x->x_obj, &s_list);
}
x->x_verbosity = 0; /* quiet by default */
return (x);
}
static void routeOSC_set(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv)
{
int i;
(void)s;
if (argc > x->x_num)
{
pd_error (x, "routeOSC: too many paths");
return;
}
for (i = 0; i < argc; ++i)
{
if (argv[i].a_type != A_SYMBOL)
{
pd_error (x, "routeOSC: path %d not a symbol", i);
return;
}
if (argv[i].a_w.w_symbol->s_name[0] != '/')
{
pd_error (x, "routeOSC: path %d doesn't start with /", i);
return;
}
}
for (i = 0; i < argc; ++i)
{
if (argv[i].a_w.w_symbol->s_name[0] == '/')
{ /* Now that's a nice prefix */
x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name;
x->x_prefix_depth[i] = routeOSC_count_slashes(x->x_prefixes[i]);
}
}
}
static void routeOSC_paths(t_routeOSC *x)
{ /* print out the paths we are matching */
int i;
for (i = 0; i < x->x_num; ++i) post("path[%d]: %s (depth %d)", i, x->x_prefixes[i], x->x_prefix_depth[i]);
}
static void routeOSC_verbosity(t_routeOSC *x, t_floatarg v)
{
x->x_verbosity = (v == 0)? 0: 1;
if (x->x_verbosity) post("routeOSC_verbosity(%p) is %d", x, x->x_verbosity);
}
static int routeOSC_count_slashes(const char *prefix)
{ /* find the path depth of the prefix by counting the numberof slashes */
int i = 0;
const char *p = prefix;
while (*p != '\0') if (*p++ == '/') i++;
return i;
}
static void routeOSC_doanything(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv)
{
const char *pattern, *nextSlash;
int i = 0, pattern_depth = 0, matchedAnything = 0;
int noPath = 0; // nonzero if we are dealing with a simple list (as from a previous [routeOSC])
pattern = s->s_name;
if (x->x_verbosity) post("routeOSC_doanything(%p): pattern is %s", x, pattern);
if (pattern[0] != '/')
{
/* output unmatched data on rightmost outlet */
if (x->x_verbosity) post("routeOSC_doanything no OSC path(%p) , %d args", x, argc);
outlet_anything(x->x_outlets[x->x_num], s, argc, argv);
return;
}
pattern_depth = routeOSC_count_slashes(pattern);
if (x->x_verbosity) post("routeOSC_doanything(%p): pattern_depth is %i", x, pattern_depth);
nextSlash = NextSlashOrNull(pattern+1);
if (*nextSlash == '\0')
{ /* pattern_depth == 1 */
/* last level of the address, so we'll output the argument list */
for (i = 0; i < x->x_num; ++i)
{
if
(
(x->x_prefix_depth[i] <= pattern_depth)
&& (MyPatternMatch(x, pattern+1, x->x_prefixes[i]+1))
)
{
++matchedAnything;
if (noPath)
{ // just a list starting with a symbol
// The special symbol is s
if (x->x_verbosity) post("routeOSC_doanything _1_(%p): (%d) noPath: s is \"%s\"", x, i, s->s_name);
outlet_anything(x->x_outlets[i], s, argc, argv);
}
else // normal OSC path
{
// I hate stupid Max lists with a special first element
if (argc == 0)
{
if (x->x_verbosity) post("routeOSC_doanything _2_(%p): (%d) no args", x, i);
outlet_bang(x->x_outlets[i]);
}
else if (argv[0].a_type == A_SYMBOL)
{
// Promote the symbol that was argv[0] to the special symbol
if (x->x_verbosity) post("routeOSC_doanything _3_(%p): (%d) symbol: is \"%s\"", x, i, argv[0].a_w.w_symbol->s_name);
outlet_anything(x->x_outlets[i], argv[0].a_w.w_symbol, argc-1, argv+1);
}
else if (argc > 1)
{
// Multiple arguments starting with a number, so naturally we have
// to use a special function to output this "list", since it's what
// Max originally meant by "list".
if (x->x_verbosity) post("routeOSC_doanything _4_(%p): (%d) list:", x, i);
outlet_list(x->x_outlets[i], 0L, argc, argv);
}
else
{
// There was only one argument, and it was a number, so we output it
// not as a list
if (argv[0].a_type == A_FLOAT)
{
if (x->x_verbosity) post("routeOSC_doanything _5_(%p): (%d) a single float", x, i);
outlet_float(x->x_outlets[i], argv[0].a_w.w_float);
}
else
{
pd_error(x, "* routeOSC: unrecognized atom type!");
}
}
}
}
}
}
else
{
/* There's more address after this part, so our output list will begin with
the next slash. */
t_symbol *restOfPattern = 0; /* avoid the gensym unless we have to output */
char patternBegin[1000];
/* Get the incoming pattern to match against all our prefixes */
for (i = 0; i < x->x_num; ++i)
{
restOfPattern = 0;
if (x->x_prefix_depth[i] <= pattern_depth)
{
StrCopyUntilNthSlash(patternBegin, pattern+1, x->x_prefix_depth[i]);
if (x->x_verbosity)
post("routeOSC_doanything _6_(%p): (%d) patternBegin is %s", x, i, patternBegin);
if (MyPatternMatch(x, patternBegin, x->x_prefixes[i]+1))
{
if (x->x_verbosity)
post("routeOSC_doanything _7_(%p): (%d) matched %s depth %d", x, i, x->x_prefixes[i], x->x_prefix_depth[i]);
++matchedAnything;
nextSlash = NthSlashOrNull(pattern+1, x->x_prefix_depth[i]);
if (x->x_verbosity)
post("routeOSC_doanything _8_(%p): (%d) nextSlash %s [%d]", x, i, nextSlash, nextSlash[0]);
if (*nextSlash != '\0')
{
if (x->x_verbosity) post("routeOSC_doanything _9_(%p): (%d) more pattern", x, i);
restOfPattern = gensym(nextSlash);
outlet_anything(x->x_outlets[i], restOfPattern, argc, argv);
}
else if (argc == 0)
{
if (x->x_verbosity) post("routeOSC_doanything _10_(%p): (%d) no more pattern, no args", x, i);
outlet_bang(x->x_outlets[i]);
}
else
{
if (x->x_verbosity) post("routeOSC_doanything _11_(%p): (%d) no more pattern, %d args", x, i, argc);
if (argv[0].a_type == A_SYMBOL) // Promote the symbol that was argv[0] to the special symbol
outlet_anything(x->x_outlets[i], argv[0].a_w.w_symbol, argc-1, argv+1);
else
outlet_anything(x->x_outlets[i], gensym("list"), argc, argv);
}
}
}
}
}
if (!matchedAnything)
{
// output unmatched data on rightmost outlet a la normal 'route' object, jdl 20020908
if (x->x_verbosity) post("routeOSC_doanything _13_(%p) unmatched %d, %d args", x, i, argc);
outlet_anything(x->x_outlets[x->x_num], s, argc, argv);
}
}
static void routeOSC_bang(t_routeOSC *x)
{
/* output non-OSC data on rightmost outlet */
if (x->x_verbosity) post("routeOSC_bang (%p)", x);
outlet_bang(x->x_outlets[x->x_num]);
}
static void routeOSC_float(t_routeOSC *x, t_floatarg f)
{
/* output non-OSC data on rightmost outlet */
if (x->x_verbosity) post("routeOSC_float (%p) %f", x, f);
outlet_float(x->x_outlets[x->x_num], f);
}
static void routeOSC_symbol(t_routeOSC *x, t_symbol *s)
{
/* output non-OSC data on rightmost outlet */
if (x->x_verbosity) post("routeOSC_symbol (%p) %s", x, s->s_name);
outlet_symbol(x->x_outlets[x->x_num], s);
}
static void routeOSC_list(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv)
{
/* output non-OSC data on rightmost outlet */
if (x->x_verbosity) post("routeOSC_list (%p) s=%s argc is %d", x, s->s_name, argc);
if (0 == argc) pd_error(x, "routeOSC_list (%p) empty list", x);/* this should never happen but catch it just in case... */
else if (argv[0].a_type == A_SYMBOL) routeOSC_doanything(x, argv[0].a_w.w_symbol, argc-1, &argv[1]);
else if (argv[0].a_type == A_FLOAT)
{
if (x->x_verbosity) post("routeOSC_list (%p) floats:", x);
outlet_list(x->x_outlets[x->x_num], 0L, argc, argv);
}
}
static const char *NextSlashOrNull(const char *p)
{
while (*p != '/' && *p != '\0') p++;
return p;
}
static const char *NthSlashOrNull(const char *p, int n)
{
int i;
for (i = 0; i < n; ++i)
{
while (*p != '/')
{
if (*p == '\0') return p;
p++;
}
if (i < n-1) p++; /* skip the slash unless it's the nth slash */
}
return p;
}
static void StrCopyUntilSlash(char *target, const char *source)
{
while (*source != '/' && *source != '\0')
{
*target = *source;
++target;
++source;
}
*target = 0;
}
static void StrCopyUntilNthSlash(char *target, const char *source, int n)
{ /* copy the path up but not including the nth slash */
int i = n;
while (i > 0)
{
while (*source != '/')
{
*target = *source;
if (*source == '\0') return;
++target;
++source;
}
i--;
/* got a slash. If it's the nth, don't include it */
if (i == 0)
{
*target = 0;
return;
}
*target = *source;
++source;
++target;
}
}
/* from
OSC-pattern-match.c
Matt Wright, 3/16/98
Adapted from oscpattern.c, by Matt Wright and Amar Chaudhury
*/
static int PatternMatch (void *x, const char *pattern, const char *test)
{
theWholePattern = pattern;
if (pattern == 0 || pattern[0] == 0) return test[0] == 0;
if (test[0] == 0)
{
if (pattern[0] == '*') return PatternMatch (x, pattern+1, test);
return 0;
}
switch (pattern[0])
{
case 0:
return test[0] == 0;
case '?':
return PatternMatch (x, pattern+1, test+1);
case '*':
if (PatternMatch (x, pattern+1, test)) return 1;
return PatternMatch (x, pattern, test+1);
case ']':
case '}':
pd_error(x, "routeOSC: Spurious %c in pattern \".../%s/...\"",pattern[0], theWholePattern);
return 0;
case '[':
return MatchBrackets (x, pattern,test);
case '{':
return MatchList (x, pattern,test);
case '\\':
if (pattern[1] == 0) return test[0] == 0;
if (pattern[1] == test[0]) return PatternMatch (x, pattern+2,test+1);
return 0;
default:
if (pattern[0] == test[0]) return PatternMatch (x, pattern+1,test+1);
return 0;
}
}
/* we know that pattern[0] == '[' and test[0] != 0 */
static int MatchBrackets (void *x, const char *pattern, const char *test)
{
int result;
int negated = 0;
const char *p = pattern;
if (pattern[1] == 0)
{
pd_error(x, "routeOSC: Unterminated [ in pattern \".../%s/...\"", theWholePattern);
return 0;
}
if (pattern[1] == '!')
{
negated = 1;
p++;
}
while (*p != ']')
{
if (*p == 0)
{
pd_error(x, "Unterminated [ in pattern \".../%s/...\"", theWholePattern);
return 0;
}
if (p[1] == '-' && p[2] != 0)
{
if (test[0] >= p[0] && test[0] <= p[2])
{
result = !negated;
goto advance;
}
}
if (p[0] == test[0])
{
result = !negated;
goto advance;
}
p++;
}
result = negated;
advance:
if (!result) return 0;
while (*p != ']')
{
if (*p == 0)
{
pd_error(x, "Unterminated [ in pattern \".../%s/...\"", theWholePattern);
return 0;
}
p++;
}
return PatternMatch (x, p+1,test+1);
}
static int MatchList (void *x, const char *pattern, const char *test)
{
const char *restOfPattern, *tp = test;
for(restOfPattern = pattern; *restOfPattern != '}'; restOfPattern++)
{
if (*restOfPattern == 0)
{
pd_error(x, "Unterminated { in pattern \".../%s/...\"", theWholePattern);
return 0;
}
}
restOfPattern++; /* skip close curly brace */
pattern++; /* skip open curly brace */
while (1)
{
if (*pattern == ',')
{
if (PatternMatch (x, restOfPattern, tp)) return 1;
tp = test;
++pattern;
}
else if (*pattern == '}') return PatternMatch (x, restOfPattern, tp);
else if (*pattern == *tp)
{
++pattern;
++tp;
}
else
{
tp = test;
while (*pattern != ',' && *pattern != '}') pattern++;
if (*pattern == ',') pattern++;
}
}
}
/* end of routeOSC.c */
osc-0.3/unpackOSC-help.pd 0000664 0000000 0000000 00000004335 14664073227 0015261 0 ustar 00root root 0000000 0000000 #N canvas 4 80 688 469 10;
#X obj 56 236 cnv 15 100 60 empty empty empty 20 12 0 14 #00fc04 #404040 0;
#X obj 75 250 unpackOSC;
#X floatatom 176 268 10 0 0 1 - - - 0;
#X obj 75 357 pipelist;
#X text 34 417 see:;
#X text 181 417 for a way to send OSC over TCP or serial connections.;
#X obj 69 418 unpackOSCstream;
#X obj 102 323 print unpacked;
#X text 131 353 If the OSC packet has a timetag \, [pipelist] will delay it until the time occurs;
#N canvas 521 651 494 344 META 0;
#X text 12 155 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.;
#X text 12 25 LICENSE GPL v2 or later;
#X text 12 5 KEYWORDS control list_op;
#X text 12 45 DESCRIPTION parses lists of floats (only integers on [0..255]) assuming they represent OSC packets.;
#X text 12 75 INLET_0 list of floats on [0..255];
#X text 12 95 OUTLET_0 OSC message;
#X text 13 115 OUTLET_1 milliseconds until timetag time;
#X text 12 135 AUTHOR Martin Peach;
#X restore 609 422 pd META;
#X obj 75 83 packOSC;
#X obj 102 123 print bytes;
#X msg 47 38 /some/path 1 2 3;
#X msg 26 17 [ \, /another/one/ 4 \, ];
#X obj 75 384 print delayed;
#X msg 68 59 timetagoffset 1.1e+06;
#X obj 114 103 print bundle_depth;
#X text 514 377 2012/02/14 Martin Peach;
#X text 267 25 [unpackOSC] processes lists of floats (only integers on [0..255]) as though they were OSC packets.;
#X text 84 144 <- usually the bytes pass over the network with [udpsend]/[udpreceive] \, or serial port using [comport] with [slipenc] and [slipdec];
#X text 208 59 timetag offset is in microseconds relative to sender's clock;
#X text 243 261 second outlet is timetag offset in millieconds relative to receiver's clock;
#X msg 98 215 usepdtime \$1;
#X obj 194 215 tgl 20 0 empty empty empty 0 -10 0 12 #fcfcfc #000000 #000000 0 1;
#X text 226 208 Use Pd logical time (default) or system time. Setting to 1 \, re-syncs Pd's time to the system time.;
#X obj 75 303 t a a;
#X obj 75 103 t a a;
#X connect 1 0 25 0;
#X connect 1 1 3 1;
#X connect 1 1 2 0;
#X connect 3 0 14 0;
#X connect 10 0 26 0;
#X connect 10 1 16 0;
#X connect 12 0 10 0;
#X connect 13 0 10 0;
#X connect 15 0 10 0;
#X connect 22 0 1 0;
#X connect 23 0 22 0;
#X connect 25 0 3 0;
#X connect 25 1 7 0;
#X connect 26 0 1 0;
#X connect 26 1 11 0;
osc-0.3/unpackOSC.c 0000664 0000000 0000000 00000054734 14664073227 0014162 0 ustar 00root root 0000000 0000000 /* unpackOSC is like dumpOSC but outputs a list consisting of a single symbol for the path */
/* and a list of floats and/or symbols for the data, and adds an outlet for a time delay. */
/* This allows for the separation of the protocol and its transport. */
/* Started by Martin Peach 20060420 */
/* dumpOSC.c header follows: */
/*
Written by Matt Wright and Adrian Freed, The Center for New Music and
Audio Technologies, University of California, Berkeley. Copyright (c)
1992,93,94,95,96,97,98,99,2000,01,02,03,04 The Regents of the University of
California (Regents).
Permission to use, copy, modify, distribute, and distribute modified versions
of this software and its documentation without fee and without a signed
licensing agreement, is hereby granted, provided that the above copyright
notice, this paragraph and the following two paragraphs appear in all copies,
modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The OSC webpage is http://opensoundcontrol.org
*/
/*
dumpOSC.c
server that displays OpenSoundControl messages sent to it
for debugging client udp and UNIX protocol
by Matt Wright, 6/3/97
modified from dumpSC.c, by Matt Wright and Adrian Freed
version 0.2: Added "-silent" option a.k.a. "-quiet"
version 0.3: Incorporated patches from Nicola Bernardini to make
things Linux-friendly. Also added ntohl() in the right places
to support little-endian architectures.
to-do:
More robustness in saying exactly what's wrong with ill-formed
messages. (If they don't make sense, show exactly what was
received.)
Time-based features: print time-received for each packet
Clean up to separate OSC parsing code from socket/select stuff
pd: branched from http://www.cnmat.berkeley.edu/OpenSoundControl/src/dumpOSC/dumpOSC.c
-------------
-- added pd functions
-- socket is made differently than original via pd mechanisms
-- tweaks for Win32 www.zeggz.com/raf 13-April-2002
-- the OSX changes from cnmat didnt make it here yet but this compiles
on OSX anyway.
*/
//#define DEBUG 1
#include "packingOSC.h"
#include "OSC_timeTag.h"
static t_class *unpackOSC_class;
typedef struct _unpackOSC
{
t_object x_obj;
t_outlet *x_data_out;
t_outlet *x_delay_out;
int x_bundle_flag;/* non-zero if we are processing a bundle */
int x_recursion_level;/* number of times we reenter unpackOSC_list */
int x_abort_bundle;/* non-zero if unpackOSC_list is not well formed */
int x_reentry_count;
int x_use_pd_time;
OSCTimeTag x_pd_timetag;
double x_pd_timeref;
} t_unpackOSC;
void unpackOSC_setup(void);
static void *unpackOSC_new(void);
static void unpackOSC_free(t_unpackOSC *x);
static void unpackOSC_list(t_unpackOSC *x, t_symbol *s, int argc, t_atom *argv);
static void unpackOSC_usepdtime(t_unpackOSC *x, t_floatarg f);
static t_symbol* unpackOSC_path(t_unpackOSC *x, const char *path, size_t length);
static void unpackOSC_Smessage(t_unpackOSC *x, t_atom *data_at, int *data_atc, void *v, int n);
static void unpackOSC_PrintTypeTaggedArgs(t_unpackOSC *x, t_atom *data_at, int *data_atc, void *v, int n);
static void unpackOSC_PrintHeuristicallyTypeGuessedArgs(t_unpackOSC *x, t_atom *data_at, int *data_atc, void *v, int n, int skipComma);
static const char *unpackOSC_DataAfterAlignedString(t_unpackOSC *x, const char *string, const char *boundary);
static int unpackOSC_IsNiceString(t_unpackOSC *x, const char *string, const char *boundary);
static void *unpackOSC_new(void)
{
t_unpackOSC *x;
x = (t_unpackOSC *)pd_new(unpackOSC_class);
x->x_data_out = outlet_new(&x->x_obj, &s_list);
x->x_delay_out = outlet_new(&x->x_obj, &s_float);
x->x_bundle_flag = 0;
x->x_recursion_level = 0;
x->x_abort_bundle = 0;
unpackOSC_usepdtime(x, 1.);
return (x);
}
static void unpackOSC_free(t_unpackOSC *x)
{
(void)x;
}
void unpackOSC_setup(void)
{
unpackOSC_class = class_new(gensym("unpackOSC"),
(t_newmethod)unpackOSC_new, (t_method)unpackOSC_free,
sizeof(t_unpackOSC), 0, 0);
class_addlist(unpackOSC_class, (t_method)unpackOSC_list);
class_addmethod(unpackOSC_class, (t_method)unpackOSC_usepdtime,
gensym("usepdtime"), A_FLOAT, 0);
}
static void unpackOSC_usepdtime(t_unpackOSC *x, t_floatarg f)
{
x->x_use_pd_time = (int)f;
if(x->x_use_pd_time) {
x->x_pd_timetag = OSCTT_Now();
x->x_pd_timeref = clock_getlogicaltime();
} else {
x->x_pd_timetag.seconds = x->x_pd_timetag.fraction = 0;
x->x_pd_timeref = 0;
}
}
/* unpackOSC_list expects an OSC packet in the form of a list of floats on [0..255] */
static void unpackOSC_dolist(t_unpackOSC *x, int argc, const char *buf, t_atom out_argv[MAX_MESG])
{
debugprint(">>> %s(%p, %d, %p)\n", __FUNCTION__, x, argc, buf);
debugprint(">>> unpackOSC_list: %d bytes, abort=%d, reentry_count %d recursion_level %d\n",
argc, x->x_abort_bundle, x->x_reentry_count, x->x_recursion_level);
if(x->x_abort_bundle) return; /* if backing quietly out of the recursive stack */
x->x_reentry_count++;
if ((argc%4) != 0)
{
pd_error(x, "unpackOSC: Packet size (%d) not a multiple of 4 bytes: dropping packet", argc);
goto unpackOSC_list_out;
}
if ((argc >= 8) && (strncmp(buf, "#bundle", 8) == 0))
{ /* This is a bundle message. */
int i = 16; /* Skip "#bundle\0" and time tag */
OSCTimeTag tt;
double delta = 0.;
debugprint("unpackOSC: bundle msg:\n");
if (argc < 16)
{
pd_error(x, "unpackOSC: Bundle message too small (%d bytes) for time tag", argc);
goto unpackOSC_list_out;
}
x->x_bundle_flag = 1;
/* Print the time tag */
debugprint("unpackOSC bundle timetag: [ %x.%0x\n", ntohl(*((uint32_t *)(buf+8))),
ntohl(*((uint32_t *)(buf+12))));
/* convert the timetag into a millisecond delay from now */
tt.seconds = ntohl(*((uint32_t *)(buf+8)));
tt.fraction = ntohl(*((uint32_t *)(buf+12)));
/* pd can use a delay in milliseconds */
if (tt.seconds == 0 && tt.fraction ==1) {
delta = 0;
} else {
OSCTimeTag now;
if (x->x_use_pd_time) {
double deltaref = clock_gettimesince(x->x_pd_timeref);
now = OSCTT_offsetms(x->x_pd_timetag, deltaref);
} else {
now = OSCTT_Now();
}
delta = OSCTT_getoffsetms(tt, now);
}
outlet_float(x->x_delay_out, delta);
/* Note: if we wanted to actually use the time tag as a little-endian
64-bit int, we'd have to word-swap the two 32-bit halves of it */
while(i < argc)
{
int size = ntohl(*((int *) (buf + i)));
if ((size % 4) != 0)
{
pd_error(x, "unpackOSC: Bad size count %d in bundle (not a multiple of 4)", size);
goto unpackOSC_list_out;
}
if ((size + i + 4) > argc)
{
pd_error(x, "unpackOSC: Bad size count %d in bundle (only %d bytes left in entire bundle)",
size, argc-i-4);
goto unpackOSC_list_out;
}
/* Recursively handle element of bundle */
x->x_recursion_level++;
debugprint("unpackOSC: bundle depth %d\n", x->x_recursion_level);
if (x->x_recursion_level > MAX_BUNDLE_NESTING)
{
pd_error(x, "unpackOSC: bundle depth %d exceeded", MAX_BUNDLE_NESTING);
x->x_abort_bundle = 1;/* we need to back out of the recursive stack*/
goto unpackOSC_list_out;
}
debugprint("unpackOSC: bundle calling unpackOSC_list(x=%p, size=%d, buf[%d]=%p)\n",
x, size, i+4, &buf[i+4]);
unpackOSC_dolist(x, size, &buf[i+4], out_argv);
i += 4 + size;
}
if (i != argc)
{
pd_error(x, "unpackOSC: This can't happen");
}
x->x_bundle_flag = 0; /* end of bundle */
debugprint("unpackOSC: bundle end ] depth is %d\n", x->x_recursion_level);
} else if ((argc == 24) && (strcmp(buf, "#time") == 0)) {
post("unpackOSC: Time message: %s\n :).\n", buf);
goto unpackOSC_list_out;
} else { /* This is not a bundle message or a time message */
int out_argc = 0; /* number of atoms to be output; atoms are stored in out_argv */
const char*messageName = buf;
int messageLen;
const char*args = unpackOSC_DataAfterAlignedString(x, messageName, buf+argc);
t_symbol *path;
debugprint("unpackOSC: message name string: %s length %d\n", messageName, argc);
if (args == 0)
{
pd_error(x, "unpackOSC: Bad message name string: Dropping entire message.");
goto unpackOSC_list_out;
}
messageLen = args-messageName;
/* put the OSC path into a single symbol */
path = unpackOSC_path(x, messageName, messageLen); /* returns 0 if path failed */
if (path == 0)
{
pd_error(x, "unpackOSC: Bad message path: Dropping entire message.");
goto unpackOSC_list_out;
}
debugprint("unpackOSC: message '%s', args=%p\n", path?path->s_name:0, args);
debugprint("unpackOSC_list calling unpackOSC_Smessage: message length %d\n", argc-messageLen);
unpackOSC_Smessage(x, out_argv, &out_argc, (void *)args, argc-messageLen);
if (0 == x->x_bundle_flag)
outlet_float(x->x_delay_out, 0); /* no delay for message not in a bundle */
outlet_anything(x->x_data_out, path, out_argc, out_argv);
}
x->x_abort_bundle = 0;
unpackOSC_list_out:
x->x_recursion_level = 0;
x->x_reentry_count--;
}
static void unpackOSC_list(t_unpackOSC *x, t_symbol *s, int argc, t_atom *argv)
{
static t_atom out_atoms[MAX_MESG]; /* symbols making up the payload */
char raw[MAX_MESG];/* bytes making up the entire OSC message */
int i;
(void)s;
if(!argc) {
return;
}
if ((argc%4) != 0)
{
pd_error(x, "unpackOSC: Packet size (%d) not a multiple of 4 bytes: dropping packet", argc);
return;
}
if(argc>MAX_MESG) {
pd_error(x, "unpackOSC: Packet size (%d) greater than max (%d). Change MAX_MESG and recompile if you want more.", argc, MAX_MESG);
return;
}
/* copy the list to a byte buffer, checking for bytes only */
for (i = 0; i < argc; ++i)
{
if (argv[i].a_type == A_FLOAT)
{
int j = (int)argv[i].a_w.w_float;
/* if ((j == argv[i].a_w.w_float) && (j >= 0) && (j <= 255)) */
/* this can miss bytes between 128 and 255 because they are interpreted somewhere as negative, */
/* so change to this: */
if ((j == argv[i].a_w.w_float) && (j >= -128) && (j <= 255))
{
raw[i] = (char)j;
}
else
{
pd_error(x, "unpackOSC: Data[%d] out of range (%d), dropping packet",
i, (int)argv[i].a_w.w_float);
return;
}
}
else
{
pd_error(x, "unpackOSC: Data[%d] not float, dropping packet", i);
return;
}
}
unpackOSC_dolist(x, argc, raw, out_atoms);
}
static t_symbol*unpackOSC_path(t_unpackOSC *x, const char *path, size_t len)
{
size_t i;
if(len<1)
{
pd_error(x, "unpackOSC: No Path, dropping message");
return 0;
}
if (path[0] != '/')
{
pd_error(x, "unpackOSC: Path doesn't begin with \"/\", dropping message");
return 0;
}
for (i = 1; i < len; ++i)
{
if (path[i] == '\0')
{ /* the end of the path: turn path into a symbol */
return gensym(path);
}
}
pd_error(x, "unpackOSC: Path too long, dropping message");
return 0;
}
#define SMALLEST_POSITIVE_FLOAT 0.000001f
static void unpackOSC_Smessage(t_unpackOSC *x, t_atom *data_at, int *data_atc, void *v, int n)
{
char *chars = v;
if (n != 0)
{
if (chars[0] == ',')
{
if (chars[1] != ',')
{
debugprint("unpackOSC_Smessage calling unpackOSC_PrintTypeTaggedArgs: message length %d\n", n);
/* This message begins with a type-tag string */
unpackOSC_PrintTypeTaggedArgs(x, data_at, data_atc, v, n);
}
else
{
debugprint("unpackOSC_Smessage calling unpackOSC_PrintHeuristicallyTypeGuessedArgs: message length %d, skipComma 1\n", n);
/* Double comma means an escaped real comma, not a type string */
unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, data_at, data_atc, v, n, 1);
}
}
else
{
debugprint("unpackOSC_Smessage calling unpackOSC_PrintHeuristicallyTypeGuessedArgs: message length %d, skipComma 0\n", n);
unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, data_at, data_atc, v, n, 0);
}
}
}
static void unpackOSC_PrintTypeTaggedArgs(t_unpackOSC *x, t_atom *data_at, int *data_atc, void *v, int n)
{
const char *typeTags, *thisType, *p;
int myargc = *data_atc;
t_atom *mya = data_at;
typeTags = v;
if (!unpackOSC_IsNiceString(x, typeTags, typeTags+n))
{
debugprint("unpackOSC_PrintTypeTaggedArgs not nice string\n");
/* No null-termination, so maybe it wasn't a type tag
string after all */
unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, data_at, data_atc, v, n, 0);
return;
}
debugprint("unpackOSC_PrintTypeTaggedArgs calling unpackOSC_DataAfterAlignedString %p to %p\n", typeTags, typeTags+n);
p = unpackOSC_DataAfterAlignedString(x, typeTags, typeTags+n);
debugprint("unpackOSC_PrintTypeTaggedArgs p is %p: ", p);
if (p == NULL) return; /* malformed message */
for (thisType = typeTags + 1; *thisType != 0; ++thisType)
{
switch (*thisType)
{
case 'b': /* blob: an int32 size count followed by that many 8-bit bytes */
{
int i, blob_bytes = ntohl(*((int *) p));
debugprint("blob: %u bytes\n", blob_bytes);
p += 4;
for (i = 0; i < blob_bytes; ++i, ++p, ++myargc)
SETFLOAT(mya+myargc,(*(unsigned char *)p));
while (i%4)
{
++i;
++p;
}
break;
}
case 'm': /* MIDI message is the next four bytes*/
{
int i;
debugprint("MIDI: 0x%08X\n", ntohl(*((int *) p)));
for (i = 0; i < 4; ++i, ++p, ++myargc)
SETFLOAT(mya+myargc, (*(unsigned char *)p));
break;
}
case 'i': case 'r': case 'c':
debugprint("integer: %d\n", ntohl(*((int *) p)));
SETFLOAT(mya+myargc,(signed)ntohl(*((int *) p)));
myargc++;
p += 4;
break;
case 'f':
{
intfloat32 thisif;
thisif.i = ntohl(*((int *) p));
debugprint("float: %f\n", thisif.f);
SETFLOAT(mya+myargc, thisif.f);
myargc++;
p += 4;
break;
}
case 'h': case 't':
debugprint("[A 64-bit int]\n");
pd_error(x, "unpackOSC: PrintTypeTaggedArgs: [A 64-bit int] not implemented");
p += 8;
break;
case 'd':
debugprint("[A 64-bit float]\n");
pd_error(x, "unpackOSC: PrintTypeTaggedArgs: [A 64-bit float] not implemented");
p += 8;
break;
case 's': case 'S':
if (!unpackOSC_IsNiceString(x, p, typeTags+n))
{
pd_error(x, "unpackOSC: PrintTypeTaggedArgs: Type tag said this arg is a string but it's not!\n");
return;
}
else
{
debugprint("string: \"%s\"\n", p);
SETSYMBOL(mya+myargc,gensym(p));
myargc++;
p = unpackOSC_DataAfterAlignedString(x, p, typeTags+n);
}
break;
case 'T':
debugprint("[True]\n");
SETFLOAT(mya+myargc,1.);
myargc++;
break;
case 'F':
debugprint("[False]\n");
SETFLOAT(mya+myargc,0.);
myargc++;
break;
case 'N':
debugprint("[Nil]\n");
SETFLOAT(mya+myargc,0.);
myargc++;
break;
case 'I':
debugprint("[Infinitum]\n");
SETSYMBOL(mya+myargc,gensym("INF"));
myargc++;
break;
default:
debugprint("unpackOSC_PrintTypeTaggedArgs unknown typetag %c (%0X)\n", *thisType, *thisType);
pd_error(x, "unpackOSC: PrintTypeTaggedArgs: [Unrecognized type tag %c]", *thisType);
return;
}
}
*data_atc = myargc;
}
static void unpackOSC_PrintHeuristicallyTypeGuessedArgs(t_unpackOSC *x, t_atom *data_at, int *data_atc, void *v, int n, int skipComma)
{
int i;
int *ints;
intfloat32 thisif;
const char *chars, *string, *nextString;
int myargc = *data_atc;
t_atom* mya = data_at;
/* Go through the arguments 32 bits at a time */
ints = v;
chars = v;
for (i = 0; i < n/4; )
{
string = &chars[i*4];
thisif.i = ntohl(ints[i]);
/* Reinterpret the (potentially byte-reversed) thisif as a float */
if (thisif.i >= -1000 && thisif.i <= 1000000)
{
debugprint("%d ", thisif.i);
SETFLOAT(mya+myargc,(t_float) (thisif.i));
myargc++;
i++;
}
else if (thisif.f >= -1000.f && thisif.f <= 1000000.f &&
(thisif.f <=0.0f || thisif.f >= SMALLEST_POSITIVE_FLOAT))
{
debugprint("%f ", thisif.f);
SETFLOAT(mya+myargc,thisif.f);
myargc++;
i++;
}
else if (unpackOSC_IsNiceString(x, string, chars+n))
{
nextString = unpackOSC_DataAfterAlignedString(x, string, chars+n);
debugprint("\"%s\" ", (i == 0 && skipComma) ? string +1 : string);
SETSYMBOL(mya+myargc,gensym(string));
myargc++;
i += (nextString-string) / 4;
}
else
{
/* unhandled .. ;) */
post("unpackOSC: PrintHeuristicallyTypeGuessedArgs: indeterminate type: 0x%x xx", ints[i]);
i++;
}
*data_atc = myargc;
}
}
#define STRING_ALIGN_PAD 4
static const char *unpackOSC_DataAfterAlignedString(t_unpackOSC *x, const char *string, const char *boundary)
{
/* The argument is a block of data beginning with a string. The
string has (presumably) been padded with extra null characters
so that the overall length is a multiple of STRING_ALIGN_PAD
bytes. Return a pointer to the next byte after the null
byte(s). The boundary argument points to the character after
the last valid character in the buffer---if the string hasn't
ended by there, something's wrong.
If the data looks wrong, return 0 */
int i;
debugprint("unpackOSC_DataAfterAlignedString %p (boundary - string = %ld)\n", string, boundary-string);
if ((boundary - string) %4 != 0)
{
pd_error(x, "unpackOSC: DataAfterAlignedString: bad boundary");
return 0;
}
for (i = 0; string[i] != '\0'; i++)
{
debugprint("%02X(%c) ", string[i], string[i]);
if (string + i >= boundary)
{
pd_error(x, "unpackOSC: DataAfterAlignedString: Unreasonably long string");
return 0;
}
}
/* Now string[i] is the first null character */
debugprint("\nunpackOSC_DataAfterAlignedString first null character at %p\n", &string[i]);
i++;
for (; (i % STRING_ALIGN_PAD) != 0; i++)
{
if (string + i >= boundary)
{
pd_error(x, "unpackOSC: DataAfterAlignedString: Unreasonably long string");
return 0;
}
if (string[i] != '\0')
{
pd_error(x, "unpackOSC: DataAfterAlignedString: Incorrectly padded string");
return 0;
}
}
debugprint("unpackOSC_DataAfterAlignedString first non-null character at %p\n", &string[i]);
return string+i;
}
static int unpackOSC_IsNiceString(t_unpackOSC *x, const char *string, const char *boundary)
{
/* Arguments same as DataAfterAlignedString(). Is the given "string"
really an OSC-string? I.e., is it
"A sequence of non-null ASCII characters followed by a null, followed
by 0-3 additional null characters to make the total number of bits a
multiple of 32"? (OSC 1.0)
Returns 1 if true, else 0. */
int i;
if ((boundary - string) %4 != 0)
{
pd_error(x, "unpackOSC: IsNiceString: bad boundary\n");
return 0;
}
/* any non-zero byte will be accepted, so UTF-8 sequences will also be accepted -- not strictly OSC v1.0 */
for (i = 0; string[i] != '\0'; i++)
/* if ((!isprint(string[i])) || (string + i >= boundary)) return 0; */ /* only ASCII printable chars */
/*if ((0==(string[i]&0xE0)) || (string + i >= boundary)) return 0;*/ /* onl;y ASCII space (0x20) and above */
if (string + i >= boundary) return 0; /* anything non-zero */
/* If we made it this far, it's a null-terminated sequence of characters
within the given boundary. Now we just make sure it's null padded... */
/* Now string[i] is the first null character */
i++;
for (; (i % STRING_ALIGN_PAD) != 0; i++)
if (string[i] != '\0') return 0;
return 1;
}
/* end of unpackOSC.c */
osc-0.3/unpackOSCstream-help.pd 0000664 0000000 0000000 00000002711 14664073227 0016471 0 ustar 00root root 0000000 0000000 #N canvas 1 53 638 435 10;
#X text 8 369 check also:;
#X obj 240 386 tcpreceive;
#X obj 183 386 tcpsend;
#X obj 318 386 tcpserver;
#X obj 389 386 tcpclient;
#X text 472 374 Author: Roman Haefeli;
#X text 472 390 Version: 2008-09-09;
#X text 158 65 [unpackOSCstream] is meant to be a replacement for [unpackOSC]
\, when receiving from a stream based protocol \, such as TCP.;
#X obj 18 197 unpackOSCstream;
#X obj 18 17 tcpreceive 9995;
#X obj 18 222 print;
#X floatatom 105 221 5 0 0 0 - - -;
#X text 159 283 reference:;
#X obj 10 386 packOSCstream;
#X obj 110 386 unpackOSC;
#X text 160 300 https://opensoundcontrol.stanford.edu/spec-1_0.html
: Section "OSC Packets";
#X text 141 221 milliseconds delay;
#X text 158 119 [unpackOSCstream] will only be able to decode OSC packets
or bundles created by [packOSCstream]. OSC packets that were generated
by [packOSC] will cause errors or wrong output.;
#N canvas 507 340 494 344 META 0;
#X text 12 155 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan
Wilkes for Pd version 0.42.;
#X text 12 25 LICENSE GPL v2 or later;
#X text 12 5 KEYWORDS control network abstraction;
#X text 12 46 DESCRIPTION meant to be a replacement for [unpackOSC]
\, when receiving from a stream based protocol \, such as TCP.;
#X text 12 75 INLET_0 anything;
#X text 12 95 OUTLET_0 anything;
#X text 12 115 OUTLET_1 float;
#X text 12 135 AUTHOR Roman Haefeli;
#X restore 591 413 pd META;
#X connect 8 0 10 0;
#X connect 8 1 11 0;
#X connect 9 0 8 0;
osc-0.3/unpackOSCstream.pd 0000664 0000000 0000000 00000000462 14664073227 0015544 0 ustar 00root root 0000000 0000000 #N canvas 693 326 361 208 10;
#X obj 9 7 inlet;
#X obj 9 135 unpackOSC;
#X obj 9 158 outlet;
#X obj 60 158 outlet;
#X text 172 144 Author: Roman Haefeli;
#X text 172 160 Version: 2011-02-01;
#X obj 9 71 mrpeach/slipdec 65536;
#X connect 0 0 6 0;
#X connect 1 0 2 0;
#X connect 1 1 3 0;
#X connect 6 0 1 0;