My apologies in advance for my ignorance on this–this problem is beyond my limited scripting skills! I need to convert a data stream (system exclusive messages for a MIDI keyboard) from decimal to hex. The data looks like this:
240 15 2 0 7 4 13 4 1 247 240…
And should look like this:
F0 0F 02 00 07 04 0D 04 01 F7 F0…
This is something I found on the net that will do the conversion in applescript (I’ve got it plugged into automator) but I can’t figure out how to have the script read the entire string, parse/convert the bytes and add leading zeros where necessary (i.e. with 7 instead of writing the byte as 7 it must be 07). I would be really grateful for any assistance!
Andrew
on run {input, parameters}
set nDec to 240
set nHex to do shell script "perl -e 'printf(\"%X\", " & nDec & ")'" --> "F0"
return nHex
end run
Here’s another one that seems like it should work (but I couldn’t put the front and back on it to do so!)
on toHex(num)
set str to ""
set lst to {"A", "B", "C", "D", "E", "F"}
repeat while (num is greater than 0)
set m to num mod 16
if m is greater than 9 then set m to item (m - 9) of lst
set str to (m as string) & str
set num to num div 16
end repeat
return str
end toHex
Something you’re looking for is like this I guess:
set hexChars to "0123456789ABCDEF"
set midiStream to "240 15 2 0 7 4 13 4 1 247 240"
set hexValues to {}
repeat with byteString in words of midiStream
set byteValue to byteString as integer
set hexString to character (byteValue div 16 + 1) of hexChars & character (byteValue mod 16 + 1) of hexChars
set end of hexValues to hexString
end repeat
set oldTIDs to AppleScript's text item delimiters
set AppleScript's text item delimiters to space
set outputStream to hexValues as string
set AppleScript's text item delimiters to oldTIDs
return outputStream
BRILLIANT–I’m SO GRATEFUL! Thanks to your script and some insane routing through the netherworld of OS9 I can recover a series of sequences I created on my ESQ-1 25 years ago and I was certain were lost. This is some serious time travel…
Just one note: It takes quite a long time to process longer strings: The time taken seems to increase exponentially as the length of the string increases. But heck, as long as it does the work I can wait! Many many many thanks again. Andrew
I’m off and running, but in fact it is taking quite a while, so if you have any ideas about speed increases they would be most welcome. The stream below, for example, took 548 seconds (on a fast machine, with fans blazing). Shorter streams go very fast–were I to chop this in half it would probably go in a couple of seconds. Any ideas? Thanks.
Always nice to meet people working with older synthesizers, I recently revised an old Roland RD250 digital piano as my midi keyboard (I like the old fashioned heavy and piano-feel key mechanism) and also revised a Roland Juno 106 lately which had a dead voice chip.
You’re more than welcome.
I wasn’t aware that performance was important. When you want to speed things up in AppleScript, code becomes everything but obvious, so I thought I post a clean small script. Here an optimized version even if the code is less obvious. Took now one second on my machine.
on byteStrings2HexStrings(byteStrings)
set hexValues to {}
script o
property hexChars : {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}
property byteStrings : {}
property hexStrings : {}
end script
set o's byteStrings to words of byteStrings
repeat with i from 1 to count o's byteStrings
set byteValue to item i of o's byteStrings as integer
set hexString to item (byteValue div 16 + 1) of o's hexChars & item (byteValue mod 16 + 1) of o's hexChars
set end of o's hexStrings to hexString
end repeat
set oldTIDs to AppleScript's text item delimiters
set AppleScript's text item delimiters to space
set hexStrings to o's hexStrings as string
set AppleScript's text item delimiters to oldTIDs
return hexStrings
end byteStrings2HexStrings
When lists getting bigger than 10k items I would suggest moving to a shell, it has a built in function named printf which can print a value in another presentation:
on byteStrings2HexStrings(byteStrings)
set value to do shell script "while read line; do
for word in $line; do
printf '%02X ' \"$word\"
done
done <<< " & quoted form of byteStrings & "
"
return text 1 thru -2 of value
end byteStrings2HexStrings
The difference is that the first script is faster when using smaller lists and the latter (like McUsr’s example) will be faster when using bigger lists (strings of decimal values).
Here is a pretty fast solution, given that you aren’t on OS9 when performing the conversion to hex.
set decnumbers to "240 15 1 14 1 8 11 9 0 6 0 6 3 12 12 4 2 12 0 14 1 0 11 3 0 6 0"
set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, space}
set decnumbers to text items of decnumbers
set AppleScript's text item delimiters to linefeed
set decnumbers to decnumbers as text
set AppleScript's text item delimiters to tids
set hexnumbers to paragraphs 1 thru -2 of (do shell script "( echo \"obase=16\" ; echo " & quoted form of decnumbers & " )| bc |xargs printf '%02s
'" without altering line endings)
set hexnumbers to items of hexnumbers
set AppleScript's text item delimiters to space
set hexnumbers to hexnumbers as text
set AppleScript's text item delimiters to tids
return hexnumbers
It seems that there is a small error in McUsr’s script.
He save the current delimiter after setting it to “”.
I assume that it would be better to save it on entry.
# set AppleScript's text item delimiters to "" # DISABLED
set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, space}
set decnumbers to "240 15 1 14 1 8 11 9 0 6 0 6 3 12 12 4 2 12 0 14 1 0 11 3 0 6 0"
# set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, space} # MOVED
set decnumbers to text items of decnumbers
set AppleScript's text item delimiters to linefeed
set decnumbers to decnumbers as text
set AppleScript's text item delimiters to "" # EDITED
set hexnumbers to do shell script "( echo \"obase=16\" ; echo " & quoted form of decnumbers & " )| bc |xargs printf '%02s
'" without altering line endings
set AppleScript's text item delimiters to linefeed
set hexnumbers to text items of hexnumbers
set AppleScript's text item delimiters to space
set hexnumbers to hexnumbers as text
set AppleScript's text item delimiters to tids
return hexnumbers
Yvan KOENIG (VALLAURIS, France) jeudi 5 mars 2015 11:55:41
First a copy/paste error, I didn’t get the s within the area to paste over.
And yes, Yvan is right, I had to put it inside, as I broke the script at a time, in the middle of some operation involving text item delimiters. Both issues will be fixed almost as soon as you have read this.
I have removed the space in post #6, which resulted from the last linefeed sent from the do shell script, so now it should be all good and well for my part, but I realize that the format of the ouput really depends on how the OP, would like to continue processing the hexnumbers.
nvm… updated my post so the string always ends with an space.
edit: I’ve updated it to a less format sensitive handler. Even other whitespaces (like linefeed), double whitespaces, leading and trailing spaces are all supported now.
Once again MANY thanks to all of you for these modifications–I’m impressed not only by your technical skills but by your collective generosity. It makes me feel like there’s something really right in the world…
Andrew
Both scripts presented here work correctly up to numbers ≤ 256. For numbers greater than 256, the script from @DJ Bazzie Wazzie throws an error. @McUsrII’s script gives the correct value, but does not always add the required 0.
For example, the decimal number 1300 should have the hexadecimal representation “0514”, not “514” (result of @McUsrII’s script, and, as I sad, @DJ Bazzie Wazzie’s script throws error for 1300.)
I undertook to tweak the script of @DJ Bazzie Wazzie. Here is tweaked script:
set hexChars to "0123456789ABCDEF"
set midiStream to "5020000 15 2 0 7 4 1300 4 1 247 240"
set hexValues to ""
repeat with byteString in words of midiStream
set byteValue to byteString as integer
set {aNum, n} to {"", byteValue}
repeat
set aNum to (item (n mod 16 + 1) of my hexChars) & aNum
set n to n div 16
if (n is 0) then exit repeat
end repeat
if ((count aNum) mod 2) = 1 then set aNum to "0" & aNum
set hexValues to hexValues & aNum & space
end repeat
set hexValues to text 1 thru -2 of hexValues
set hexs to do shell script "printf '%02X ' " & "240 15 2 0 7 4 13 4 1 247 240"
→ "F0 0F 02 00 07 04 0D 04 01 F7 F0 "
The actual terminal command is:
A quick breakdown of the printf command:
% indicates that what follows determines the output format
0 pad with zero
2 pad to 2 digits
X hexadecimal in upper case (‘x’ would generate lowercase letters)
’ ’ The trailing space inside the quotes appends a space to each number. If you exclude that, you get a solid stream of 2-digit hex numbers and the quotes become optional.
As always, ‘man printf’ will provide the command’s man page.
To work with a string from a file (e.g. the long string from post #4) you could do something like this:
set hexs to do shell script "printf '%02X ' " & (read alias (((path to desktop) as text) & "dechex.txt"))