Home › Forums › General › Programming › Cleaning blank nibbles from hexstring
- This topic has 18 replies, 3 voices, and was last updated 3 years, 11 months ago by damien.
-
AuthorPosts
-
May 9, 2020 at 10:22 pm #118284
Hi everybody,
I’m looking for an easy way to cleanup the sysex message the Ensoniq DP4 is sending while in edit parameter mode. Everything is getting well. I am really looking forward to check my 6000 lines script of LUA to update my panel from the current DP4 settings π
SO, once the dump of the “Edit Buffer Message” is requested from Ctrlr I get the following hexstring:
F0 0F 40 00 00 25 01 00 00 03 02 02 00 05 03 06 0F 07 05 07 02 06 03 06 05 02 00 04 03 06 0F 06 0E 06 06 06 09 06 07 02 00 00 01 02 0F 00 00 03 00 04 08 05 04 04 09 02 05 01 0B 04 0B 00 00 00 07 07 05 00 00 01 0C 01 06 00 00 05 01 00 0A 00 0D 00 01 09 04 04 0B 04 09 00 06 00 01 02 0C 07 0F 00 07 00 03 02 0F 05 0D 07 0F 07 0F 00 03 02 00 01 02 02 0D 05 01 03 02 04 0E 02 0E 01 04 00 04 00 00 02 09 09 0D 00 00 00 0C 02 0D 00 00 01 07 01 03 00 00 00 00 05 0E 0E 0D 02 00 00 06 00 01 03 0F 07 0F 00 07 00 03 02 00 00 00 07 0F 07 0F 00 00 00 00 02 0B 00 0D 02 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 07 0F 00 00 00 02 00 00 07 0F 07 0F 07 0F 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 07 0F 00 00 00 02 00 00 07 0F 07 0F 04 00 00 00 00 00 00 02 00 01 00 00 00 00 01 00 00 00 F7
Header is “F0 0F 40 00 00”, “25” is the type of message (Edit Buffer), “01” is the current active unit (DSP1), “00 00” should be the size of the message but it’s blank (probably a bug) then the raw message is coming afterwards (current preset name, and so on).
As you can see, every byte is nibblized with a 0000HHHH 0000LLLL type [2 Nibbles M]. That’s a lot of useless zeros!
The structure of the message is well described in the manual.
I would like to clean the received message so that I can use it after cleanup like this:
32 20 53 6F 75 72 63 65 20 43 6F 6E 66 69 67 20 01 2F 00 30 ...
First I skip the header (after F0) with EditBufferNibMessage = midi:getLuaData():getRange(9, 332)
Now that I have the raw nibblized Edit Message how can I obtain a clean string without the first 0 from the bytes ?
thanks a lot π
- This topic was modified 3 years, 11 months ago by damien. Reason: typo
May 10, 2020 at 5:36 am #118286Hi Damien,
Try this: π
myMethod = function(mod,value) local t={} local reBuilt=MemoryBlock() local m2=MemoryBlock( [[ F0 0F 40 00 00 25 01 00 00 03 02 02 00 05 03 06 0F 07 05 07 02 06 03 06 05 02 00 04 03 06 0F 06 0E 06 06 06 09 06 07 02 00 00 01 02 0F 00 00 03 00 F7 ]]) for i=9,m2:getSize()-2,2 do assert(i < m2:getSize()) -- for debugging only local msb=bit.lshift(m2:getByte(i),4) local lsb=m2:getByte(i+1) local total = msb+lsb --console(String(string.format("[%d] = %.2X",i,total))) if total > 0 then table.insert(t,total) end end reBuilt:createFromTable(t) console(String(reBuilt:toHexString(1))) end --f
- This reply was modified 3 years, 11 months ago by dnaldoog. Reason: added how to reverse the process example panel
Attachments:
You must be logged in to view attached files.May 10, 2020 at 3:42 pm #118294Why cleaning the message? Why not reading it as it comes from the DP4?
Don’t get any answer from the seller of the DP4 I’m targetting πMay 11, 2020 at 12:03 am #118317Thanks Dnaldoog for the script, this is exactly what I needed.
I’ll place it in the midiMessageReceived global method with few adjustments.
If the dump is an “Edit Buffer” message type (type 25) with the right size (334 bytes) then the message will be denybblized and the decoding of the current status (bypass, currently selected unit, algo parameter etc) occures…To reply to goodweather I want to denybblize it because all the instructions in the sysex manual provide in a big table the offsets position for 8bit bytes hex values for every algo parameters. The offset starts at 0 position from the 9th 8bit byte of the full message.
I already set all my LUA script with this structure according to the reference manual like this:
CD_Routing = midiMessage:getLuaData():getByte(155) -- 0,3 [0=Serial 1=parallel 2=Feedback1 3=Feedback2]
So first of all I need to get the string structured with hexadecimal bytes.
I’m sure there is better ways to decode the incoming message and process it but this is the most logical to me.
goodweather if you can get your hands on a DP4 or a DP4+ that would be great for sure. For electronic music, it’s a classic. They are nearly identical, the DP4+ has one or two extra algo and can switch between +4dB and -10dB levels. I would buy another one without hesitation but they are pretty hard to find those days and the price is going up like crazy. I remember paying mine for about 300CAD but in 2020 a good deal would be around 500USD, the DP4+ is a bit more expensive.
They are 25+ years old units and start to fail nowadays. Mine is switched On for 3 weeks now with the Ctrlr pannel project and it fried few days ago π I have to recap the PSU the voltages are totally out of specs and probably some regulators are dead, especially a LM2926T that is discontinued for decades and is hard to find. I’ll fix it anyway they are not hard to repair. I already ordered the parts.
Everybody is raving for the phaser and modulation effects, but if you dig deeper into it, the chorus are huge, the pitchshifter that uses 2 DSP is as good as the Eventide H3000, the plate reverbs are gritty in a good way. People loves it but they don’t even know what they are capable of, they stay on the presets, hence the Ctrlr pannel π So I highly recommand this unit if you want character. We have everything emulated in VST nowadays but this is the kind of unit that keeps his place legitimately in a studio rack. Don’t hesitate to ask me if you need more info.Have a good weekend guys, take care.
- This reply was modified 3 years, 11 months ago by damien. Reason: typo
May 11, 2020 at 10:40 am #118319I’m still not sure to understand the need…
You get a 334 bytes message
In the documentation, you are saying (sorry i didn’t check) that they mention the offset with each byte (=8 bits) representing one data (your example CD_Routing is byte 155)
So, why not directly extracting the data byte by byte?
This is what I’m doing for all sysex dumps and there is no conversion needed except of course if they used bit packing.
Do not confuse the actual message and your way of seeing/displaying it on the screen/console!Sent a reminder to the guy. Cross fingers but I guess he already sold it and it is the reason he is not answering.
May 11, 2020 at 12:11 pm #118320Hi,
Maybe I’m off the track but this is the way I pictured it.
Let’s take the message received from above
03 02 02 00 05 03 06 0F 07 05 07 02 06 03 06 05 02 00 04 03 06 0F 06 0E 06 06 06 09 06 07...
They say in the manual : offset 01 to 16 = Preset name
To me, byte offset 1 to 16 from the given raw message is
03 02 02 00 05 03 06 0F 07 05 07 02 06 03 06 05
raw message offset would be
offset 1 = 03
offset 2 = 02
offset 3 = 02
offset 4 = 00
etcThe “clean” message with the right offset position corresponding to the manual would be without the interleaved blank nybbles:
32 20 53 6F 75 72 63 65 20 43 6F 6E 66 69 67
offset 1 = 32
offset 2 = 20
offset 3 = 53
…
It means ” 2 Source Config” in ASCII
So truncating the zeros and pairing the 2nd numbers gives the useful hexadecimal bytesAccording to the manual it’s written for this particular message : “data segment size 163 bytes, 326 Nybbles”
Message format – 8 bit data bytes must be transmitted and received using the 7 bit data format of MIDI, where the MSB of these data bytes must always be zero, so each of the 8 bit data byte is converted to two 4 bit nybbles for transmission. These nybbles each sent as bytes whose upper four bits are all zero. This is a description of the format of all nybblized data bytes within the packet frame as they are transmitted or received via MIDI.
0000HHHH with H = Hi 4 bits of data byte – transmitted first
0000LLLL with L = Lo 4 bits of data byte
This represents how the 8 bit byte HHHHLLLL would be transmitted.I’m not used to manipulate bits and bytes, LSB, MSB, etc maybe there’s just a very logical and academic way to deal with the raw sysex message received according to this very structure.
To me, removing the first 0 of a pair of number would do the job to get hex bytes with the right offset position.
I will face the same structure when I’ll set all my knob values to send to the unit since the message formula is :F0 0F 40 00 (header) 00 (ID) 01 (Msg type) 00 01 (Command type) 00 0U (DSP Unit) 00 0P (0ppp pppp Algo Parameter) 00 00 0v 0V (Value) F7
with
mmmmmmmm = parameter value MSByte
llllllll = parameter valuer LSByteSo if I want to send value 127 for Param 3 on the Unit C the message would be :
F0 0F 40 00 00 01 00 01 00 02 00 03 00 00 07 0F F7
Is it a common way to send and receive sysex messages?
Maybe there’s a 1 line LUA function to decypher sent/received message with this format but I don’t know.- This reply was modified 3 years, 11 months ago by damien. Reason: typo
May 11, 2020 at 1:09 pm #118322OK! With your explanation I understand now the bit packing they are using and why you need to extract the 4 lowest bits then assemble them by 2 (msb+lsb).
Code from dnaldoog is perfect indeed (shift left x4 then add to next).
Didn’t want to criticize, just to understand πMay 11, 2020 at 8:24 pm #118326It took me a whole day to understand This paragraph in the manual. It made no sense to me.
I read your guide “Ctrlr-Step-by-Step-v13.pdf” T’es franΓ§ais Goodweather?May 12, 2020 at 2:59 pm #118339Belge, mon nom c’est Bontemps π
May 13, 2020 at 4:36 am #118353Hi everybody,
This is working very well and you were absolutely right, setModulatorValue doesn’t work, the right method is
panel:getModulatorByName("ModulatorName"):getComponent():setValue(Value,true)
I experienced a little glitch with my code to update the panel from the received sysex of the current parameter values from the Ensoniq DP4
I can get the algo Index from the 4 DSP given in hexadecimal
I have a string with all the algo names listed
My problem is that the value of the algo index is in hex and the string argument index is an integer
i don’t know where to convert the given value with the tonumber method
this is my script to update the algo name in a the dedicated label from the EditBufferMessage which is actually my cleaned MemoryBlock
function UAAlgoLblDecode(EditBufferMessage) AlgoIndexUA = EditBufferMessage:getByte(16) -- Get Algo For Unit A from Edit Buffer Sysex Message in hex format AlgoName = {"No Effect","Small Room Rev","Large Room Rev","Hall Reverb","Non Lin Reverb 1","Small Plate","Large Plate","Gated Reverb","Reverse Reverb","MultiTap Delay","Dual Delay","Tempo Delay","Parametric EQ","Guitar Amp 1","VCF-Distortion","Speaker Cabinet","Rotating Speaker","EQ-Compressor","No Effect","Expander","InversExpander","8 Voice Chorus","EQ-Chorus-DDL","Flanger","Phaser-DDL","PitchShift-DDL","Pitch Shifter","Guitar Amp 2","VanderPolFilter","Sine/Noise Gen","No Effect","No Effect","Vocoder Low","Vocoder Mid 1","Vocoder Mid 2","Vocoder High","Pitch Shift 2U","Pitch Shift 2U #2","EQ-Tremolo-DDL","EQ-Vibrato-DDL","EQ-Panner-DDL","De-esser","Duck / Gate","3.3 sec DDL 2U","3.3 sec DDL 2U #2","Tunable Speaker","Non Lin Reverb 2","EQ-Flanger-DDL","FastPitchShift","No Effect","Guitar Amp 3","No Effect","No Effect","EQ-DDL-withLFO","Reverse Reverb 2","Rumble Filter","No Effect","No Effect","Keyed Expander","Non Lin Reverb 3"} AlgoNameUA = AlgoName[AlgoIndexUA+1] -- Index position (+1) of the Algoname string, from an hex value that needs to be converted to decimal. AlgoTextUA = string.format("%s", AlgoNameUA) if AlgoNameUA ~= nil then panel:getLabel("lblAlgoUA"):setText(AlgoTextUA) else AlgoLabelUA = "Select Algo UA" end end
How to convert “AlgoNameUA”, which is an hex value from the EditBufferMessage, to a decimal number that corresponds to the index position of my algoName string ?
Right now if the value is 32, the given Index of the string ins 50(dec equivalent). so it’s not good.
thanks a lot in advance guys !
May 13, 2020 at 9:27 am #118367DnaldDoog, I got your reply by email but turned to a ghost posts online. Don’t worry.
Actually there was something wrong in the denybblizing script:
if total > 0 then...
I removed it since we need all values even 00 that was nudging all the values.
and that changed everything I now have all the correct values for my algo name so all the labels showing the right infos and the button switch the good current settings.
Yes I would really like to get your exemple for nybllizing with the 0000HHHH 0000LLLL structure that would be awesome! It’s my next step so it may be very useful.
Thanks π
May 13, 2020 at 10:13 am #118343Belge, mon nom cβest Bontemps
Ouii j’ai lu l’entΓͺte de ton guide c’est pour ca que je te demandais Γ§a. Regarde sur audiofanzine y a un DP4 Γ vendre en Belgique :
https://fr.audiofanzine.com/multieffet/ensoniq/dp4/petites-annonces/i.2013732.htmlMay 13, 2020 at 12:06 pm #118371Oui, c’est celui que j’essaye d’avoir et pour lequel j’ai dΓ©jΓ envoyΓ© 2 messages mais sans rΓ©ponse. Le gars doit l’avoir dΓ©jΓ vendu et ne doit pas avoir envie de rΓ©pondre. Il laisse aller l’annonce jusque expiration dans 2-3 jours je pense…
******
To complete dnaldoog answer’s (which is not appearing here but that we got in mail), here is a few conversion functions that you can put in one single method (I’m calling that method Miscellaneous).
As dnaldoog mentioned, not needed in most cases but sometimes well π-- -- Returns HEX representation of a DECimal number -- dec2hex = function(num) local hexstr = '0123456789ABCDEF' local s = '' while num > 0 do local mod = math.fmod(num, 16) s = string.sub(hexstr, mod+1, mod+1) .. s num = math.floor(num / 16) end if s == '' then s = '0' end return s end -- -- For the conversion of a DECimal number to anything else -- it is just needed to use the built-in Lua function tonumber(string, base) -- -- print(tonumber("0100",2)) -- 4 -- print(tonumber("3F",16)) -- 63 -- -- Returns HEX representation of a String -- function str2hex(str) local hex = '' while #str > 0 do local hb = dec2hex(string.byte(str, 1, 1)) if #hb < 2 then hb = '0' .. hb end hex = hex .. hb str = string.sub(str, 2) end return hex end -- -- To display a memory block in ASCII -- it is just needed to use the built-in Lua function toString() -- -- message is "f0 00 20 32 28 00 74 01 32 2e 30 2e 32 f7" -- console(MidiMessage:getData():getRange(8, 5):toString()) -- 2.0.2 -- -- Returns ASCII representation of a string of HEX numbers separated by blanks or not -- Example: console(hex2ascii("32 2E 30 2E 32")) gives 2.0.2 -- Example: console(hex2ascii("322E302E32")) gives also 2.0.2 -- function hex2ascii(str) local ascii = '' while #str > 0 do if string.sub(str, 1,1) ~= " " then ascii = ascii .. string.char(tonumber(string.sub(str, 1,2), 16)) str = string.sub(str, 3) else str = string.sub(str, 2) end end return ascii end
May 13, 2020 at 12:29 pm #118373- post
- Whoops – typo -> re-edit save
- oh no – another typo -> re-edit save
- post = ghost
Well I post it here for posterity again, because it seems you can re-post after a certain time limit.
Hi Damien,
There shouldn’t be any conversion necessary. getByte() returns an unsigned integer.
The Hex form of a number is used in Binary strings such as in MemoryBlocks or binary files and of course MIDI just for convenience. It’s just a human readable version of a number. That is why MemoryBlock contains the method toHexString(1). It converts integers to human readable Hexidecimal form.
You will find that if you send a sysex message using decimal, it still works:
sysexString={240,66,18,0,value,247} panel:sendMidiMessageNow(CtrlrMidiMessage(sysexString))
also:
print(string.char(0x41)) = A print(string.char(65)) =A
so AlgoName[0x0A] is the same as AlgoName[10] — I suppose most people would use [10]
AlgoIndexUA = EditBufferMessage:getByte(16)
Get Algo For Unit A from Edit Buffer Sysex Message
in hex format» as an integerAlgoName = {[0]="No Effect","Small Room Rev",..."Non Lin Reverb 3"} -- optionally start your table with [0] to avoid incrementing by 1, but either way is okay AlgoNameUA = AlgoName[AlgoIndexUA] --or even AlgoNameUA = AlgoName[ EditBufferMessage:getByte(16)]
This code below shouldn’t be necessary either, because you are already returning a string value from a lua table anyway:
AlgoTextUA = string.format("%s", AlgoNameUA)
I also added a panel attachment to my first post for reconstituting 4 bit nibbles (from a string) if you are interested.
- This reply was modified 3 years, 11 months ago by dnaldoog.
May 13, 2020 at 12:49 pm #118375Hi Damien and Goodweather,
Ah, you got the email anyway! Great. I re-posted it anyway.
Well here is how I would refourbitnibbleize: (A new English verb) or in French I suggest requartrebitnibbilizer ????
Passing that original string “32 20 53 6F 75 72 63 65 20 43 6F 6E 66 69 67” into the following function
local mb=MemoryBlock("32 20 53 6F 75 72 63 65 20 43 6F 6E 66 69 67") -- 2 Source Config mynibblizedsysex=splitinto4bitnibbles(mb) console(String(mynibblizedsysex:toHexString(1)))
should output
02 00 03 02 02 00 05 03 06 0f 07 05 07 02 06 03 06 05 02 00 04 03 06 0f 06 0e 06 06 06 09 06 07
splitinto4bitnibbles=function(m) local t={} local split=MemoryBlock() for i=0,m:getSize()-1 do z=m:getByte(i) local msb=bit.rshift(bit.band(z,240),4) local lsb = bit.band(z,15) table.insert(t,msb) table.insert(t,lsb) end split:createFromTable(t) return split -- return a MemoryBlock end
See a dynamic example attached!
- This reply was modified 3 years, 11 months ago by dnaldoog.
Attachments:
You must be logged in to view attached files.May 14, 2020 at 5:25 am #118384Hi Dnaldoog and GoodWeather
Thanks for the replies, I comply my script to your recommandation and it’s working very well.
Now I need to write a global sysex midi message when the pots values are changing, I’ll work on it this weekend.@Goodweath Nevermind if the seller does not reply on audiofanzine the price could be a little bit less expensive anyway. Patience always pays off π
Retetraligonybblization sounds good as well!
- This reply was modified 3 years, 11 months ago by damien.
May 14, 2020 at 5:41 am #118386I have another question about BlockMemory
Right now I have the edit buffer sysex dump hard coded in a Lua Method.
For another function called “UAParamBufferMessage” I need to remove the first 17 bytes from the EditBufferMessage. The first offset will shift from 17 to 0
everytime I call this new string, ctrlr says the string is nil, my syntax is bad, it’s a (bad) mix between C, java and php π
Can you please help to create a new shortened substring from the longest one with the proper synthax?
t={} m2 = MemoryBlock([[ F0 0F 40 00 00 25 01 00 00 03 02 02 00 05 03 06 0F 07 05 07 02 06 03 06 05 02 00 04 03 06 0F 06 0E 06 06 06 09 06 07 02 00 00 01 02 0F 00 00 03 00 04 08 05 04 04 09 02 05 01 0B 04 0B 00 00 00 07 07 05 00 00 01 0C 01 06 00 00 05 01 00 0A 00 0D 00 01 09 04 04 0B 04 09 00 06 00 01 02 0C 07 0F 00 07 00 03 02 0F 05 0D 07 0F 07 0F 00 03 02 00 01 02 02 0D 05 01 03 02 04 0E 02 0E 01 04 00 04 00 00 02 09 09 0D 00 00 00 0C 02 0D 00 00 01 07 01 03 00 00 00 00 05 0E 0E 0D 02 00 00 06 00 01 03 0F 07 0F 00 07 00 03 02 00 00 00 07 0F 07 0F 00 00 00 00 02 0B 00 0D 02 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 07 0F 00 00 00 02 00 00 07 0F 07 0F 07 0F 02 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 07 0F 00 00 00 02 00 00 07 0F 07 0F 04 00 00 00 00 00 00 02 00 01 00 00 00 00 01 00 00 00 F7 ]]) for i = 7, m2:getSize()-2,2 do assert(i < m2:getSize()) msb = bit.lshift(m2:getByte(i),4) lsb = m2:getByte(i+1) total = msb+lsb console(String(string.format("[%d] = %.2X",i,total))) table.insert(t,total) end EditBufferMessage = MemoryBlock() EditBufferMessage:createFromTable(t)
that gives me
32 20 53 6F 75 72 63 65 20 43 6F 6E 66 69 67 20 | 01 2F 00 30...
And I want to create the string UAParamBufferMessage
something like
UAParamBufferMessage = EditBufferMessage(starting at byte #17 and ends at byte #51)
01 2F 00 30...
Thanks a lot!
- This reply was modified 3 years, 11 months ago by damien. Reason: typo again
May 14, 2020 at 10:49 am #118388Hi Damien,
Looking at it, it looks like you would start at byte 42 and then if you wanted a 51 byte MemoryBlock, I am guessing you might need to extract a further 102 bytes from the message, something like m:getRange(42,102).
May 14, 2020 at 11:25 pm #118400Hi Damien,
Looking at it, it looks like you would start at byte 42 and then if you wanted a 51 byte MemoryBlock, I am guessing you might need to extract a further 102 bytes from the message, something like m:getRange(42,102).
Hi dnaldoog
oh okay I was using the string.sub() function but it’s not working.
You are right the getRange(start,end) is working exactly as needed.It’s easy to get mistaken by the “end” variable. which counts the amount of arguments to get, not the offset end position. I got it from a previous tip you gave a long time ago on the forum.
thanks! have a nice day- This reply was modified 3 years, 11 months ago by damien. Reason: typo
-
AuthorPosts
- The forum ‘Programming’ is closed to new topics and replies.