Jump to content

Cakewalk Sonar Home Studio 6 automation


SteveH
 Share

Recommended Posts

Background: Cakewalk Sonar Home Studio is a 'Digital Audio Workstation' or music sequencer program. In the 'Producer' version there is a 'Fit Improvisation' command not available in the 'Home Studio' version. 'Fit Improvisation' calculates a new midi tempo every beat in order to synchronize a midi track with an audio track. For example, you start by recording an acoustic piano piece onto an audio track. On a midi track, you play a note for each beat of the audio track. Then you apply the 'Fit Improvisation'. Now the midi beat is synchronized with the audio. You can add midi drums/base tracks that match the audio.

Problem: I'm not interested in paying $$$ for a professional class upgrade of software that I use as a hobby at home. So I decided to find another way to apply 'Fit Improvisation'

Solution: I found AutoIT... AMAZING!. My script uses mainly keystroke emulation to parse throught the midi track and gather the exact time for each note. Within the script, the new tempo is calculated based on the difference in time between 2 notes. Keystroke emulation enters the new tempo at the appropriate time.

This script is not elaborate, but it works. I'm not sure that anyone else would have any interest in this but I thought I would post it anyways.

The (.exe and) source files are attached, but here is the main script:

AutoItSetOption("WinTitleMatchMode",2)
If WinExists("Help") then WinClose("Help") ;program mixes up Sonar Help window with Sonar progam
If WinExists("Synth Rack") then WinClose("Synth Rack") ;synth rack is always on top and gets in the way
WinActivate("SONAR")
If WinActive("SONAR")=0 Then
    MsgBox(16, "ERROR", "Unable to find SONAR")
    Exit
EndIf
$message=FileRead("Fit Improv ReadMe.txt")
If MsgBox(65, "Fit Improvisation Instructions:", $message)<>1 Then Exit
AutoItSetOption("SendKeyDelay", 50) ;increase value to slow down typing speed
Send("^{Home}") ;rewind to beginning
Send("!esn") ;unselect clips
;set variables...
Send("!op")
$timebase = InputBox("TIMEBASE", "What is the TIMEBASE (Ticks per quarter note) ?", "960","",-1,-1,0,0)
If WinExists("Project Options") then WinClose("Project Options")
$events = InputBox("Number of Events to Process", "How many Events/Notes in Selected track ? (Don't know?... double click the track to open/view the track properties)", "0","",-1,-1,0,0)
If WinExists("Track Properties") then WinClose("Track Properties")
WinActivate("SONAR")
If $events = 0 Then Exit
Send("!ik{TAB}^c{Enter}")
$bpmeasure = ClipGet()
Send("!it^c{Enter}")
$basetempo = ClipGet()

ProgressOn("Progress Meter", "Do NOT touch your keyboard or mouse", "Progress Meter: 0 percent",0,0,1)

for $time = 0 to (($events-1)*$timebase) Step $timebase
    
    ;move to next note
    Send("{F3}")
    ;get current transport time
    Send("!gt^c{Enter}")
    $transport=ClipGet()
    ;convert transport from M:B:T to ticks
    $MBT = StringSplit( $transport , ":" )
    $tick = ($MBT[1]-1)*$bpmeasure*$timebase + ($MBT[2]-1)*$timebase + $MBT[3]
    
    if $time > 0 then

        $move=$tick-$mark ;number of ticks from last event
        $tempo=Round(($basetempo*$timebase/$move) , 2)
        ;MsgBox(4096, "Tempo", $tempo , 10)
        $measure=INT(($time-$timebase)/($timebase*$bpmeasure))+1
        $beat=INT((($time-$timebase)-(($measure-1)*$timebase*$bpmeasure))/$timebase)+1
        If $tempo < 8 then
            If MsgBox(17, "Tempo Error:", "The calculated tempo of " & $tempo & " (Measure " & $measure & ", Beat " & $beat & ") is too low")<>1 Then Exit
        ElseIf $tempo > 1000 Then
            If MsgBox(17, "Tempo Error:", "The calculated tempo of " & $tempo & " (Measure " & $measure & ", Beat " & $beat & ") is too high")<>1 Then Exit 
        Else ;enter new tempo at measure:beat   
            Send("!it")
            Send($tempo)
            Send("!it")
            Send($measure)
            Send(".")
            Send($beat)
            Send("{Enter}")
        EndIf

    EndIf
    
    $mark=$tick
    $i=Round($time/(($events-1)*$timebase)*100,0)
    ProgressSet( $i, "Progress Meter:  " & $i & "  percent")

next

Send("^{Home}") ;rewind to beginning

Fit_Improv.au3

Edited by SteveH
Link to comment
Share on other sites

Cool keep up the good work. Interesting way to use the clip board. Look into the BlockInput() function to prevent someone from using the keyboard or mouse while it's working. Just make sure you enable the user input when it's finished!

Edited by Hallman
Link to comment
Share on other sites

Thanks for the suggestion Hallman...

There would definitly be a problem with the accuracy of the script if the keyboard is touched during the process. A 3 minute song at 75 beats per minute results in 225 events to be processed... it takes a few minutes (thus the progress bar).

I figured out how to use AutoIT from the Help file and online examples. Didn't know about the BlockInput() function.

Looks like it wouldn't work properly on Windows 98 or Windows XP if SP2 not installed. I see in the Help file that I can use @OSVersion to detect Win98 but how can I detect WinXP with only SP1? (Not a big deal for me... using WinXP SP3 or Vista)

I think I will update the script with BlockInput() anyways. Thanks.

Link to comment
Share on other sites

Here is the updated script using the BlockInput() function to prevent the user from typing on the keyboard while the script is running. I updated the Zip attachments on the 1st post.

AutoItSetOption("WinTitleMatchMode",2)
If WinExists("Help") then WinClose("Help") ;program mixes up Sonar Help window with Sonar progam
If WinExists("Synth Rack") then WinClose("Synth Rack") ;synth rack is always on top and gets in the way
WinActivate("SONAR")
If WinActive("SONAR")=0 Then
    MsgBox(16, "ERROR", "Unable to find SONAR")
    Exit
EndIf
$message=FileRead("Fit Improv ReadMe.txt")
If MsgBox(65, "Fit Improvisation Instructions:", $message)<>1 Then Exit
AutoItSetOption("SendKeyDelay", 50) ;increase value to slow down typing speed
Send("^{Home}") ;rewind to beginning
Send("!esn") ;unselect clips
;set variables...
Send("!op") ;open project options dialogue so user can verify 'Ticks per quarter note' = 960
$timebase = InputBox("TIMEBASE", "What is the TIMEBASE (Ticks per quarter note) ?", "960","",-1,-1,0,0)
If WinExists("Project Options") then WinClose("Project Options")
$events = InputBox("Number of Events to Process", "How many Events/Notes in Selected track ? (Don't know?... double click the track to open/view the track properties)", "0","",-1,-1,0,0)
If WinExists("Track Properties") then WinClose("Track Properties")
WinActivate("SONAR")
If $events = 0 Then Exit
If @OSVersion <> "WIN_98" And @OSVersion <> "WIN_ME" Then
    BlockInput(1) ;block user input while script running
EndIf
Send("!ik{TAB}^c{Enter}") ;open Meter/Key Signature dialogue and select/copy 'beats per measure'
$bpmeasure = ClipGet()
Send("!it^c{Enter}") ;open Tempo dialogue and select/copy baseline tempo
$basetempo = ClipGet()

ProgressOn("Progress Meter", "Fit to Improvisation", "Progress Meter: 0 percent",0,0,1)

for $time = 0 to (($events-1)*$timebase) Step $timebase
    
    Send("{F3}") ;move to next note
    Send("!gt^c{Enter}") ;open 'Go' dialogue and select/copy current transport time
    $transport=ClipGet()
    ;convert transport from M:B:T to ticks
    $MBT = StringSplit( $transport , ":" )
    $tick = ($MBT[1]-1)*$bpmeasure*$timebase + ($MBT[2]-1)*$timebase + $MBT[3]
    
    if $time > 0 then

        $move=$tick-$mark ;number of ticks from last event
        $tempo=Round(($basetempo*$timebase/$move) , 2)
        ;MsgBox(4096, "Tempo", $tempo , 10)
        $measure=INT(($time-$timebase)/($timebase*$bpmeasure))+1
        $beat=INT((($time-$timebase)-(($measure-1)*$timebase*$bpmeasure))/$timebase)+1
        If ($tempo < 8 or $tempo > 1000) then
            BlockInput(0)
            If MsgBox(17, "Tempo Error:", "The calculated tempo of " & $tempo & " (Measure " & $measure & ", Beat " & $beat & ") is out of range")<>1 Then Exit
            If @OSVersion <> "WIN_98" And @OSVersion <> "WIN_ME" Then
                BlockInput(1) ;block user input while script running
            EndIf
        Else ;enter new tempo at measure:beat   
            Send("!it")
            Send($tempo)
            Send("!it")
            Send($measure)
            Send(".")
            Send($beat)
            Send("{Enter}")
        EndIf

    EndIf
    
    $mark=$tick
    $i=Round($time/(($events-1)*$timebase)*100,0)
    ProgressSet( $i, "Progress Meter:  " & $i & "  percent")

next

Send("^{Home}") ;rewind to beginning
BlockInput(0) ;turn off block input
Link to comment
Share on other sites

Here is the updated script using the BlockInput() function to prevent the user from typing on the keyboard while the script is running. I updated the Zip attachments on the 1st post.

Cool :)

It's nice to see a newcomer that actually puts in the effort to figure out things himself instead of expecting us to tell him everything :)

Keep it up! I'm interested in seeing what you post in the future.

~ Hallman

Note: Typically posting just the source is sufficient for smaller scripts like this. It is, of course entirely your decision, but people are much less likely to download a zip or exe than a plain au3 (Not to mention it uses less of the forums bandwidth).

Edited by Hallman
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...