Jump to content

Can I trigger patch change from Playback?


joachim_s

Recommended Posts

  • 5 months later...
  • 4 weeks later...
Dewdman42, I'm interested in working with your MIDI Playback script, but when I take the link to GitHub, it gives me a "page not found", and my searching hasn't turned it up. Any chance you could check and update the link? Thanks immensely. And I really need to thank you a hundred-fold, your posts have been a giant help to me in the past!
Link to comment
Share on other sites

  • 1 year later...
This provides a possible solution for something I've been going nuts trying to do, but I still have an issue you may be able to help with. I ran the sample project in the PatchChanges.concert.zip and noticed that it only works one time! If I play the audio file a second time, the patches do not change. I have to close the project and reopen it for it to work again. Why is that? I'm running midi monitor and see no messages are sent the second time.
Link to comment
Share on other sites

  • 1 month later...
  • 3 months later...
So I was able to get the basic scripting thing work in general, it will take my initial patch change as expected at 37 measures but then it skips to the next patch nearly immediately. like take one...then 2...then it doesn't correct or respond after that. I feel like i'm doing this right but maybe i'm missing something. Any tips or tricks?
Link to comment
Share on other sites

  • 1 year later...

thank you for your work, I really appreciate it. I just need to switch patches while playing a backing track. I have a problem, I can't get it to work in my Mac. No matter what value I set as program change, the script changes to the wrong patch-number and stops playing the playback track. Attached is the test project, any suggestions are appreciated. M1 Mac, Monterey 12.6.1, MainStage 3.5.3. Thank you

LINK

Edited by ochoalmocho
Link to comment
Share on other sites

The script is sending PC#2 at beat 8, as desired, which changes to the DUEL patch...which you have setup as needing to receive PC#3.  This is almost surely a matter of whether MainStage is configured to start at PC#0 or PC#1, something like that.

Please be more specific about the results you are having.

 

Edited by Dewdman42
Link to comment
Share on other sites

1 hour ago, Dewdman42 said:

Please be more specific about the results you are having

 

Thank you for your reply. I will try to explain better what happens: if I set in the script e. g. beat 8 and patch number 1 or 2 or 3, the result is the same: the program change moves towards Duel and the backing track stops, no matter what is the number in the script’s program change. My goal is to move from a patch to another of the same SET (song).

 

Link to comment
Share on other sites

It’s been a while since I worked with MainStage so some of this you will have to figure out.  But I am sure it is changing to duel because you have it set for P3.  But there could be something going on within the set arrangement.  The script is doing exactly what it’s supposed to do.  There will be something about how you have the patch or set configuration or preferences

Link to comment
Share on other sites

14 hours ago, Dewdman42 said:

There will be something about how you have the patch or set configuration or preferences

The program considers patch 1 as 0 and so on, this explains the problem. I don't know if there's a way to change this setting, but everything works ok now. Thank you

Link to comment
Share on other sites

 

19 hours ago, ochoalmocho said:

The program considers patch 1 as 0 and so on, this explains the problem. I don't know if there's a way to change this setting, but everything works ok now. Thank you

I’m coming in at the very end of this thread and haven’t read anything else, but regarding this last comment, there’s a MainStage setting to change the patch numbering i.e. 0-127 or 1-128

  • Like 1
Link to comment
Share on other sites

  • 2 months later...

 

Hello everyone, and congratulations to the participants, really interesting topic.

I'm writing to ask for help on an issue that may have already been discussed... I have two types of problems creating my custom script:

1) In my patches I have a player that starts automatically on the patch/set field. When I boot for the first time, if I debug my script, GetTimingInfo returns me the blockStartBeat values starting from 1. If I go to the next patch and then go back the blockStartBeat value doesn't reset, and it doesn't start from 1 anymore. It's normal?

2) The previous question helps me to try to solve the main problem. My audio sequence played by the player does not have a fixed bpm, but a variable one. Using the MidiPlayer.js and exporting a midi track with everything in the correct position and with the tempo variations, MIDI and Audio no longer start to match from the first bpm variation. Is it possible to solve this problem?

 

Link to comment
Share on other sites

  • 1 month later...

Ok, I've spent WAY too much time on this... I was a web developer in a former life, and I try to write code as compact as possible. Documentation on this API is horrible. I had trouble getting the "sendAtBeat" method to work, turns out you MUST have the HandleMIDI function included for it to work.

For some reason you must add 1 to the actual program change number ("P#").

The clock keeps running during playback unless stopped, I added that factor in. You can now go from set to set and have the script trigger the changes correctly.

The only remaining issue is that a "set" (song) may begin on a beat other than the downbeat. That may be an issue for some, but I don't think it will be for me. My primary motivation is to be able to change vocal and guitar patches in sync with the backing track. It now works as planned.

Here's the code:

 

// Add 1 to P# shown for beat entry below
// Edit with your own Beat: Program Change # value pairs

var     changes =    {
    4: 2,
    8: 3,
    12: 1,
    16: 2,
    20: 3,
    24: 1
    };

/*******************************************/
var NeedsTimingInfo = true;
var startBeat = 0;

function Reset() {
    startBeat = 0;
}

function ProcessMIDI() {
    if (startBeat) return;
    
    startBeat = GetTimingInfo().blockStartBeat;
    Object.keys(changes).forEach(function(beat) {
        var pc = new ProgramChange;
        pc.number = changes[beat];
        pc.sendAtBeat(startBeat + parseInt(beat));
    });
}

function HandleMIDI(event){
}

 

  • Like 1
Link to comment
Share on other sites

  • 2 months later...
On 5/1/2023 at 10:30 PM, yeldarb said:

The clock keeps running during playback unless stopped, I added that factor in. You can now go from set to set and have the script trigger the changes correctly.

The only remaining issue is that a "set" (song) may begin on a beat other than the downbeat. That may be an issue for some, but I don't think it will be for me. My primary motivation is to be able to change vocal and guitar patches in sync with the backing track. It now works as planned.

 

 

Hello!

First of all thanx to all for very helpfull info! It's something that I searching a long long time!

Our band using the Mainstage 3 for playback tracks only and I play a guitar on stage through the guitar processor Line 6 HX XL.

I would like to connect Mainstage by midi for switching presets (and snapshots) depending on the playback set (song).

I took script method and it works fine, but I have a problem. When I choose another set (in my case it's a playback song) in the mainstage, the scripts from the various songs begin to overlap each other and the Line 6 Helix switches both scripts at once, not the one that is playing at the moment.

Is it possible to solve this by the script or maybe another method?

THANX!

MY LINE6 MIDI INFO : 

Presets: PC 112, PC 113

Snapshots: CC 069:000, CC 069:001, CC 069:002, CC 069:003

Just in case, I attach my script for the 1st song:

Quote

 

var NeedsTimingInfo = true;

//========= EDIT THIS ARRAY ====================

var programChanges = [
   {beatPos:5, channel:1, msb:000, lsb:1, number:112},
   {beatPos:29, channel:1, msb:000, lsb:0, number:112},
   {beatPos:93, channel:1, msb:000, lsb:1, number:112},
   {beatPos:129, channel:1, msb:000, lsb:0, number:112},
   {beatPos:193, channel:1, msb:000, lsb:1, number:112},
   {beatPos:249, channel:1, msb:000, lsb:0, number:112},
   {beatPos:306, channel:1, msb:000, lsb:3, number:112},
   {beatPos:355, channel:1, msb:000, lsb:0, number:112},
   {beatPos:474, channel:1, msb:000, lsb:0, number:112},
   {beatPos:509, channel:1, msb:000, lsb:1, number:112},
  
  // copy the above line for more Program changes
];


//========= DO NOT EDIT BELOW HERE =============

var idx = 0;
var pc = new ProgramChange;  //reuse in a loop
var cc = new ControlChange;

function ProcessMIDI() {
  /* Get current timing info */
  var info = GetTimingInfo();
 
  /* only do work if playing */
  if ( !info.playing ) {
      idx = 0;
      return;
  }
  
  if (idx < programChanges.length) {
  
     while ( idx < programChanges.length &&
             info.blockStartBeat <= programChanges[idx].beatPos && 
             info.blockEndBeat >= programChanges[idx].beatPos ) {
        
        if(programChanges[idx].msb != undefined) {         
            cc.channel = programChanges[idx].channel;
            cc.beatPos = programChanges[idx].beatPos;
            cc.number = 0;
            cc.value = programChanges[idx].msb;
            cc.send();
            //cc.trace();
        }
        
        if(programChanges[idx].lsb != undefined) {                    
            cc.channel = programChanges[idx].channel;
            cc.beatPos = programChanges[idx].beatPos;
            cc.number = 69;
            cc.value = programChanges[idx].lsb;
            cc.send();
            //cc.trace();
        }

        if(programChanges[idx].number != undefined) {
            pc.number = programChanges[idx].number;    
            pc.beatPos = programChanges[idx].beatPos;
            pc.channel = programChanges[idx].channel;  
            pc.send();
            //pc.trace();
        }
        
        idx++;
     }              
  }
  
}

// ignore incoming midi, might not need this, not sure right now
function HandleMIDI(event) {
}

 

 

Link to comment
Share on other sites

  • 8 months later...
On 3/7/2018 at 9:48 PM, Dewdman42 said:

Check out this example Mainstage Project:

PatchChanges.concert.zip 778.98 kB · 255 downloads

This is just a quirky little demo that plays an audio loop using the Playback plugin. At certain points of time that happen to be exactly on beats, ProgramChanges are being sent out by the Scripter script (shown below). This demo sends the Program Change to an IAC port, which then feeds back into Mainstage in order to visually watch the Mainstage patches change in response to the program change messages. In your own usage, you probably won't be doing that part, just sending the ProgramChange off to some external device.

in order to see this in action, you will need to create at least one IAC port if you don't have one created already, using Apple AudioMidi Setup. Once you have that, make sure the channel holding the Scripter plugin (which is in the set called "MySong"), is configured to send midi output to that IAC port. That's only in order to observe the loopback program changes.

iac.jpg.258f8c864d3d638fd41ca873b2370976.jpg

Then press play and watch the screen, you will hear a little audio loop playing and watch mainstage change patches exactly on beats, as configured in the script. You can speed up or slow down the tempo, won't matter, the patch changes will happen exactly on the beat because the audio has been beat mapped or whatever.

If you are trying to do something real, then you don't need the IAC stuff, just put the script on a channel that will be used to send out the program changes, and have it send to the device of your choice. If you want to be able to mess with tempo after the fact and have audio and midi remain in sync, then your audio needs to be setup as a legitimate apple loop file, which you can do ahead of time in Logic. If you don't plan to mess with tempo at all, then it doesn't really matter, but it may be more difficult to figure out exactly where on the midi timeline to send your PC, but with trial and error you can figure that out.

You have to edit the Script for where in time to send the ProgramChanges.

Here is the script:

var NeedsTimingInfo = true;

//========= EDIT THIS ARRAY ====================

var programChanges = [
   {beatPos:4.99, channel:1,  number:2}
  ,{beatPos:8.99, channel:1,  number:3}
  ,{beatPos:12.99, channel:1, number:1}
  ,{beatPos:16.99, channel:1, number:2}    
  ,{beatPos:20.99, channel:1, number:3}
  ,{beatPos:24.99, channel:1, number:1}
  // copy the above line for more Program changes
];


//========= DO NOT EDIT BELOW HERE =============

var idx = 0;
var pc = new ProgramChange;  //reuse in a loop

function ProcessMIDI() {
  /* Get current timing info */
  var info = GetTimingInfo();
 
  /* only do work if playing */
  if ( !info.playing ) {
      idx = 0;
      return;
  }
  
  if (idx < programChanges.length) {
  
     while ( idx < programChanges.length &&
             info.blockStartBeat <= programChanges[idx].beatPos && 
             info.blockEndBeat >= programChanges[idx].beatPos ) {
                  
        pc.number = programChanges[idx].number;    
        pc.beatPos = programChanges[idx].beatPos;
        pc.channel = programChanges[idx].channel;  
        pc.send();
        // pc.trace();
        idx++;
     }              
  }
  
}

// ignore incoming midi, might not need this, not sure right now
function HandleMIDI(event) {
}
 

Hi Dewdman42,

this thread is a bit old, but I couldn't find any other one which was so much on point for my recent request. I tried your script for the Mainstage Scripter and it works fantastic for changing patches automatically while the Playback is running. However I have one more request (probably and hopefully just a tiny small one). As I sync the tempo from the Playback with the one from MainStage (to be able to use e.g. later the Metronom), how can it be stopped at the end automatically. The playback itself stops at the end, but the Play function from MainStage doesn't until I hit the space bar. And if I forget this, I run into problems, if I start the next Playback (in a new set with other patches to be changed). I hope, you get my point and maybe it's just another comment inside Scripter for the last beat needed or so.

Thanks for any help, Stefan

Link to comment
Share on other sites

not entirely sure i understand your question, but the midi time clock is always running in Mainstage and LogicPro, even when the transport is stopped.  But you can use the GetTimingInfo function to obtain the transport info and then look at whether the transport is actually still playing or not.  Based on that you can have scripter do things (or not). 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...