1 /*
2 * oscsend - Send OpenSound Control message.
3 *
4 * Copyright (C) 2008 Kentaro Fukuchi <kentaro@fukuchi.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * TODO:
21 * - support binary blob.
22 * - support TimeTag.
23 * - receive replies.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <math.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <config.h>
33 #include <limits.h>
34 #include <lo/lo.h>
35
usage(void)36 void usage(void)
37 {
38 printf("oscsend version %s\n"
39 "Copyright (C) 2008 Kentaro Fukuchi\n\n"
40 "Usage: oscsend hostname port address types values...\n"
41 "or oscsend url address types values...\n"
42 "Send OpenSound Control message via UDP.\n\n"
43 "Description\n"
44 "hostname: specifies the remote host's name.\n"
45 "port : specifies the port number to connect to the remote host.\n"
46 "url : specifies the destination parameters using a liblo URL.\n"
47 " e.g. UDP \"osc.udp://localhost:9000\"\n"
48 " Multicast \"osc.udp://224.0.1.9:9000\"\n"
49 " TCP \"osc.tcp://localhost:9000\"\n"
50 " File \"file:///tmp/dump.osc\"\n"
51 " stdout -\n\n"
52
53 "address : the OSC address where the message to be sent.\n"
54 "types : specifies the types of the following values.\n",
55 VERSION);
56 printf(" %c - 32bit integer\n", LO_INT32);
57 printf(" %c - 64bit integer\n", LO_INT64);
58 printf(" %c - 32bit floating point number\n", LO_FLOAT);
59 printf(" %c - 64bit (double) floating point number\n",
60 LO_DOUBLE);
61 printf(" %c - string\n", LO_STRING);
62 printf(" %c - symbol\n", LO_SYMBOL);
63 printf(" %c - char\n", LO_CHAR);
64 printf(" %c - 4 byte midi packet (8 digits hexadecimal)\n",
65 LO_MIDI);
66 printf(" %c - TRUE (no value required)\n", LO_TRUE);
67 printf(" %c - FALSE (no value required)\n", LO_FALSE);
68 printf(" %c - NIL (no value required)\n", LO_NIL);
69 printf(" %c - INFINITUM (no value required)\n", LO_INFINITUM);
70 printf("values : space separated values.\n\n"
71 "Example\n"
72 "$ oscsend localhost 7777 /sample/address %c%c%c%c 1 3.14 hello\n",
73 LO_INT32, LO_TRUE, LO_FLOAT, LO_STRING);
74 }
75
create_message(char ** argv)76 lo_message create_message(char **argv)
77 {
78 /* Note:
79 * argv[0] <- types
80 * argv[1..] <- values
81 */
82 int i, argi;
83 lo_message message;
84 const char *types, *arg;
85 int values;
86
87 message = lo_message_new();
88 if (argv[0] == NULL) {
89 /* empty message is allowed. */
90 values = 0;
91 } else {
92 types = argv[0];
93 values = strlen(types);
94 }
95
96 argi = 1;
97 arg = NULL;
98 for (i = 0; i < values; i++) {
99 switch(types[i]) {
100 case LO_INT32:
101 case LO_FLOAT:
102 case LO_STRING:
103 case LO_BLOB:
104 case LO_INT64:
105 case LO_TIMETAG:
106 case LO_DOUBLE:
107 case LO_SYMBOL:
108 case LO_CHAR:
109 case LO_MIDI:
110 arg = argv[argi];
111 if (arg == NULL) {
112 fprintf(stderr, "Value #%d is not given.\n", i + 1);
113 goto EXIT;
114 }
115 break;
116 default:
117 break;
118 }
119 switch (types[i]) {
120 case LO_INT32:
121 {
122 char *endp;
123 int64_t v;
124
125 v = strtol(arg, &endp, 10);
126 if (*endp != '\0') {
127 fprintf(stderr, "An invalid value was given: '%s'\n",
128 arg);
129 goto EXIT;
130 }
131 if ((v == LONG_MAX || v == LONG_MIN) && errno == ERANGE) {
132 fprintf(stderr, "Value out of range: '%s'\n", arg);
133 goto EXIT;
134 }
135 if (v > INT_MAX || v < INT_MIN) {
136 fprintf(stderr, "Value out of range: '%s'\n", arg);
137 goto EXIT;
138 }
139 lo_message_add_int32(message, (int32_t) v);
140 argi++;
141 break;
142 }
143 case LO_INT64:
144 {
145 char *endp;
146 int64_t v;
147
148 v = strtoll(arg, &endp, 10);
149 if (*endp != '\0') {
150 fprintf(stderr, "An invalid value was given: '%s'\n",
151 arg);
152 goto EXIT;
153 }
154 if ((v == LONG_MAX || v == LONG_MIN) && errno == ERANGE) {
155 fprintf(stderr, "Value out of range: '%s'\n", arg);
156 goto EXIT;
157 }
158 lo_message_add_int64(message, v);
159 argi++;
160 break;
161 }
162 case LO_FLOAT:
163 {
164 char *endp;
165 float v;
166
167 #ifdef __USE_ISOC99
168 v = strtof(arg, &endp);
169 #else
170 v = (float) strtod(arg, &endp);
171 #endif /* __USE_ISOC99 */
172 if (*endp != '\0') {
173 fprintf(stderr, "An invalid value was given: '%s'\n",
174 arg);
175 goto EXIT;
176 }
177 lo_message_add_float(message, v);
178 argi++;
179 break;
180 }
181 case LO_DOUBLE:
182 {
183 char *endp;
184 double v;
185
186 v = strtod(arg, &endp);
187 if (*endp != '\0') {
188 perror(NULL);
189 fprintf(stderr, "An invalid value was given: '%s'\n",
190 arg);
191 goto EXIT;
192 }
193 lo_message_add_double(message, v);
194 argi++;
195 break;
196 }
197 case LO_STRING:
198 lo_message_add_string(message, arg);
199 argi++;
200 break;
201 case LO_SYMBOL:
202 lo_message_add_symbol(message, arg);
203 argi++;
204 break;
205 case LO_CHAR:
206 lo_message_add_char(message, arg[0]);
207 argi++;
208 break;
209 case LO_MIDI:
210 {
211 unsigned int midi;
212 uint8_t packet[4];
213 int ret;
214
215 ret = sscanf(arg, "%08x", &midi);
216 if (ret != 1) {
217 fprintf(stderr,
218 "An invalid hexadecimal value was given: '%s'\n",
219 arg);
220 goto EXIT;
221 }
222 packet[0] = (midi >> 24) & 0xff;
223 packet[1] = (midi >> 16) & 0xff;
224 packet[2] = (midi >> 8) & 0xff;
225 packet[3] = midi & 0xff;
226 lo_message_add_midi(message, packet);
227 argi++;
228 break;
229 }
230 case LO_TRUE:
231 lo_message_add_true(message);
232 break;
233 case LO_FALSE:
234 lo_message_add_false(message);
235 break;
236 case LO_NIL:
237 lo_message_add_nil(message);
238 break;
239 case LO_INFINITUM:
240 lo_message_add_infinitum(message);
241 break;
242 default:
243 fprintf(stderr, "Type '%c' is not supported or invalid.\n",
244 types[i]);
245 goto EXIT;
246 break;
247 }
248 }
249
250 return message;
251 EXIT:
252 lo_message_free(message);
253 return NULL;
254 }
255
main(int argc,char ** argv)256 int main(int argc, char **argv)
257 {
258 lo_address target;
259 lo_message message;
260 int ret, i=1, dump_to_file=0, dump_to_stdout=0;
261 char file_uri[256];
262
263 if (argc < 3) {
264 usage();
265 exit(1);
266 }
267
268 if (argv[i] == NULL) {
269 fprintf(stderr, "No hostname is given.\n");
270 exit(1);
271 }
272
273 if (strstr(argv[i], "://")!=0) {
274 if(strncmp(argv[i], "file://", 7)==0 && strlen(argv[i])>7) {
275 dump_to_file=1;
276 memset(file_uri, 0, sizeof(file_uri));
277 strncpy(file_uri, argv[i]+7, sizeof(file_uri)-1);
278 }
279 else {
280 target = lo_address_new_from_url(argv[i]);
281 if (target == NULL) {
282 fprintf(stderr, "Failed to open %s\n", argv[i]);
283 exit(1);
284 }
285 }
286 i++;
287 }
288 else if(strncmp(argv[i], "-", 1)==0 && strlen(argv[i])==1) {
289 dump_to_stdout=1;
290 i++;
291 }
292 else if (argv[i+1] == NULL) {
293 fprintf(stderr, "No port number is given.\n");
294 exit(1);
295 }
296 else {
297 target = lo_address_new(argv[i], argv[i+1]);
298 if (target == NULL) {
299 fprintf(stderr, "Failed to open %s:%s\n", argv[i], argv[i+1]);
300 exit(1);
301 }
302 i += 2;
303 }
304
305 if (argv[i] == NULL) {
306 fprintf(stderr, "No path is given.\n");
307 exit(1);
308 }
309
310 message = create_message(&argv[i+1]);
311 if (message == NULL) {
312 fprintf(stderr, "Failed to create OSC message.\n");
313 exit(1);
314 }
315
316 if(!dump_to_file && !dump_to_stdout) {
317 lo_address_set_ttl(target, 1);
318
319 ret = lo_send_message(target, argv[i], message);
320 if (ret == -1) {
321 fprintf(stderr, "An error occurred: %s\n",
322 lo_address_errstr(target));
323 lo_message_free(message);
324 exit(1);
325 }
326 }
327 else {
328 FILE *fout = NULL;
329
330 if(dump_to_file) {
331 FILE *f=fopen(file_uri, "w+");
332 if(f == NULL) {
333 fprintf(stderr, "An error occurred: Could not open file for writing OSC message: '%s'\n",
334 file_uri);
335 lo_message_free(message);
336 exit(1);
337 }
338 fout=f;
339 }
340 else if(dump_to_stdout) {
341 fout=stdout;
342 }
343
344 size_t size;
345 void *msg_ptr=lo_message_serialise(message, argv[i], NULL, &size);
346 fwrite(msg_ptr, 1, size, fout);
347 fflush(fout);
348 fclose(fout);
349 free(msg_ptr);
350 }
351
352 lo_message_free(message);
353
354 return 0;
355 }
356