Help with custom script for transposing MIDI polyAT

Post Reply New Topic
RELATED
PRODUCTS

Post

Hi everyone, this is my first post here!

I am a programmer but I haven't written Bitwig scripts before, so I am looking for some pointers.
Recently, I acquired a Roland A-80 with firmware version EPROM 1.02, which has a bug.
I think I can make a workaround with a Bitwig script, but I don't understand how to manipulate MIDI.

The A-80 is blessed with polyphonic aftertouch, making it a wonderful master keyboard.
However, my specimen has a bug where the polyAT message is 3 octaves higher than the corresponding note (I verified this with MIDI Monitor).
Hopefully, it is possible to edit the Bitwig Generic MIDI Keyboard script with some lines that transpose these messages down 3 octaves again.
This way, my A-80 will always behave as intended whenever I use Bitwig.

I am guessing I need setMidiCallback() on a function which recognises and manipulates a polyAT message.
Can anyone show me how to do something like that?

Post

Normally PolyAT data goes into the "Note Lane" as opposed to the "Controller Lane".
The Note Lane is very fast, but doesn't allow much manipulation (it does allow transposing, but only for everything, not only for specific messages AFAIK).
But since PolyAT isn't as time critical, you should be able to do what you want if you first remove the PolyAT from the Note lane by filtering it with the Bitmask when you create your Note Input in the init function. This would let go everything through:

Code: Select all

keys = midiIn.createNoteInput("Keys", "??????");
The question mark acts as a "joker" here. But you can be much more specific:

Code: Select all

PadNotes = host.getMidiInPort(0).createNoteInput("MPD226 Pads", "8?????", "9?????", "D?????","E?????", "A?????");
(I pick examples from different scripts, therefore the different ways to access the Midi Ports)
A0 to AF is the polyphonic aftertouch, so if you remove that from above example, it should not go to the Note Lane but to the controller lane for you to change.
Alternatively you can set:

Code: Select all

PadNotes.setShouldConsumeEvents(false);
Which would send everything to the Controller Lane, no matter the set bits, but that could be a bit wasteful here.

Next is to set up your Midi Callback:

Code: Select all

host.getMidiInPort(0).setMidiCallback(onMidi);
That function looks like this:

Code: Select all

function onMidi(status, data1, data2)
{
    printMidi(status, data1, data2); // Just so you see what comes in in the scripting console (shortcut J in BWS).
}
In here, you check for PolyAT messages, then manipulate the data you get with status, data1 and data2, then send it back to the Note Lane with:

Code: Select all

PadNotes.sendRawMidiEvent(new_status, new_data1, new_data2);
This is mostly off the top of my head, so there may be errors, but you should get the idea.

Cheers,

Tom
"Out beyond the ideas of wrongdoing and rightdoing, there is a field. I’ll meet you there." - Rumi
Sculptures ScreenDream Mastodon

Post

This is just what I need. Thank you!

I need some time to figure the rest out.
I will report back later.

Post

I can recommend looking at other scripts too, not only the documentation, to see stuff in context. Most of them are in the Bitwig installation folder Bitwig Studio\resources\controllers
More can be found in the controller script library, but most of them are probably out of date:
https://www.bitwig.com/en/community/con ... ripts.html
Version 2 saw some major changes and improvements in the API.

And BTW. You can now also write controller scripts in Java if that should be your thing - although for simple stuff and starting out I would recommend JavaScript.

Cheers,

Tom
"Out beyond the ideas of wrongdoing and rightdoing, there is a field. I’ll meet you there." - Rumi
Sculptures ScreenDream Mastodon

Post

I got a script working with the above suggestions, other scripts, and the API Developer's Guide.
However, I'm concerned about latency, so I tried an alternative.

Code: Select all

var tablePolyAT = []
for (var i = 0; i < 128; i++) {if (i > 35) {tablePolyAT[i] = i - 36} else {tablePolyAT[i] = i + 92}}

function init() {
    midiIn = host.getMidiInPort(0)
    nonPolyAT = midiIn.createNoteInput("All except polyAT", "8?????",
				       "9?????", "B?????", "C?????",
				       "D?????", "E?????", "F?????")
    polyAT = midiIn.createNoteInput("Transposed polyAT", "A?????")
    polyAT.setKeyTranslationTable(tablePolyAT)
}
I created two NoteInputs: one for everything except polyAT, and one especially for polyAT with a transpose table.
This requires setting the armed track to receive "All ins" (could become a problem with multiple controllers).

I assumed that this would be faster, bypassing the ControllerLane.
However, while e.g. Note On info passes normally (I can play vsts), polyAT does not pass through at all.
What am I missing?

P.S. Sorry if I'm blatantly violating any JS conventions; this is my first time.

EDIT: Punctuation.
Last edited by Born A Kid on Thu Oct 18, 2018 6:25 pm, edited 1 time in total.

Post

Well, since only Poly AT is going through the script, latency shouldn't be much of an issue. Did you actually notice any problems with modulation timing?

Your second solution was something I contemplated as well, but since I never tried it, I was reluctant to recommend it.

You could prevent the "All ins" problem by having two dedicated tracks for the two inputs and in the second have a note receiver receiving from the first with all blocking off, to merge both together.

If you only have the polyAT input on a track, does anything come in (do the yellow light blink? Does a midi monitor show anything?)?
Is your translation logic correct for your bug or is it maybe off a bit?

As for JS: Better use semicolons for line endings, otherwise you may encounter problems with auto-semicolon-insertion by the interpreter. I personally wouldn't break up lines like you did because of this. JS has some funny ideas like that, like variable hoisting and it's strange function scope instead of block scope... Needs a bit of getting used to... :-)

Any errors in the scripting console?

Cheers,

Tom
"Out beyond the ideas of wrongdoing and rightdoing, there is a field. I’ll meet you there." - Rumi
Sculptures ScreenDream Mastodon

Post

Translation logic was verified on the first solution (which works).
But it's easy to go wrong on an index indeed!

It seems like the polyAT messages don't go through.

Code: Select all

function init() {
    polyAT = midiIn.createNoteInput("polyAT", "A?????");
}
With just this script, no yellow square blinks when I'm sending polyAT.
MIDI Monitor confirms that the messages are received by the computer (data is e.g. A07337).

Also does not work: looping a clip with Cs on all octaves, then sending a C polyAT message with above script.

Maybe Bitwig ignores polyAT if there was no corresponding Note On message on the NoteInput?

EDIT: Terminology.

Post

Since I've got a working solution (manually transposed polyAT via ControllerLane), I will stick to that for now.
However, if anyone understands why the polyAT messages don't go through in the script in the post above, I'd still like to know.

Tom, thanks a lot for your help!
You got me started in Bitwig Scripting.

Regards, Lars

Post

That is what I was wondering, since PolyAT is Note specific and Bitwig does convert Midi to it's internal logic (Midi isn't exactly clever when it comes to knowing what is what), it would make sense to me to think that maybe one can't send one without the other. But if you want to be sure, you could write to BW support and ask if they can explain what happens there and if its actually intended or a missing feature/bug.

I hope you will have a lot of fun with scripting - a lot can be done with it, not just controller stuff, but also scripts that generate stuff etc.

Cheers,

Tom

P.S. I was thinking of a VST Midi Monitor inside of your track... ;-)
"Out beyond the ideas of wrongdoing and rightdoing, there is a field. I’ll meet you there." - Rumi
Sculptures ScreenDream Mastodon

Post

I know this thread is old,
but after read it I've solved the same issue in an A50, sending the pressure message to the control lane.
Later I found what I think is a better solution using MidiPipe.

Thanks!

Post

Yeah, the API has improved a lot in the meantime and some of what I wrote in the past no longer applies (should still work if it isn't deprecated but may no longer be necessary or the best solution).

Cheers,

Tom
"Out beyond the ideas of wrongdoing and rightdoing, there is a field. I’ll meet you there." - Rumi
Sculptures ScreenDream Mastodon

Post Reply

Return to “Controller Scripting”