1 /*
2  * Copyright (C) 2018-2021 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of Zrythm
5  *
6  * Zrythm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Zrythm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
18  *
19  * This file incorporates work covered by the following copyright and
20  * permission notice:
21  *
22  *  AUTHOR: Steven Goodwin (StevenGoodwin@gmail.com)
23  *      Copyright 2010, Steven Goodwin.
24  *
25  *  This program is free software; you can redistribute it and/or
26  *  modify it under the terms of the GNU General Public License as
27  *  published by the Free Software Foundation; either version 3 of
28  *  the License,or (at your option) any later version.
29  *
30  *  This program is distributed in the hope that it will be useful,
31  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
32  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  *  GNU General Public License for more details.
34  *
35  *  You should have received a copy of the GNU General Public License
36  *  along with this program; if not, write to the Free Software
37  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38  */
39 
40 #include "audio/channel.h"
41 #include "audio/exporter.h"
42 #include "audio/midi_event.h"
43 #include "audio/midi_file.h"
44 #include "audio/midi_note.h"
45 #include "audio/midi_region.h"
46 #include "audio/region.h"
47 #include "audio/tempo_track.h"
48 #include "audio/track.h"
49 #include "gui/backend/event.h"
50 #include "gui/backend/event_manager.h"
51 #include "gui/widgets/bot_dock_edge.h"
52 #include "gui/widgets/center_dock.h"
53 #include "gui/widgets/clip_editor.h"
54 #include "gui/widgets/clip_editor_inner.h"
55 #include "gui/widgets/main_window.h"
56 #include "gui/widgets/midi_arranger.h"
57 #include "gui/widgets/midi_editor_space.h"
58 #include "gui/widgets/midi_region.h"
59 #include "gui/widgets/region.h"
60 #include "gui/widgets/timeline_arranger.h"
61 #include "gui/widgets/timeline_panel.h"
62 #include "project.h"
63 #include "utils/arrays.h"
64 #include "utils/flags.h"
65 #include "utils/math.h"
66 #include "utils/mem.h"
67 #include "utils/object_utils.h"
68 #include "utils/objects.h"
69 #include "utils/yaml.h"
70 #include "zrythm_app.h"
71 
72 #include <ext/midilib/src/midifile.h>
73 #include <ext/midilib/src/midiutil.h>
74 
75 ZRegion *
midi_region_new(const Position * start_pos,const Position * end_pos,unsigned int track_name_hash,int lane_pos,int idx_inside_lane)76 midi_region_new (
77   const Position * start_pos,
78   const Position * end_pos,
79   unsigned int     track_name_hash,
80   int              lane_pos,
81   int              idx_inside_lane)
82 {
83   ZRegion * self = object_new (MidiRegion);
84 
85   self->id.type = REGION_TYPE_MIDI;
86 
87   region_init (
88     self, start_pos, end_pos, track_name_hash,
89     lane_pos, idx_inside_lane);
90 
91    return self;
92 }
93 
94 /**
95  * Create a region from the chord descriptor.
96  *
97  * Default size will be timeline snap and default
98  * notes size will be editor snap.
99  */
100 ZRegion *
midi_region_new_from_chord_descr(const Position * pos,ChordDescriptor * descr,unsigned int track_name_hash,int lane_pos,int idx_inside_lane)101 midi_region_new_from_chord_descr (
102   const Position *  pos,
103   ChordDescriptor * descr,
104   unsigned int      track_name_hash,
105   int               lane_pos,
106   int               idx_inside_lane)
107 {
108   int r_length_ticks =
109     snap_grid_get_default_ticks (
110       SNAP_GRID_TIMELINE);
111   int mn_length_ticks =
112     snap_grid_get_default_ticks (
113       SNAP_GRID_EDITOR);
114 
115   /* get region end pos */
116   Position r_end_pos;
117   position_from_ticks (
118     &r_end_pos,
119     pos->ticks + (double) r_length_ticks);
120 
121   /* create region */
122   ZRegion * r =
123     midi_region_new (
124       pos, &r_end_pos, track_name_hash, lane_pos,
125       idx_inside_lane);
126 
127   /* get midi note positions */
128   Position mn_pos, mn_end_pos;
129   position_init (&mn_pos);
130   position_from_ticks (
131     &mn_end_pos, mn_length_ticks);
132 
133   /* create midi notes */
134   for (int i = 0; i < CHORD_DESCRIPTOR_MAX_NOTES;
135        i++)
136     {
137       if (descr->notes[i])
138         {
139           MidiNote * mn =
140             midi_note_new (
141               &r->id, &mn_pos, &mn_end_pos,
142               i + 36, VELOCITY_DEFAULT);
143           midi_region_add_midi_note (
144             r, mn, F_NO_PUBLISH_EVENTS);
145         }
146     }
147 
148   return r;
149 }
150 
151 /**
152  * Prints the MidiNotes in the Region.
153  *
154  * Used for debugging.
155  */
156 void
midi_region_print_midi_notes(ZRegion * self)157 midi_region_print_midi_notes (
158   ZRegion * self)
159 {
160   MidiNote * mn;
161   for (int i = 0; i < self->num_midi_notes; i++)
162     {
163       mn = self->midi_notes[i];
164       g_message ("Note at %d", i);
165       midi_note_print (mn);
166     }
167 }
168 
169 /**
170  * Inserts the MidiNote to the given ZRegion.
171  *
172  * @param idx Index to insert at.
173  * @param pub_events Publish UI events or not.
174  */
175 void
midi_region_insert_midi_note(ZRegion * self,MidiNote * midi_note,int idx,int pub_events)176 midi_region_insert_midi_note (
177   ZRegion *  self,
178   MidiNote * midi_note,
179   int        idx,
180   int        pub_events)
181 {
182   array_double_size_if_full (
183     self->midi_notes, self->num_midi_notes,
184     self->midi_notes_size, MidiNote *);
185   array_insert (
186     self->midi_notes, self->num_midi_notes,
187     idx, midi_note);
188 
189   for (int i = idx; i < self->num_midi_notes; i++)
190     {
191       MidiNote * mn = self->midi_notes[i];
192       midi_note_set_region_and_index (
193         mn, self, i);
194     }
195 
196   if (pub_events)
197     {
198       EVENTS_PUSH (
199         ET_ARRANGER_OBJECT_CREATED, midi_note);
200     }
201 }
202 
203 /**
204  * Returns the midi note with the given pitch from
205  * the unended notes.
206  *
207  * Used when recording.
208  *
209  * @param pitch The pitch. If -1, it returns any
210  *   unended note. This is useful when the loop
211  *   point is met and we want to end them all.
212  */
213 MidiNote *
midi_region_pop_unended_note(ZRegion * self,int pitch)214 midi_region_pop_unended_note (
215   ZRegion * self,
216   int          pitch)
217 {
218   MidiNote * match = NULL;
219   for (int i = 0; i < self->num_unended_notes; i++)
220     {
221       MidiNote * mn = self->unended_notes[i];
222       if (pitch == -1 || mn->val == pitch)
223         {
224           match = mn;
225           break;
226         }
227     }
228 
229   if (match)
230     {
231       /* pop it from the array */
232       array_delete (
233         self->unended_notes,
234         self->num_unended_notes,
235         match);
236 
237       return match;
238     }
239 
240   return NULL;
241 }
242 
243 /**
244  * Gets first midi note
245  */
246 MidiNote *
midi_region_get_first_midi_note(ZRegion * region)247 midi_region_get_first_midi_note (
248   ZRegion * region)
249 {
250   MidiNote * result = NULL;
251   for (int i = 0;
252     i < region->num_midi_notes;
253     i++)
254   {
255     ArrangerObject * cur_mn_obj =
256       (ArrangerObject *) region->midi_notes[i];
257     ArrangerObject * result_obj =
258       (ArrangerObject *) result;
259     if (!result
260         || result_obj->end_pos.ticks > cur_mn_obj->end_pos.ticks)
261     {
262       result = region->midi_notes[i];
263     }
264   }
265   return result;
266 }
267 /**
268  * Gets last midi note
269  */
270 MidiNote *
midi_region_get_last_midi_note(ZRegion * region)271 midi_region_get_last_midi_note (
272   ZRegion * region)
273 {
274   MidiNote * result = NULL;
275   for (int i = 0;
276     i < region->num_midi_notes;
277     i++)
278   {
279     if (
280       !result ||
281       ((ArrangerObject *) result)->
282         end_pos.ticks <
283       ((ArrangerObject *) region->midi_notes[i])->
284         end_pos.ticks)
285     {
286       result = region->midi_notes[i];
287     }
288   }
289   return result;
290 }
291 
292 /**
293  * Gets highest midi note
294  */
295 MidiNote *
midi_region_get_highest_midi_note(ZRegion * region)296 midi_region_get_highest_midi_note (
297   ZRegion * region)
298 {
299   MidiNote * result = NULL;
300   for (int i = 0;
301     i < region->num_midi_notes;
302     i++)
303   {
304     if (!result
305         || result->val < region->midi_notes[i]->val)
306     {
307       result = region->midi_notes[i];
308     }
309   }
310   return result;
311 }
312 
313 /**
314  * Gets lowest midi note
315  */
316 MidiNote *
midi_region_get_lowest_midi_note(ZRegion * region)317 midi_region_get_lowest_midi_note (
318   ZRegion * region)
319 {
320   MidiNote * result = NULL;
321   for (int i = 0;
322     i < region->num_midi_notes;
323     i++)
324   {
325     if (!result
326         || result->val > region->midi_notes[i]->val)
327     {
328       result = region->midi_notes[i];
329     }
330   }
331 
332   return result;
333 }
334 
335 /**
336  * Removes the MIDI note from the Region.
337  *
338  * @param free Also free the MidiNote.
339  * @param pub_event Publish an event.
340  */
341 void
midi_region_remove_midi_note(ZRegion * region,MidiNote * midi_note,int free,int pub_event)342 midi_region_remove_midi_note (
343   ZRegion *   region,
344   MidiNote * midi_note,
345   int        free,
346   int        pub_event)
347 {
348   if (MA_SELECTIONS)
349     {
350       arranger_selections_remove_object (
351         (ArrangerSelections *) MA_SELECTIONS,
352         (ArrangerObject *) midi_note);
353     }
354 
355   /*ARRANGER_WIDGET_GET_PRIVATE (*/
356     /*MW_MIDI_ARRANGER);*/
357   /*if (ar_prv->start_object ==*/
358         /*(ArrangerObject *) midi_note)*/
359     /*{*/
360       /*ar_prv->start_object = NULL;*/
361     /*}*/
362 
363   array_delete (
364     region->midi_notes, region->num_midi_notes,
365     midi_note);
366 
367   for (int i = 0; i < region->num_midi_notes; i++)
368     {
369       midi_note_set_region_and_index (
370         region->midi_notes[i], region, i);
371     }
372 
373   if (free)
374     free_later (midi_note, arranger_object_free);
375 
376   if (pub_event)
377     {
378       EVENTS_PUSH (
379         ET_ARRANGER_OBJECT_REMOVED,
380         ARRANGER_OBJECT_TYPE_MIDI_NOTE);
381     }
382 }
383 
384 /**
385  * Creates a MIDI region from the given MIDI
386  * file path, starting at the given Position.
387  *
388  * @param idx The index of this track, starting from
389  *   0. This will be sequential, ie, if idx 1 is
390  *   requested and the MIDI file only has tracks
391  *   5 and 7, it will use track 7.
392  */
393 ZRegion *
midi_region_new_from_midi_file(const Position * start_pos,const char * abs_path,unsigned int track_name_hash,int lane_pos,int idx_inside_lane,int idx)394 midi_region_new_from_midi_file (
395   const Position * start_pos,
396   const char *     abs_path,
397   unsigned int     track_name_hash,
398   int              lane_pos,
399   int              idx_inside_lane,
400   int              idx)
401 {
402   g_message (
403     "%s: reading from %s...", __func__, abs_path);
404 
405   ZRegion * self = object_new (ZRegion);
406   ArrangerObject * r_obj =
407     (ArrangerObject *) self;
408 
409   self->id.type = REGION_TYPE_MIDI;
410 
411   MIDI_FILE * mf =
412     midiFileOpen (abs_path);
413   g_return_val_if_fail (mf, NULL);
414 
415   char str[128];
416   char txt[60000];
417   int ev;
418   MIDI_MSG msg;
419   unsigned int j;
420   Position pos, global_pos;
421   MidiNote * mn;
422   double ticks;
423 
424   Position end_pos;
425   position_from_ticks (
426     &end_pos, start_pos->ticks + 1);
427   region_init (
428     self, start_pos, &end_pos, track_name_hash,
429     lane_pos, idx_inside_lane);
430 
431   midiReadInitMessage (&msg);
432   int num_tracks = midiReadGetNumTracks (mf);
433   double ppqn = (double) midiFileGetPPQN (mf);
434   double transport_ppqn =
435     transport_get_ppqn (TRANSPORT);
436 
437   int actual_iter = 0;
438 
439   for (int i = 0; i < num_tracks; i++)
440     {
441       if (!midi_file_track_has_data (abs_path, i))
442         {
443           continue;
444         }
445 
446       if (actual_iter != idx)
447         {
448           actual_iter++;
449           continue;
450         }
451 
452       g_message (
453         "%s: reading MIDI Track %d", __func__, i);
454       g_return_val_if_fail (i < 1000, NULL);
455       while (midiReadGetNextMessage (mf, i, &msg))
456         {
457           /* convert time to zrythm time */
458           ticks =
459             ((double) msg.dwAbsPos * transport_ppqn) /
460             ppqn;
461           position_from_ticks (&pos, ticks);
462           position_from_ticks (
463             &global_pos,
464             r_obj->pos.ticks + ticks);
465           g_debug (
466             "dwAbsPos: %d ", msg.dwAbsPos);
467 
468           int bars = position_get_bars (&pos, true);
469           if (ZRYTHM_HAVE_UI &&
470               bars > TRANSPORT->total_bars -8)
471             {
472               transport_update_total_bars (
473                 TRANSPORT, bars + 8,
474                 F_PUBLISH_EVENTS);
475             }
476 
477           if (msg.bImpliedMsg)
478             {
479               ev = msg.iImpliedMsg;
480             }
481           else
482             {
483               ev = msg.iType;
484             }
485 
486           if (muGetMIDIMsgName (str, ev))
487             {
488               g_debug ("MIDI msg name: %s", str);
489             }
490           switch(ev)
491             {
492             case msgNoteOff:
493 handle_note_off:
494               g_debug (
495                 "Note off at %d "
496                 "[ch %d pitch %d]",
497                 msg.dwAbsPos,
498                 msg.MsgData.NoteOff.iChannel,
499                 msg.MsgData.NoteOff.iNote);
500               mn =
501                 midi_region_pop_unended_note (
502                   self, msg.MsgData.NoteOff.iNote);
503               if (mn)
504                 {
505                   arranger_object_end_pos_setter (
506                     (ArrangerObject *) mn,  &pos);
507                 }
508               else
509                 {
510                   g_message (
511                     "Found a Note off event without "
512                     "a corresponding Note on. "
513                     "Skipping...");
514                 }
515               break;
516             case msgNoteOn:
517               /* 0 velocity is a note off */
518               if (msg.MsgData.NoteOn.iVolume == 0)
519                 {
520                   msg.MsgData.NoteOff.iChannel =
521                     msg.MsgData.NoteOn.iChannel;
522                   msg.MsgData.NoteOff.iNote =
523                     msg.MsgData.NoteOn.iNote;
524                   goto handle_note_off;
525                 }
526 
527               g_debug (
528                 "Note on at %d "
529                 "[ch %d pitch %d vel %d]",
530                 msg.dwAbsPos,
531                 msg.MsgData.NoteOn.iChannel,
532                 msg.MsgData.NoteOn.iNote,
533                 msg.MsgData.NoteOn.iVolume);
534               midi_region_start_unended_note (
535                 self, &pos, NULL,
536                 msg.MsgData.NoteOn.iNote,
537                 msg.MsgData.NoteOn.iVolume, 0);
538               break;
539             case  msgNoteKeyPressure:
540               muGetNameFromNote (
541                 str,
542                 msg.MsgData.NoteKeyPressure.iNote);
543               g_debug (
544                 "(%.2d) %s %d",
545                 msg.MsgData.NoteKeyPressure.
546                   iChannel,
547                 str,
548                 msg.MsgData.NoteKeyPressure.
549                   iPressure);
550               break;
551             case  msgSetParameter:
552               muGetControlName (
553                 str,
554                 msg.MsgData.NoteParameter.iControl);
555               g_debug (
556                 "(%.2d) %s -> %d",
557                 msg.MsgData.NoteParameter.iChannel,
558                 str,
559                 msg.MsgData.NoteParameter.iParam);
560               break;
561             case  msgSetProgram:
562               muGetInstrumentName (
563                 str,
564                 msg.MsgData.ChangeProgram.iProgram);
565               g_debug (
566                 "(%.2d) %s",
567                 msg.MsgData.ChangeProgram.iChannel,
568                 str);
569               break;
570             case msgChangePressure:
571               muGetControlName (
572                 str,
573                 msg.MsgData.ChangePressure.
574                   iPressure);
575               g_debug (
576                 "(%.2d) %s",
577                 msg.MsgData.ChangePressure.iChannel,
578                 str);
579               break;
580             case msgSetPitchWheel:
581               g_debug (
582                 "(%.2d) %d",
583                 msg.MsgData.PitchWheel.iChannel,
584                 msg.MsgData.PitchWheel.iPitch);
585               break;
586             case msgMetaEvent:
587               g_debug ("---- meta events");
588               switch(msg.MsgData.MetaEvent.iType)
589                 {
590                 case metaMIDIPort:
591                   g_debug (
592                     "MIDI Port = %d",
593                     msg.MsgData.MetaEvent.Data.
594                       iMIDIPort);
595                   break;
596                 case metaSequenceNumber:
597                   g_debug (
598                     "Sequence Number = %d",
599                     msg.MsgData.MetaEvent.Data.
600                       iSequenceNumber);
601                   break;
602                 case  metaTextEvent:
603                   g_debug (
604                     "Text = '%s'",
605                     msg.MsgData.MetaEvent.Data.Text.
606                       pData);
607                   break;
608                 case  metaCopyright:
609                   g_debug (
610                     "Copyright = '%s'",
611                     msg.MsgData.MetaEvent.Data.Text.
612                       pData);
613                   break;
614                 case  metaTrackName:
615                   {
616                     char tmp[6000];
617                     strncpy (
618                       tmp,
619                       (char *) msg.MsgData.MetaEvent.Data.
620                         Text.pData,
621                       msg.iMsgSize - 3);
622                     tmp[msg.iMsgSize - 3] = '\0';
623                     arranger_object_set_name (
624                       (ArrangerObject *) self,
625                       tmp, F_NO_PUBLISH_EVENTS);
626                     g_warn_if_fail (self->name);
627                     g_message (
628                       "[data sz %d] Track name = '%s'",
629                       msg.iMsgSize - 3, self->name);
630                   }
631                   break;
632                 case  metaInstrument:
633                   g_message (
634                     "Instrument = '%s'",
635                     msg.MsgData.MetaEvent.Data.Text.
636                       pData);
637                   break;
638                 case  metaLyric:
639                   g_message (
640                     "Lyric = '%s'",
641                     msg.MsgData.MetaEvent.
642                       Data.Text.pData);
643                   break;
644                 case  metaMarker:
645                   g_message (
646                     "Marker = '%s'",
647                     msg.MsgData.MetaEvent.
648                       Data.Text.pData);
649                   break;
650                 case  metaCuePoint:
651                   g_message (
652                     "Cue point = '%s'",
653                     msg.MsgData.MetaEvent.
654                       Data.Text.pData);
655                   break;
656                 case  metaEndSequence:
657                   g_message ("End Sequence");
658                   if (position_is_equal (
659                         &pos, start_pos))
660                     {
661                        /* this is an empty track,
662                         * so return NULL
663                        * instead */
664                       return NULL;
665                     }
666                   arranger_object_end_pos_setter (
667                     r_obj, &global_pos);
668                   arranger_object_loop_end_pos_setter (
669                     r_obj, &global_pos);
670                   break;
671                 case  metaSetTempo:
672                   g_message (
673                     "tempo %d",
674                     msg.MsgData.MetaEvent.Data.
675                       Tempo.iBPM);
676                   break;
677                 case  metaSMPTEOffset:
678                   g_message (
679                     "SMPTE offset = %d:%d:%d.%d %d",
680                     msg.MsgData.MetaEvent.Data.
681                       SMPTE.iHours,
682                     msg.MsgData.MetaEvent.Data.
683                       SMPTE.iMins,
684                     msg.MsgData.MetaEvent.Data.
685                       SMPTE.iSecs,
686                     msg.MsgData.MetaEvent.Data.
687                       SMPTE.iFrames,
688                     msg.MsgData.MetaEvent.Data.
689                       SMPTE.iFF);
690                   break;
691                 case metaTimeSig:
692                   g_message (
693                     "Time sig = %d/%d",
694                     msg.MsgData.MetaEvent.Data.
695                       TimeSig.iNom,
696                     msg.MsgData.MetaEvent.Data.
697                       TimeSig.iDenom /
698                       MIDI_NOTE_CROCHET);
699                   break;
700                 case metaKeySig:
701                   if (muGetKeySigName (
702                         str,
703                         msg.MsgData.MetaEvent.
704                           Data.KeySig.iKey))
705                     g_message("Key sig = %s", str);
706                   break;
707                 case  metaSequencerSpecific:
708                   g_message("Sequencer specific = ");
709                   /*HexList(msg.MsgData.MetaEvent.Data.Sequencer.pData, msg.MsgData.MetaEvent.Data.Sequencer.iSize);*/
710                   break;
711                 }
712               break;
713 
714             case msgSysEx1:
715             case msgSysEx2:
716               g_message("Sysex = ");
717               /*HexList(msg.MsgData.SysEx.pData, msg.MsgData.SysEx.iSize);*/
718               break;
719             }
720 
721           /* print the hex */
722           if (ev == msgSysEx1 ||
723               (ev==msgMetaEvent &&
724                msg.MsgData.MetaEvent.iType ==
725                  metaSequencerSpecific))
726             {
727             /* Already done a hex dump */
728             }
729           else
730             {
731               char tmp[100];
732               strcpy (txt, "[");
733               if (msg.bImpliedMsg)
734                 {
735                   sprintf (
736                     tmp, "%.2x!", msg.iImpliedMsg);
737                   strcat (txt, tmp);
738                 }
739               for (j = 0; j < msg.iMsgSize; j++)
740                 {
741                   sprintf(
742                     tmp, " %.2x ", msg.data[j]);
743                   strcat (txt, tmp);
744                 }
745               strcat (txt, "]");
746               g_debug ("%s", txt);
747             }
748         }
749 
750       if (actual_iter == idx)
751         {
752           break;
753         }
754     }
755 
756   midiReadFreeMessage (&msg);
757   midiFileClose (mf);
758 
759   if (self->num_unended_notes != 0)
760     {
761       g_warning (
762         "unended notes found: %d",
763         self->num_unended_notes);
764 
765       double length =
766         arranger_object_get_length_in_ticks (
767           (ArrangerObject *) self);
768       position_from_ticks (&end_pos, length);
769 
770       while (self->num_unended_notes > 0)
771         {
772           mn =
773             midi_region_pop_unended_note (self, -1);
774           arranger_object_end_pos_setter (
775             (ArrangerObject *) mn,  &end_pos);
776         }
777     }
778 
779   g_return_val_if_fail (
780     position_is_before (
781       &self->base.pos, &self->base.end_pos), NULL);
782 
783   g_message (
784     "%s: done ~ %d MIDI notes read", __func__,
785     self->num_midi_notes);
786 
787   return self;
788 }
789 
790 /**
791  * Starts an unended note with the given pitch and
792  * velocity and adds it to \ref ZRegion.midi_notes.
793  *
794  * If another note exists with the same pitch, this
795  * will be ignored.
796  *
797  * @param end_pos If this is NULL, it will be set to
798  *   1 tick after the start_pos.
799  */
800 void
midi_region_start_unended_note(ZRegion * self,Position * start_pos,Position * _end_pos,int pitch,int vel,int pub_events)801 midi_region_start_unended_note (
802   ZRegion *  self,
803   Position * start_pos,
804   Position * _end_pos,
805   int        pitch,
806   int        vel,
807   int        pub_events)
808 {
809   g_return_if_fail (self && start_pos);
810 
811   /* set end pos */
812   Position end_pos;
813   if (_end_pos)
814     {
815       position_set_to_pos (&end_pos, _end_pos);
816     }
817   else
818     {
819       position_set_to_pos (&end_pos, start_pos);
820       position_add_ticks (&end_pos, 1);
821     }
822 
823   MidiNote * mn =
824     midi_note_new (
825       &self->id, start_pos, &end_pos, pitch, vel);
826   midi_region_add_midi_note (self, mn, pub_events);
827 
828   /* add to unended notes */
829   array_append (
830     self->unended_notes,
831     self->num_unended_notes, mn);
832 }
833 
834 /**
835  * Exports the ZRegion to an existing MIDI file
836  * instance.
837  *
838  * @param add_region_start Add the region start
839  *   offset to the positions.
840  * @param export_full Traverse loops and export the
841  *   MIDI file as it would be played inside Zrythm.
842  *   If this is 0, only the original region (from
843  *   true start to true end) is exported.
844  * @param use_track_pos Whether to use the track
845  *   position in the MIDI data. The track will be
846  *   set to 1 if false.
847  */
848 void
midi_region_write_to_midi_file(ZRegion * self,MIDI_FILE * mf,const int add_region_start,bool export_full,bool use_track_pos)849 midi_region_write_to_midi_file (
850   ZRegion *   self,
851   MIDI_FILE * mf,
852   const int   add_region_start,
853   bool        export_full,
854   bool        use_track_pos)
855 {
856   MidiEvents * events =
857     midi_region_get_as_events (
858       self, add_region_start, export_full);
859   MidiEvent * ev;
860   int i;
861   ArrangerObject * r_obj =
862     (ArrangerObject *) self;
863   Track * track =
864     arranger_object_get_track (r_obj);
865   for (i = 0; i < events->num_events; i++)
866     {
867       ev = &events->events[i];
868 
869       BYTE tmp[] =
870         { ev->raw_buffer[0],
871           ev->raw_buffer[1],
872         ev->raw_buffer[2] };
873       midiTrackAddRaw (
874         mf, use_track_pos ? track->pos : 1, 3, tmp,
875         1,
876         i == 0 ?
877           (int) ev->time :
878           (int)
879           (ev->time - events->events[i - 1].time));
880     }
881   midi_events_free (events);
882 }
883 
884 /**
885  * Exports the ZRegion to a specified MIDI file.
886  *
887  * @param full_path Absolute path to the MIDI file.
888  * @param export_full Traverse loops and export the
889  *   MIDI file as it would be played inside Zrythm.
890  *   If this is 0, only the original region (from
891  *   true start to true end) is exported.
892  */
893 void
midi_region_export_to_midi_file(ZRegion * self,const char * full_path,int midi_version,const int export_full)894 midi_region_export_to_midi_file (
895   ZRegion * self,
896   const char *   full_path,
897   int            midi_version,
898   const int      export_full)
899 {
900   MIDI_FILE *mf;
901 
902   if ((mf = midiFileCreate(full_path, TRUE)))
903     {
904       /* Write tempo information out to track 1 */
905       midiSongAddTempo (
906         mf, 1,
907         (int)
908         tempo_track_get_current_bpm (P_TEMPO_TRACK));
909 
910       /* All data is written out to _tracks_ not
911        * channels. We therefore
912       ** set the current channel before writing
913       data out. Channel assignments
914       ** can change any number of times during the
915       file, and affect all
916       ** tracks messages until it is changed. */
917       midiFileSetTracksDefaultChannel (
918         mf, 1, MIDI_CHANNEL_1);
919 
920       midiFileSetPPQN (mf, TICKS_PER_QUARTER_NOTE);
921 
922       midiFileSetVersion (mf, midi_version);
923 
924       /* common time: 4 crochet beats, per bar */
925       int beats_per_bar =
926         tempo_track_get_beats_per_bar (
927           P_TEMPO_TRACK);
928       midiSongAddSimpleTimeSig (
929         mf, 1, beats_per_bar,
930         math_round_double_to_int (
931           TRANSPORT->ticks_per_beat));
932 
933       midi_region_write_to_midi_file (
934         self, mf, 0, export_full, false);
935 
936       midiFileClose(mf);
937     }
938 }
939 
940 /**
941  * Returns the MIDI channel that this region should
942  * be played on, starting from 1.
943  */
944 uint8_t
midi_region_get_midi_ch(const ZRegion * self)945 midi_region_get_midi_ch (
946   const ZRegion * self)
947 {
948   g_return_val_if_fail (self, 1);
949   uint8_t ret;
950   TrackLane * lane = region_get_lane (self);
951   g_return_val_if_fail (lane, 1);
952   if (lane->midi_ch > 0)
953     ret = lane->midi_ch;
954   else
955     {
956       Track * track = track_lane_get_track (lane);
957       g_return_val_if_fail (track, 1);
958       ret = track->midi_ch;
959     }
960 
961   g_return_val_if_fail (ret > 0, 1);
962 
963   return ret;
964 }
965 
966 /**
967  * Returns a newly initialized MidiEvents with
968  * the contents of the region converted into
969  * events.
970  *
971  * Must be free'd with midi_events_free ().
972  *
973  * @param add_region_start Add the region start
974  *   offset to the positions.
975  * @param export_full Traverse loops and export the
976  *   MIDI file as it would be played inside Zrythm.
977  *   If this is 0, only the original region (from
978  *   true start to true end) is exported.
979  */
980 MidiEvents *
midi_region_get_as_events(const ZRegion * self,const int add_region_start,const int full)981 midi_region_get_as_events (
982   const ZRegion * self,
983   const int       add_region_start,
984   const int       full)
985 {
986   MidiEvents * events = midi_events_new ();
987 
988   ArrangerObject * self_obj =
989     (ArrangerObject *) self;
990 
991   double region_start = 0;
992   if (add_region_start)
993     region_start = self_obj->pos.ticks;
994 
995   MidiNote * mn;
996   for (int i = 0; i < self->num_midi_notes; i++)
997     {
998       mn = self->midi_notes[i];
999       ArrangerObject * mn_obj =
1000         (ArrangerObject *) mn;
1001 
1002       midi_events_add_note_on (
1003         events, 1, mn->val, mn->vel->vel,
1004         (midi_time_t)
1005         (position_to_ticks (&mn_obj->pos) +
1006           region_start),
1007         0);
1008       midi_events_add_note_off (
1009         events, 1, mn->val,
1010         (midi_time_t)
1011         (position_to_ticks (&mn_obj->end_pos) +
1012           region_start),
1013         0);
1014     }
1015 
1016   midi_events_sort_by_time (
1017     events);
1018 
1019   return events;
1020 }
1021 
1022 /**
1023  * Sends all MIDI notes off at the given local
1024  * point.
1025  */
1026 static inline void
send_notes_off_at(ZRegion * self,MidiEvents * midi_events,midi_time_t time)1027 send_notes_off_at (
1028   ZRegion *          self,
1029   MidiEvents *       midi_events,
1030   midi_time_t        time)
1031 {
1032   /*g_debug ("sending notes off at %u", time);*/
1033 
1034   midi_byte_t channel = 1;
1035   if (self->id.type == REGION_TYPE_MIDI)
1036     {
1037       channel = midi_region_get_midi_ch (self);
1038     }
1039   else if (self->id.type == REGION_TYPE_CHORD)
1040     {
1041       /* FIXME set channel */
1042     }
1043 
1044   midi_events_add_all_notes_off (
1045     midi_events, channel, time, F_QUEUED);
1046 
1047 }
1048 
1049 /**
1050  * Fills MIDI event queue from the region.
1051  *
1052  * The events are dequeued right after the call to
1053  * this function.
1054  *
1055  * @note The caller already splits calls to this
1056  *   function at each sub-loop inside the region,
1057  *   so region loop related logic is not needed.
1058  *
1059  * @param g_start_frames Global start frame.
1060  * @param local_start_frame The start frame offset
1061  *   from 0 in this cycle.
1062  * @param nframes Number of frames at start
1063  *   Position.
1064  * @param note_off_at_end Whether a note off should
1065  *   be added at the end frame (eg, when the caller
1066  *   knows there is a region loop or the region
1067  *   ends).
1068  * @param midi_events MidiEvents to fill (from
1069  *   Piano Roll Port for example).
1070  */
1071 REALTIME
1072 void
midi_region_fill_midi_events(ZRegion * self,long g_start_frames,nframes_t local_start_frame,nframes_t nframes,bool note_off_at_end,MidiEvents * midi_events)1073 midi_region_fill_midi_events (
1074   ZRegion *    self,
1075   long         g_start_frames,
1076   nframes_t    local_start_frame,
1077   nframes_t    nframes,
1078   bool         note_off_at_end,
1079   MidiEvents * midi_events)
1080 {
1081   ArrangerObject * r_obj =
1082     (ArrangerObject *) self;
1083   Track * track = arranger_object_get_track (r_obj);
1084   g_return_if_fail (IS_TRACK_AND_NONNULL (track));
1085 
1086   /* send all MIDI notes off if needed */
1087   if (note_off_at_end)
1088     {
1089       send_notes_off_at (
1090         self, midi_events,
1091         (midi_time_t)
1092           /* -1 to send event 1 sample
1093            * before the end point */
1094           ((local_start_frame + nframes) - 1));
1095     }
1096 
1097   const long r_local_pos =
1098     region_timeline_frames_to_local (
1099       self, g_start_frames, F_NORMALIZE);
1100 
1101 #if 0
1102   if (g_start_frames == 0)
1103     {
1104       g_debug (
1105         "%s: fill midi events - g start %ld - "
1106         "local start %"PRIu32" - nframes %"PRIu32" - "
1107         "notes off at end %u - "
1108         "r local pos %ld",
1109         __func__, g_start_frames,
1110         local_start_frame, nframes,
1111         note_off_at_end, r_local_pos);
1112     }
1113 #endif
1114 
1115   /* go through each note */
1116   int num_objs =
1117     track->type == TRACK_TYPE_CHORD ?
1118       self->num_chord_objects :
1119       self->num_midi_notes;
1120   for (int i = 0; i < num_objs; i++)
1121     {
1122       ArrangerObject * mn_obj = NULL;
1123       MidiNote * mn = NULL;
1124       ChordObject * co = NULL;
1125       ChordDescriptor * descr = NULL;
1126       if (track->type == TRACK_TYPE_CHORD)
1127         {
1128           co = self->chord_objects[i];
1129           descr =
1130             chord_object_get_chord_descriptor (co);
1131           mn_obj = (ArrangerObject *) co;
1132         }
1133       else
1134         {
1135           mn = self->midi_notes[i];
1136           mn_obj = (ArrangerObject *) mn;
1137         }
1138       if (arranger_object_get_muted (mn_obj))
1139         {
1140           continue;
1141         }
1142 
1143       /* if object starts inside the current
1144        * range */
1145       if (mn_obj->pos.frames >= 0 &&
1146           mn_obj->pos.frames >= r_local_pos &&
1147           mn_obj->pos.frames <
1148             r_local_pos + (long) nframes)
1149         {
1150           midi_time_t _time =
1151             (midi_time_t)
1152             (local_start_frame +
1153               (mn_obj->pos.frames - r_local_pos));
1154           /*g_message ("normal note on at %u", time);*/
1155 
1156           if (mn)
1157             {
1158               midi_events_add_note_on (
1159                 midi_events,
1160                 midi_region_get_midi_ch (self),
1161                 mn->val, mn->vel->vel,
1162                 _time, F_QUEUED);
1163             }
1164           else if (co)
1165             {
1166               midi_events_add_note_ons_from_chord_descr (
1167                 midi_events, descr, 1,
1168                 VELOCITY_DEFAULT, _time,
1169                 F_QUEUED);
1170             }
1171         }
1172 
1173       long mn_obj_end_frames =
1174         (track->type == TRACK_TYPE_CHORD ?
1175           math_round_double_to_type (
1176             mn_obj->pos.frames +
1177               TRANSPORT->ticks_per_beat *
1178               AUDIO_ENGINE->frames_per_tick,
1179               long) :
1180           mn_obj->end_pos.frames);
1181 
1182       /* if note ends within the cycle */
1183       if (mn_obj_end_frames >= r_local_pos &&
1184           (mn_obj_end_frames <=
1185             (r_local_pos + nframes)))
1186         {
1187           midi_time_t _time =
1188             (midi_time_t)
1189             (local_start_frame +
1190               (mn_obj_end_frames - r_local_pos));
1191 
1192           /* note actually ends 1 frame before
1193            * the end point, not at the end
1194            * point */
1195           if (_time > 0)
1196             {
1197               _time--;
1198             }
1199 
1200 #if 0
1201           if (g_start_frames == 0)
1202             {
1203               g_debug (
1204                 "note ends within cycle (end "
1205                 "frames %ld - note off time: %u",
1206                 mn_obj_end_frames, _time);
1207             }
1208 #endif
1209 
1210           if (mn)
1211             {
1212               midi_events_add_note_off (
1213                 midi_events,
1214                 midi_region_get_midi_ch (self),
1215                 mn->val, _time, F_QUEUED);
1216             }
1217           else if (co)
1218             {
1219               for (int l = 0;
1220                    l < CHORD_DESCRIPTOR_MAX_NOTES;
1221                    l++)
1222                 {
1223                   if (descr->notes[l])
1224                     {
1225                       midi_events_add_note_off (
1226                         midi_events, 1, l + 36,
1227                         _time, F_QUEUED);
1228                     }
1229                 }
1230             }
1231         }
1232     } /* foreach midi note */
1233 }
1234 
1235 /**
1236  * Fills in the array with all the velocities in
1237  * the project that are within or outside the
1238  * range given.
1239  *
1240  * @param inside Whether to find velocities inside
1241  *   the range (1) or outside (0).
1242  */
1243 void
midi_region_get_velocities_in_range(const ZRegion * self,const Position * start_pos,const Position * end_pos,Velocity *** velocities,int * num_velocities,size_t * velocities_size,int inside)1244 midi_region_get_velocities_in_range (
1245   const ZRegion *  self,
1246   const Position * start_pos,
1247   const Position * end_pos,
1248   Velocity ***     velocities,
1249   int *            num_velocities,
1250   size_t *         velocities_size,
1251   int              inside)
1252 {
1253   Position global_start_pos;
1254   for (int k = 0; k < self->num_midi_notes; k++)
1255     {
1256       MidiNote * mn = self->midi_notes[k];
1257       midi_note_get_global_start_pos (
1258         mn, &global_start_pos);
1259 
1260 #define ADD_VELOCITY \
1261   array_double_size_if_full ( \
1262     *velocities, *num_velocities, \
1263     *velocities_size, Velocity *); \
1264     (*velocities)[(* num_velocities)++] = \
1265       mn->vel
1266 
1267       if (inside &&
1268           position_is_after_or_equal (
1269             &global_start_pos, start_pos) &&
1270           position_is_before_or_equal (
1271             &global_start_pos, end_pos))
1272         {
1273           ADD_VELOCITY;
1274         }
1275       else if (!inside &&
1276           (position_is_before (
1277             &global_start_pos, start_pos) ||
1278           position_is_after (
1279             &global_start_pos, end_pos)))
1280         {
1281           ADD_VELOCITY;
1282         }
1283 #undef ADD_VELOCITY
1284     }
1285 }
1286 
1287 /**
1288  * Frees members only but not the MidiRegion
1289  * itself.
1290  *
1291  * Regions should be free'd using region_free.
1292  */
1293 void
midi_region_free_members(ZRegion * self)1294 midi_region_free_members (ZRegion * self)
1295 {
1296   g_return_if_fail (IS_REGION (self));
1297 
1298   for (int i = 0; i < self->num_midi_notes; i++)
1299     {
1300       arranger_object_free (
1301         (ArrangerObject *) self->midi_notes[i]);
1302     }
1303 }
1304