1 module osc.message;
2 
3 import std.range;
4 import std.conv;
5 import std.algorithm;
6 import std.variant;
7 import std.datetime;
8 
9 import osc.packet;
10 import osc.address;
11 import osc.typetag;
12 import osc.oscstring;
13 
14 
15 
16 /// Supported types for an OSC argument.
17 ///
18 alias OSCArgument = Algebraic!(
19 	int, 
20 	float, 
21 	string, 
22 	ubyte[], 
23 	bool, 
24 	typeof(null),
25 	char,
26 	SysTime
27 );
28 
29 
30 
31 /// An OSC message contains an address and a set of arguments.
32 ///
33 class OSCMessage : OSCPacket
34 {
35 
36 	private {
37 		Address				_address;
38 		TypeTagString		_types;
39 		OSCArgument[]		_arguments;
40 	}
41 
42 
43 
44 	/// Constructs a new empty OSC message.
45 	///
46 	this( )
47 	{
48 		_address = Address( [], false );
49 	}
50 
51 	/// Constructs a new OSC message using an address.
52 	///
53 	this( string addr )
54 	in
55 	{
56 		assert( addr.length > 0 );
57 	}
58 	do
59 	{
60 		this( );
61 		_address = Address( addr );
62 	}
63 	
64 	/// Constructs a new OSC message using an address.
65 	///
66 	this( Address addr )
67 	{
68 		this( );
69 		_address = Address( addr );
70 	}
71 
72 	/// Constructs a new OSC message by cloning another one.
73 	///
74 	this( const(OSCMessage) copy )
75 	{
76 		this( );
77 		_address = Address( copy._address );
78 		_arguments = copy._arguments.dup;
79 	}
80 
81 
82 	/// Gets the message's address.
83 	///
84 	@property Address address( ) const
85 	{
86 		return Address( _address );
87 	}
88 
89 	/// Sets the message's address.
90 	///
91 	@property void address( Address addr )
92 	{
93 		_address = Address( addr );
94 	}
95 
96 	/// Gets the argment's types.
97 	///
98 	@property const(TypeTagString) types( ) const
99 	{
100 		return _types;
101 	}
102 
103 	/// Gets the message's argument count.
104 	///
105 	@property size_t length( ) const
106 	{
107 		return _arguments.length;
108 	}
109 
110 
111 
112 	/// Duplicates the current message.
113 	///
114 	OSCMessage dup( ) const
115 	{
116 		return new OSCMessage( this );
117 	}
118 
119 
120 
121 	/// Gets the type of the argument at the given index.
122 	///
123 	TypeTag typeAt( size_t idx ) const
124 	in
125 	{
126 		assert( idx < _arguments.length );
127 	}
128 	do
129 	{
130 		return cast(TypeTag) _types[ idx ];
131 	}
132 
133 	/// Gets the argument at the given index.
134 	///
135 	OSCArgument argumentAt( size_t idx ) const
136 	in
137 	{
138 		assert( idx < _arguments.length );
139 	}
140 	do
141 	{
142 		return _arguments[ idx ];
143 	}
144 
145 
146 
147 	/// Checks if the given argument is an integer.
148 	///
149 	bool isInt( size_t idx ) const
150 	in
151 	{
152 		assert( idx < _arguments.length );
153 	}
154 	do
155 	{
156 		return _types[ idx ] == TypeTag.Integer;
157 	}
158 
159 	/// Checks if the given argument is a float.
160 	///
161 	bool isFloat( size_t idx ) const
162 	in
163 	{
164 		assert( idx < _arguments.length );
165 	}
166 	do
167 	{
168 		return _types[ idx ] == TypeTag.Float;
169 	}
170 
171 	/// Checks if the given argument is an string.
172 	///
173 	bool isString( size_t idx ) const
174 	in
175 	{
176 		assert( idx < _arguments.length );
177 	}
178 	do
179 	{
180 		return _types[ idx ] == TypeTag.String;
181 	}
182 
183 	/// Checks if the given argument is an blob.
184 	///
185 	bool isInt( size_t idx ) const
186 	in
187 	{
188 		assert( idx < _arguments.length );
189 	}
190 	do
191 	{
192 		return _types[ idx ] == TypeTag.Blob;
193 	}
194 
195 	/// Checks if the given argument is true.
196 	///
197 	bool isTrue( size_t idx ) const
198 	in
199 	{
200 		assert( idx < _arguments.length );
201 	}
202 	do
203 	{
204 		return _types[ idx ] == TypeTag.True;
205 	}
206 
207 	/// Checks if the given argument is false.
208 	///
209 	bool isFalse( size_t idx ) const
210 	in
211 	{
212 		assert( idx < _arguments.length );
213 	}
214 	do
215 	{
216 		return _types[ idx ] == TypeTag.False;
217 	}
218 
219 	/// Checks if the given argument is a boolean.
220 	///
221 	bool isBool( size_t idx ) const
222 	{
223 		return isTrue( idx ) || isFalse( idx );
224 	}
225 
226 	/// Checks if the given argument is null.
227 	///
228 	bool isNull( size_t idx ) const
229 	in
230 	{
231 		assert( idx < _arguments.length );
232 	}
233 	do
234 	{
235 		return _types[ idx ] == TypeTag.Null;
236 	}
237 
238 	/// Checks if the given argument is an impulse.
239 	///
240 	bool isImpulse( size_t idx ) const
241 	in
242 	{
243 		assert( idx < _arguments.length );
244 	}
245 	do
246 	{
247 		return _types[ idx ] == TypeTag.Impulse;
248 	}
249 
250 	/// Checks if the given argument is a timetag.
251 	///
252 	bool isTimeTag( size_t idx ) const
253 	in
254 	{
255 		assert( idx < _arguments.length );
256 	}
257 	do
258 	{
259 		return _types[ idx ] == TypeTag.TimeTag;
260 	}
261 
262 
263 
264 	/// Gets the argument at the given index.
265 	///
266 	T at( T:Variant )( size_t idx ) const
267 	in
268 	{
269 		assert( idx < _arguments.length );
270 	}
271 	do
272 	{
273 		return _arguments[ idx ];
274 	}
275 
276 	/// Gets the integer at the given index.
277 	///
278 	T at( T:int )( size_t idx ) const
279 	in
280 	{
281 		assert( idx < _arguments.length );
282 		assert( _types[ idx ] == TypeTag.Integer );
283 	}
284 	do
285 	{
286 		return _arguments[ idx ].get!T;
287 	}
288 
289 	/// Gets the float at the given index.
290 	///
291 	T at( T:float )( size_t idx ) const
292 	in
293 	{
294 		assert( idx < _arguments.length );
295 		assert( _types[ idx ] == TypeTag.Float );
296 	}
297 	do
298 	{
299 		return _arguments[ idx ].get!T;
300 	}
301 
302 	/// Gets the string at the given index.
303 	///
304 	T at( T:string )( size_t idx ) const
305 	in
306 	{
307 		assert( idx < _arguments.length );
308 		assert( _types[ idx ] == TypeTag.String );
309 	}
310 	do
311 	{
312 		return _arguments[ idx ].get!T;
313 	}
314 
315 	/// Gets the blob at the given index.
316 	///
317 	T at( T:ubyte[] )( size_t idx ) const
318 	in
319 	{
320 		assert( idx < _arguments.length );
321 		assert( _types[ idx ] == TypeTag.Blob );
322 	}
323 	do
324 	{
325 		return _arguments[ idx ].get!T.dup;
326 	}
327 
328 	/// Gets the bool at the given index.
329 	///
330 	T at( T:bool )( size_t idx ) const
331 	in
332 	{
333 		assert( idx < _arguments.length );
334 		assert( _types[ idx ] == TypeTag.True || _types[ idx ] == TypeTag.False );
335 	}
336 	do
337 	{
338 		return _arguments[ idx ].get!T;
339 	}
340 
341 	/// Gets the timetag at the given index.
342 	///
343 	T at( T:SysTime )( size_t idx ) const
344 	in
345 	{
346 		assert( idx < _arguments.length );
347 		assert( _types[ idx ] == TypeTag.TimeTag );
348 	}
349 	do
350 	{
351 		return _arguments[ idx ].get!T;
352 	}
353 
354 
355 
356 	/// Adds an argument to the message.
357 	///
358 	OSCMessage add( T )( T v )
359 	{
360 		_types = _types.add!T;
361 		OSCArgument va = v;
362 		_arguments ~= va;
363 
364 		return this;
365 	}
366 
367 	/// Adds a bool to the message.
368 	///
369 	OSCMessage add( T:bool )( T v )
370 	{
371 		_types = _types.add!bool( v );
372 		OSCArgument va = v;
373 		_arguments ~= va;
374 
375 		return this;
376 	}
377 
378 	/// Adds a null to the message.
379 	///
380 	OSCMessage addNull( )
381 	{
382 		_types = _types.addNull;
383 		OSCArgument va = null;
384 		_arguments ~= va;
385 
386 		return this;
387 	}
388 
389 	/// Adds an impulse to the message.
390 	///
391 	OSCMessage addImpulse( )
392 	{
393 		_types = _types.addImpulse;
394 		OSCArgument va = 'I';
395 		_arguments ~= va;
396 
397 		return this;
398 	}
399 
400 
401 
402 	@property override ubyte[] data( ) const
403 	{
404 		ubyte[] res;
405 		string types = "," ~ _types;
406 
407 		res ~= _address.toString.toOSC ~ types.toOSC;
408 
409 		int i;
410 		foreach( t; _types )
411 		{
412 			switch( t )
413 			{
414 			case TypeTag.Integer:
415 				res ~= _set!int( _arguments[ i ].get!int );
416 				break;
417 			
418 			case TypeTag.Float:
419 				res ~= _set!float( _arguments[ i ].get!float );
420 				break;
421 			
422 			case TypeTag.String:
423 				res ~= _set!string( _arguments[ i ].get!string );
424 				break;
425 			
426 			case TypeTag.Blob:
427 				res ~= _set!(ubyte[])( _arguments[ i ].get!(ubyte[]) );
428 				break;
429 			
430 			case TypeTag.TimeTag:
431 				res ~= _set!SysTime( _arguments[ i ].get!SysTime );
432 				break;
433 
434 			case TypeTag.True:
435 			case TypeTag.False:
436 			case TypeTag.Null:
437 			case TypeTag.Impulse:
438 				break;
439 			
440 			default:
441 				assert( 0 );
442 			}
443 
444 			i += 1;
445 		}
446 
447 		return res;
448 	}
449 
450 }
451 
452 
453 unittest 
454 {
455 	ubyte[] raw = [
456 		0x2f, 0x6f, 0x73, 0x63,
457 		0x69, 0x6c, 0x6c, 0x61,
458 		0x74, 0x6f, 0x72, 0x2f,
459 		0x34, 0x2f, 0x66, 0x72,
460 		0x65, 0x71, 0x75, 0x65,
461 		0x6e, 0x63, 0x79, 0x00,
462 		0x2c, 0x66, 0x00, 0x00,
463 		0x43, 0xdc, 0x00, 0x00
464 	];
465 
466 	OSCMessage msg = new OSCMessage( "/oscillator/4/frequency" );
467 	msg.add!float( 440.0 );
468 
469 	assert( msg.data( ) == raw );
470 
471 	OSCMessage parsed = cast(OSCMessage) OSCPacket.parse( raw );
472 
473 	assert( parsed.address.toString == "/oscillator/4/frequency" );
474 	assert( parsed.length == 1 );
475 	assert( parsed.isFloat( 0 ) );
476 	assert( parsed.at!float( 0 ) == 440 );
477 }
478 
479 unittest
480 {
481 	import std.math : abs;
482 
483 	OSCMessage initial = new OSCMessage( "/unittest/test" );
484 	initial.add!int( 42 );
485 	initial.add!float( 3.14 );
486 	initial.add!string( "test" );
487 	initial.add!(ubyte[])( cast(ubyte[]) "ubyte" );
488 	initial.add!bool( true );
489 	initial.add!bool( false );
490 	initial.addNull;
491 	initial.addImpulse;
492 	SysTime currTime = Clock.currTime;
493 	initial.add!SysTime( currTime );
494 
495 	ubyte[] raw = initial.data( );
496 
497 	OSCMessage parsed = cast(OSCMessage) OSCPacket.parse( raw );
498 
499 	assert( parsed.address == initial.address );
500 	assert( parsed.types == initial.types );
501 	assert( parsed.at!int( 0 ) == 42 );
502 	assert( abs( parsed.at!float( 1 ) - 3.14 ) < 0.001 );
503 	assert( parsed.at!string( 2 ) == "test" );
504 	assert( parsed.at!(ubyte[])( 3 ) == cast(ubyte[]) "ubyte" );
505 	assert( parsed.at!bool( 4 ) == true );
506 	assert( parsed.at!bool( 5 ) == false );
507 	assert( parsed.isNull( 6 ) );
508 	assert( parsed.isImpulse( 7 ) );
509 	assert( parsed.at!SysTime( 8 ) == currTime );
510 }
511 
512 unittest
513 {
514 	import std.math : abs;
515 
516 	ubyte[] raw = [
517 		0x2f, 0x66, 0x6f, 0x6f,
518 		0x00, 0x00, 0x00, 0x00,
519 		0x2c, 0x69, 0x69, 0x73,
520 		0x66, 0x66, 0x00, 0x00,
521 		0x00, 0x00, 0x03, 0xe8,
522 		0xff, 0xff, 0xff, 0xff,
523 		0x68, 0x65, 0x6c, 0x6c,
524 		0x6f, 0x00, 0x00, 0x00,
525 		0x3f, 0x9d, 0xf3, 0xb6,
526 		0x40, 0xb5, 0xb2, 0x2d,
527 	];
528 
529 	OSCMessage a = cast(OSCMessage) OSCPacket.parse( raw );
530 
531 	assert( a.address.toString == "/foo" );
532 	assert( a.length == 5 );
533 	assert( a.types == "iisff" );
534 	assert( a.at!int( 0 ) == 1000 );
535 	assert( a.at!int( 1 ) == -1 );
536 	assert( a.at!string( 2 ) == "hello" );
537 	assert( abs( a.at!float( 3 ) - 1.234 ) < 0.001 );
538 	assert( abs( a.at!float( 4 ) - 5.678 ) < 0.001 );
539 
540 	const OSCMessage b = new OSCMessage( "/foo" )
541 		.add!int( 1000 )
542 		.add!int( -1 )
543 		.add!string( "hello" )
544 		.add!float( 1.234 )
545 		.add!float( 5.678 );
546 	
547 	assert( b.data == raw );
548 }