Do not allow to embed ogg/vorbis files, require import
[ardour.git] / scripts / vamp_audio_to_midi.lua
1 ardour {
2         ["type"] = "EditorAction",
3         name = "Polyphonic Audio to MIDI",
4         license     = "MIT",
5         author      = "Ardour Team",
6 description = [[
7 Analyze audio from the selected audio region to a selected MIDI region.
8
9 A MIDI region on the target track will have to be created first (use the pen tool).
10
11 This script uses the Polyphonic Transcription VAMP plugin from Queen Mary Univ, London.
12 The plugin works best at 44.1KHz input sample rate, and is tuned for piano and guitar music. Velocity is not estimated.
13 ]]
14 }
15
16 function factory () return function ()
17         local sel = Editor:get_selection ()
18         local sr = Session:nominal_sample_rate ()
19         local tm = Session:tempo_map ()
20         local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-transcription", sr)
21         local midi_region = nil
22         local audio_regions = {}
23         local start_time = Session:current_end_sample ()
24         local end_time = Session:current_start_sample ()
25         local max_pos = 0
26         local cur_pos = 0
27         for r in sel.regions:regionlist ():iter () do
28                 if r:to_midiregion():isnil() then
29                         local st = r:position()
30                         local ln = r:length()
31                         local et = st + ln
32                         if st < start_time then
33                                 start_time = st
34                         end
35                         if et > end_time then
36                                 end_time = et
37                         end
38                         table.insert(audio_regions, r)
39                         max_pos = max_pos + r:to_readable ():readable_length ()
40                 else
41                         midi_region = r:to_midiregion()
42                 end
43         end
44
45         if #audio_regions == 0 then
46                 LuaDialog.Message ("Polyphonic Audio to MIDI", "No source audio region(s) selected.\nAt least one audio-region to be analyzed need to be selected.", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run ()
47                 return
48         end
49         if not midi_region then
50                 LuaDialog.Message ("Polyphonic Audio to MIDI", "No target MIDI region selected.\nA MIDI region, ideally empty, and extending beyond the selected audio-region(s) needs to be selected.", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run ()
51                 return
52         end
53
54         midi_region:set_initial_position(start_time)
55         midi_region:set_length(end_time - start_time, 0)
56
57         local pdialog = LuaDialog.ProgressWindow ("Audio to MIDI", true)
58         function progress (_, pos)
59                 return pdialog:progress ((cur_pos + pos) / max_pos, "Analyzing")
60         end
61
62         for i,ar in pairs(audio_regions) do
63                 local a_off = ar:position ()
64                 local b_off = midi_region:quarter_note () - midi_region:start_beats ()
65
66                 vamp:analyze (ar:to_readable (), 0, progress)
67
68                 if pdialog:canceled () then
69                         goto out
70                 end
71
72                 cur_pos = cur_pos + ar:to_readable ():readable_length ()
73                 pdialog:progress (cur_pos / max_pos, "Generating MIDI")
74
75                 local fl = vamp:plugin ():getRemainingFeatures ():at (0)
76                 if fl and fl:size() > 0 then
77                         local mm = midi_region:midi_source(0):model()
78                         local midi_command = mm:new_note_diff_command ("Audio2Midi")
79                         for f in fl:iter () do
80                                 local ft = Vamp.RealTime.realTime2Frame (f.timestamp, sr)
81                                 local fd = Vamp.RealTime.realTime2Frame (f.duration, sr)
82                                 local fn = f.values:at (0)
83
84                                 local bs = tm:exact_qn_at_sample (a_off + ft, 0)
85                                 local be = tm:exact_qn_at_sample (a_off + ft + fd, 0)
86
87                                 local pos = Evoral.Beats (bs - b_off)
88                                 local len = Evoral.Beats (be - bs)
89                                 local note = ARDOUR.LuaAPI.new_noteptr (1, pos, len, fn + 1, 0x7f)
90                                 midi_command:add (note)
91                         end
92                         mm:apply_command (Session, midi_command)
93                 end
94                 -- reset the plugin (prepare for next iteration)
95                 vamp:reset ()
96         end
97
98         ::out::
99         pdialog:done ();
100         pdialog = nil
101         vamp = nil;
102         collectgarbage ()
103 end end
104
105 function icon (params) return function (ctx, width, height, fg)
106         local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(width * .7) .. "px")
107         txt:set_text ("\u{2669}") -- quarter note symbol UTF8
108         local tw, th = txt:get_pixel_size ()
109         ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg))
110         ctx:move_to (.5 * (width - tw), .5 * (height - th))
111         txt:show_in_cairo_context (ctx)
112 end end