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