SuperDirt icon indicating copy to clipboard operation
SuperDirt copied to clipboard

Sysex syntax

Open tadklimp opened this issue 5 years ago • 12 comments

Hi there! I've spent some time trying to send Sysex messages from Tidal to an outboard sampler without any luck.

What is the correct syntax to use?

An example sysex message : 0xF0, 0x47, 0x5F, 0x00, 0x69, 0x02, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x09, 0xF7

From SuperCollider i can send the following without any problem:

~midiOut.sysex(Int8Array[0xF0, 0x47, 0x5F, 0x00, 0x69, 0x02, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x09, 0xF7 ])

in Tidal, the following doesn't work, as it throws "Wrong Type" errors:

once $ midicmd "sysex" # array "0xF0, 0x47, 0x5F, 0x00, 0x69, 0x02, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x09, 0xF7" # midichan 0 # s "midi" 

# array expects a Pattern Double and the sysex method in Supercollider expects an Int8Array . I couldn't find this trasnformation anywhere so i added this line in the DirtEventTypes.sc file, inside the midiEvent( play: {} )block :

if(~array.notNil) {midicmd = \sysex; ~array = Int8Array.newFrom([~array.asInteger]) }; 

this solves the Error problems but I don't know of a way to send the full sysex message as one continuous List, since Tidal breaks it into a pattern.

I've tried stack, sometimes it manages to send it, but always in a random order

once $  array (stack [0xF0, 0x47, 0x5F, 0x00, 0x69, 0x02, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x09, 0xF7]) # midichan 0 # s "midi"

Please advise! Thanks and bests, Tad

tadklimp avatar Oct 20 '19 10:10 tadklimp

Hi @tadklimp,

My guess is that array is defined as a pattern of doubles in error. I think it's better sent as a string, which I think is the only 'array'-like type in the open sound control protocol standard.

The following tries to send it as a string:

import Data.Char (chr)

once $ pS "array" (pure $ map chr [0xF0, 0x47, 0x5F, 0x00, 0x69, 0x02, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x09, 0xF7]) # s "midi" # midicmd "sysex"

However, that didn't work out of the box. I guess superdirt needs something to translate from string to Int8Array.

yaxu avatar Oct 21 '19 16:10 yaxu

Hi @yaxu ,

thanks a lot for this! Almost there ... :-D I tried your suggestion and here are some observations:

  • the message arrives as a Symbol class in SuperCollider, not a String
  • the only way i could read it was to transform it into ASCII , otherwise it appears as unknown characters.
  • Here is a crude way to transform and print the message and also tweak the Ascii results [i guess ascii values above 128 appear as negative in supercollider?]
if(~array.notNil) { {
	var asciiArray = ~array.ascii; // transform msg into an array of ascii
	var numList = List.new; // a new List placeholder
	asciiArray.do({ |i|
		if(i.isNegative)
			{ i= 256+(i); numList.add(i)} // for neg. values add 256 
			{ numList.add(i) }; 
                      });
        numList.postln; // post the received msg
       }.value 
    };

I placed the above snippet in the DirtEventTypes.sc file, inside the midiEvent( play: {} )block.

With the above, everything looks good, except one very strange thing:

  • if the Sysex string includes 0x00 or 0, the value is not accepted and the following is posted on the SuperCollider window:

         `no synth or sample named 'nil' could be found.`
    

I guess that the Sysex message 0 is transformed through OSC into the symbol \0 and tries to trigger a synth ?? I cannot understand.

Any ideas?

Thanks and bests, Tad

tadklimp avatar Oct 21 '19 20:10 tadklimp

@tadklimp Ah, I suppose the string is treated as 'null-terminated' somewhere resulting in data corruption. Looking at the OSC spec, Tidal really needs to be sending this as an 'OSC blob' rather than a string (I was wrong about string being the only suitable field). Tidal doesn't support this currently but it won't take long to add. I'll make a separate issue for that and try to look at this later on.

yaxu avatar Oct 22 '19 08:10 yaxu

Investigating this a bit more:

Saving the above snippet in DirtEventTypes.sc and sending the following from Tidal:

once $  pS "array" (pure $ map chr [0xF0, 0x47, 0x5F, 0xF7, 0x00, 0x04, 0xF4 ])  # s "midi"  

The value 0x00 and anything after it is not even being sent, or they are truncated?

Is it that 0x00 is "understood" as nil and thus as the end of the OSC message?

There are some issues with how SuperCollider treats incoming OSC messages (here and here ) but is beyond my understanidng.

@telephon do you have any ideas on this?

thanks a lot!

tadklimp avatar Oct 22 '19 08:10 tadklimp

Hi @yaxu , we posted simultaneously! :-) Thanks for this! Looking forward to an update! Best, Tad

tadklimp avatar Oct 22 '19 08:10 tadklimp

Yep I'm confident switching to blobs will solve this one!

yaxu avatar Oct 22 '19 08:10 yaxu

@yaxu has this been solved?

telephon avatar Jun 08 '20 07:06 telephon

Yes I think so, apart from documentation, but the tidal issue can stand for that.

yaxu avatar Jun 08 '20 08:06 yaxu

Hi @yaxu and @telephon,

after a long hiatus I'm experimenting with Tidal and Sysex again. I'm using Tidal 1.6.1 and Supercollider 3.10.2 on OSX 10.11.6.

It seems that the sysex messaging is currently broken?

When evaluating the following: once $ array (pure [0xF0, 0x47, 0x5F, 0x00, 0x69, 0x02, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 12, 0xF7]) # s "midi" #midicmd "sysex"

this error is printed in SC3:

ERROR: Primitive '_SendMIDIOut' failed.
Wrong type.
RECEIVER:
Instance of MIDIOut {    (0x118386328, gc=3C, fmt=00, flg=00, set=02)
  instance variables [3]
    port : Integer 1
    uid : Integer 2081379076
    latency : Integer 0
}
CALL STACK:
	MethodError:reportError
		arg this = <instance of PrimitiveFailedError>
	Nil:handleError
		arg this = nil
		arg error = <instance of PrimitiveFailedError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of PrimitiveFailedError>
	Object:throw
		arg this = <instance of PrimitiveFailedError>
	Object:primitiveFailed
		arg this = <instance of MIDIOut>
	MIDIOut:write
		arg this = <instance of MIDIOut>
		arg len = 3
		arg hiStatus = 128
		arg loStatus = [*14]
		arg a = 60
		arg b = 64
	MIDIOut:noteOff
		arg this = <instance of MIDIOut>
		arg chan = <instance of Int8Array>
		arg note = 60
		arg veloc = 64
	Function:awake
		arg this = <instance of Function>
		arg beats = 379.71125616548
		arg seconds = 379.71125616548
		arg clock = <instance of Meta_SystemClock>
		var time = 379.71125616548
^^ The preceding error dump is for ERROR: Primitive '_SendMIDIOut' failed.
Wrong type.
RECEIVER: a MIDIOut

I managed to "fix" it by inserting the following code in DirtEventTypes.sc :

if(~array.notNil) { val = ~array; ~uid = midiout.uid ; donecmd.value(\sysex); schedmidi.value({ midiout.sysex(val) }) };

This sends the Sysex message but also sends a note-On and note-Off message. To avoid that I changed the hasNote variable definition in the same file from: var hasNote = (~n != \none or: {~note.notNil} );

to this: var hasNote = (~array.isNil and: {(~n != \none or: {~note.notNil} )} );

Now everything works fine and the following code works: once $ array (pure [0xF0, 0x47, 0x5F, 0x00, 0x69, 0x02, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 12, 0xF7]) # s "midi"

Maybe there is a more elegant way to solve this?

cheers! Thomas

tadklimp avatar Nov 07 '20 19:11 tadklimp

you could test if sysex works if you send directly from sclang (https://sccode.org/1-4Zg)

telephon avatar Nov 08 '20 12:11 telephon

thanks @telephon,

with the above mentioned code added to the DirtEventTypes.sc file, sysex works fine, both from Tidal and sclang.

I was just wondering if my "hack" is valid, or there is a better solution.

tadklimp avatar Nov 08 '20 12:11 tadklimp

ah I didn't see this. There is a standard way to send arrays, in this case probably as blobs as @yaxu said. I'm not sure if this hack is optimal. Perhaps you could also take a look at the RawArray subclasses in sclang, maybe there is a better way of direct conversion. I'm not sure right now …

telephon avatar Nov 08 '20 16:11 telephon