2017-02-17 20:25:28 -05:00
ardour {
[ " type " ] = " EditorAction " ,
name = " Polyphonic Audio to MIDI " ,
license = " MIT " ,
author = " Ardour Team " ,
description = [ [
Analyze audio from the selected audio region to a selected MIDI region .
A MIDI region on the target track will have to be created first ( use the pen tool ) .
This script uses the Polyphonic Transcription VAMP plugin from Queen Mary Univ , London .
The plugin works best at 44.1 KHz input sample rate , and is tuned for piano and guitar music . Velocity is not estimated .
] ]
}
2016-10-06 21:45:59 -04:00
function factory ( ) return function ( )
local sel = Editor : get_selection ( )
2017-09-26 23:03:10 -04:00
local sr = Session : nominal_sample_rate ( )
2016-10-06 21:45:59 -04:00
local tm = Session : tempo_map ( )
local vamp = ARDOUR.LuaAPI . Vamp ( " libardourvampplugins:qm-transcription " , sr )
2019-09-02 15:20:26 -04:00
local midi_region = nil
2016-10-21 00:36:13 -04:00
local audio_regions = { }
2017-09-26 23:03:10 -04:00
local start_time = Session : current_end_sample ( )
local end_time = Session : current_start_sample ( )
2019-09-01 23:19:27 -04:00
local max_pos = 0
local cur_pos = 0
2016-10-06 21:45:59 -04:00
for r in sel.regions : regionlist ( ) : iter ( ) do
if r : to_midiregion ( ) : isnil ( ) then
2016-10-21 00:36:13 -04:00
local st = r : position ( )
local ln = r : length ( )
local et = st + ln
if st < start_time then
start_time = st
end
if et > end_time then
end_time = et
end
2017-02-17 20:25:28 -05:00
table.insert ( audio_regions , r )
2019-09-01 23:19:27 -04:00
max_pos = max_pos + r : to_readable ( ) : readable_length ( )
2016-10-06 21:45:59 -04:00
else
2016-10-21 00:36:13 -04:00
midi_region = r : to_midiregion ( )
2016-10-06 21:45:59 -04:00
end
end
2019-09-02 15:20:26 -04:00
if # audio_regions == 0 then
LuaDialog.Message ( " Polyphonic Audio to MIDI " , " No source audio region(s) selected. \n At least one audio-region to be analyzed need to be selected. " , LuaDialog.MessageType . Error , LuaDialog.ButtonType . Close ) : run ( )
return
end
if not midi_region then
LuaDialog.Message ( " Polyphonic Audio to MIDI " , " No target MIDI region selected. \n A MIDI region, ideally empty, and extending beyond the selected audio-region(s) needs to be selected. " , LuaDialog.MessageType . Error , LuaDialog.ButtonType . Close ) : run ( )
return
end
2016-10-21 00:36:13 -04:00
midi_region : set_initial_position ( start_time )
midi_region : set_length ( end_time - start_time , 0 )
2016-10-07 10:33:50 -04:00
2019-09-02 09:45:14 -04:00
local pdialog = LuaDialog.ProgressWindow ( " Audio to MIDI " , true )
2019-09-01 23:19:27 -04:00
function progress ( _ , pos )
return pdialog : progress ( ( cur_pos + pos ) / max_pos , " Analyzing " )
end
2016-10-21 00:36:13 -04:00
for i , ar in pairs ( audio_regions ) do
local a_off = ar : position ( )
2016-11-10 12:02:17 -05:00
local b_off = midi_region : quarter_note ( ) - midi_region : start_beats ( )
2016-10-06 21:45:59 -04:00
2019-09-01 23:19:27 -04:00
vamp : analyze ( ar : to_readable ( ) , 0 , progress )
if pdialog : canceled ( ) then
goto out
end
cur_pos = cur_pos + ar : to_readable ( ) : readable_length ( )
pdialog : progress ( cur_pos / max_pos , " Generating MIDI " )
2016-10-21 00:36:13 -04:00
local fl = vamp : plugin ( ) : getRemainingFeatures ( ) : at ( 0 )
if fl and fl : size ( ) > 0 then
local mm = midi_region : midi_source ( 0 ) : model ( )
local midi_command = mm : new_note_diff_command ( " Audio2Midi " )
for f in fl : iter ( ) do
local ft = Vamp.RealTime . realTime2Frame ( f.timestamp , sr )
local fd = Vamp.RealTime . realTime2Frame ( f.duration , sr )
local fn = f.values : at ( 0 )
2016-10-06 21:45:59 -04:00
2017-09-26 23:03:10 -04:00
local bs = tm : exact_qn_at_sample ( a_off + ft , 0 )
local be = tm : exact_qn_at_sample ( a_off + ft + fd , 0 )
2016-10-09 10:42:47 -04:00
2016-10-21 00:36:13 -04:00
local pos = Evoral.Beats ( bs - b_off )
local len = Evoral.Beats ( be - bs )
local note = ARDOUR.LuaAPI . new_noteptr ( 1 , pos , len , fn + 1 , 0x7f )
midi_command : add ( note )
end
mm : apply_command ( Session , midi_command )
2016-10-06 21:45:59 -04:00
end
2019-09-01 23:19:27 -04:00
-- reset the plugin (prepare for next iteration)
vamp : reset ( )
2016-10-06 21:45:59 -04:00
end
2019-09-01 23:19:27 -04:00
:: out ::
pdialog : done ( ) ;
pdialog = nil
vamp = nil ;
collectgarbage ( )
2016-10-06 21:45:59 -04:00
end end
2017-02-18 10:12:59 -05:00
function icon ( params ) return function ( ctx , width , height , fg )
local txt = Cairo.PangoLayout ( ctx , " ArdourMono " .. math.ceil ( width * .7 ) .. " px " )
txt : set_text ( " \u{2669} " ) -- quarter note symbol UTF8
local tw , th = txt : get_pixel_size ( )
ctx : set_source_rgba ( ARDOUR.LuaAPI . color_to_rgba ( fg ) )
ctx : move_to ( .5 * ( width - tw ) , .5 * ( height - th ) )
txt : show_in_cairo_context ( ctx )
end end