Audacity  3.2.0
NoteTrack.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  NoteTrack.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//*******************************************************************/
15 
16 
17 
18 #include "NoteTrack.h"
19 
20 
21 
22 #include <wx/wxcrtvararg.h>
23 #include <wx/dc.h>
24 #include <wx/brush.h>
25 #include <wx/pen.h>
26 #include <wx/intl.h>
27 
28 #if defined(USE_MIDI)
29 #include "../lib-src/header-substitutes/allegro.h"
30 
31 #include <sstream>
32 
33 #define ROUND(x) ((int) ((x) + 0.5))
34 
35 #include "AColor.h"
36 #include "Prefs.h"
37 #include "Project.h"
39 
40 #include "InconsistencyException.h"
41 
42 #include "TimeWarper.h"
43 
44 #include "AllThemeResources.h"
45 #include "Theme.h"
46 
47 #ifdef SONIFY
48 #include <portmidi.h>
49 
50 #define SON_PROGRAM 0
51 #define SON_AutoSave 67
52 #define SON_ModifyState 60
53 #define SON_NoteBackground 72
54 #define SON_NoteForeground 74
55 #define SON_Measures 76 /* "bar line" */
56 #define SON_Serialize 77
57 #define SON_Unserialize 79
58 #define SON_VEL 100
59 
60 
61 PmStream *sonMidiStream;
62 bool sonificationStarted = false;
63 
65 {
66  PmError err = Pm_OpenOutput(&sonMidiStream, Pm_GetDefaultOutputDeviceID(),
67  NULL, 0, NULL, NULL, 0);
68  if (err) sonMidiStream = NULL;
69  if (sonMidiStream)
70  Pm_WriteShort(sonMidiStream, 0, Pm_Message(0xC0, SON_PROGRAM, 0));
71  sonificationStarted = true;
72 }
73 
74 
76 {
77  if (sonMidiStream) Pm_Close(sonMidiStream);
78  sonificationStarted = false;
79 }
80 
81 
82 
83 
84 void SonifyNoteOnOff(int p, int v)
85 {
86  if (!sonificationStarted)
88  if (sonMidiStream)
89  Pm_WriteShort(sonMidiStream, 0, Pm_Message(0x90, p, v));
90 }
91 
92 #define SONFNS(name) \
93  void SonifyBegin ## name() { SonifyNoteOnOff(SON_ ## name, SON_VEL); } \
94  void SonifyEnd ## name() { SonifyNoteOnOff(SON_ ## name, 0); }
95 
96 SONFNS(NoteBackground)
97 SONFNS(NoteForeground)
98 SONFNS(Measures)
99 SONFNS(Serialize)
100 SONFNS(Unserialize)
101 SONFNS(ModifyState)
102 SONFNS(AutoSave)
103 
104 #undef SONFNS
105 
106 #endif
107 
108 
109 
111  "notetrack",
113 };
114 
116 {
117  auto &tracks = TrackList::Get( project );
118  auto result = tracks.Add( std::make_shared<NoteTrack>());
119  result->AttachedTrackObjects::BuildAll();
120  return result;
121 }
122 
124  : NoteTrackBase()
125 {
126  SetDefaultName(_("Note Track"));
128 
129  mSeq = NULL;
131 
132 #ifdef EXPERIMENTAL_MIDI_OUT
133  mVelocity = 0;
134 #endif
136  mTopNote = MaxPitch;
137 
139 }
140 
142 {
143 }
144 
145 Alg_seq &NoteTrack::GetSeq() const
146 {
147  if (!mSeq) {
149  mSeq = std::make_unique<Alg_seq>();
150  else {
151  std::unique_ptr<Alg_track> alg_track
152  { Alg_seq::unserialize
154  wxASSERT(alg_track->get_type() == 's');
155  mSeq.reset( static_cast<Alg_seq*>(alg_track.release()) );
156 
157  // Preserve the invariant that at most one of the representations is
158  // valid
159  mSerializationBuffer.reset();
161  }
162  }
163  wxASSERT(mSeq);
164  return *mSeq;
165 }
166 
168 {
169  auto duplicate = std::make_shared<NoteTrack>();
170  duplicate->Init(*this);
171  // The duplicate begins life in serialized state. Often the duplicate is
172  // pushed on the Undo stack. Then we want to un-serialize it (or a further
173  // copy) only on demand after an Undo.
174  if (mSeq) {
176  wxASSERT(!mSerializationBuffer);
177  // serialize from this to duplicate's mSerializationBuffer
178  void *buffer;
179  mSeq->serialize(&buffer,
180  &duplicate->mSerializationLength);
181  duplicate->mSerializationBuffer.reset( (char*)buffer );
183  }
184  else if (mSerializationBuffer) {
185  // Copy already serialized data.
186  wxASSERT(!mSeq);
187  duplicate->mSerializationLength = this->mSerializationLength;
188  duplicate->mSerializationBuffer.reset
189  ( safenew char[ this->mSerializationLength ] );
190  memcpy( duplicate->mSerializationBuffer.get(),
191  this->mSerializationBuffer.get(), this->mSerializationLength );
192  }
193  else {
194  // We are duplicating a default-constructed NoteTrack, and that's okay
195  }
196  // copy some other fields here
197  duplicate->SetBottomNote(mBottomNote);
198  duplicate->SetTopNote(mTopNote);
199  duplicate->mVisibleChannels = mVisibleChannels;
200  duplicate->SetOffset(GetOffset());
201 #ifdef EXPERIMENTAL_MIDI_OUT
202  duplicate->SetVelocity(GetVelocity());
203 #endif
204  return duplicate;
205 }
206 
207 
208 double NoteTrack::GetOffset() const
209 {
210  return mOffset;
211 }
212 
214 {
215  return GetOffset();
216 }
217 
218 double NoteTrack::GetEndTime() const
219 {
220  return GetStartTime() + GetSeq().get_real_dur();
221 }
222 
223 void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
224  const TimeWarper &warper,
225  double semitones)
226 {
227  double offset = this->GetOffset(); // track is shifted this amount
228  auto &seq = GetSeq();
229  seq.convert_to_seconds(); // make sure time units are right
230  t1 -= offset; // adjust time range to compensate for track offset
231  t0 -= offset;
232  if (t1 > seq.get_dur()) { // make sure t0, t1 are within sequence
233  t1 = seq.get_dur();
234  if (t0 >= t1) return;
235  }
236  Alg_iterator iter(mSeq.get(), false);
237  iter.begin();
238  Alg_event_ptr event;
239  while (0 != (event = iter.next()) && event->time < t1) {
240  if (event->is_note() && event->time >= t0) {
241  event->set_pitch(event->get_pitch() + semitones);
242  }
243  }
244  iter.end();
245  // now, use warper to warp the tempo map
246  seq.convert_to_beats(); // beats remain the same
247  Alg_time_map_ptr map = seq.get_time_map();
248  map->insert_beat(t0, map->time_to_beat(t0));
249  map->insert_beat(t1, map->time_to_beat(t1));
250  int i, len = map->length();
251  for (i = 0; i < len; i++) {
252  Alg_beat &beat = map->beats[i];
253  beat.time = warper.Warp(beat.time + offset) - offset;
254  }
255  // about to redisplay, so might as well convert back to time now
256  seq.convert_to_seconds();
257 }
258 
259 // Draws the midi channel toggle buttons within the given rect.
260 // The rect should be evenly divisible by 4 on both axis.
262 ( const NoteTrack *pTrack, wxDC & dc, const wxRect &rect, int highlightedChannel )
263 {
264  dc.SetTextForeground(theTheme.Colour(clrLabelTrackText));
265  wxASSERT_MSG(rect.width % 4 == 0, "Midi channel control rect width must be divisible by 4");
266  wxASSERT_MSG(rect.height % 4 == 0, "Midi channel control rect height must be divisible by 4");
267 
268  auto cellWidth = rect.width / 4;
269  auto cellHeight = rect.height / 4;
270 
271  wxRect box;
272  for (int row = 0; row < 4; row++) {
273  for (int col = 0; col < 4; col++) {
274  // chanName is the "external" channel number (1-16)
275  // used by AColor and button labels
276  int chanName = row * 4 + col + 1;
277 
278  box.x = rect.x + col * cellWidth;
279  box.y = rect.y + row * cellHeight;
280  box.width = cellWidth;
281  box.height = cellHeight;
282 
283  bool visible = pTrack ? pTrack->IsVisibleChan(chanName - 1) : true;
284  if (visible) {
285  // highlightedChannel counts 0 based
286  if ( chanName == highlightedChannel + 1 )
287  AColor::LightMIDIChannel(&dc, chanName);
288  else
289  AColor::MIDIChannel(&dc, chanName);
290  dc.DrawRectangle(box);
291 // two choices: channel is enabled (to see and play) when button is in
292 // "up" position (original Audacity style) or in "down" position
293 //
294 #define CHANNEL_ON_IS_DOWN 1
295 #if CHANNEL_ON_IS_DOWN
296  AColor::DarkMIDIChannel(&dc, chanName);
297 #else
298  AColor::LightMIDIChannel(&dc, chanName);
299 #endif
300  AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
301  AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
302 
303 #if CHANNEL_ON_IS_DOWN
304  AColor::LightMIDIChannel(&dc, chanName);
305 #else
306  AColor::DarkMIDIChannel(&dc, chanName);
307 #endif
308  AColor::Line(dc,
309  box.x + box.width - 1, box.y,
310  box.x + box.width - 1, box.y + box.height - 1);
311  AColor::Line(dc,
312  box.x, box.y + box.height - 1,
313  box.x + box.width - 1, box.y + box.height - 1);
314  } else {
315  if ( chanName == highlightedChannel + 1 )
316  AColor::LightMIDIChannel(&dc, chanName);
317  else
318  AColor::MIDIChannel(&dc, 0);
319  dc.DrawRectangle(box);
320 #if CHANNEL_ON_IS_DOWN
321  AColor::LightMIDIChannel(&dc, 0);
322 #else
323  AColor::DarkMIDIChannel(&dc, 0);
324 #endif
325  AColor::Line(dc, box.x, box.y, box.x + box.width - 1, box.y);
326  AColor::Line(dc, box.x, box.y, box.x, box.y + box.height - 1);
327 
328 #if CHANNEL_ON_IS_DOWN
329  AColor::DarkMIDIChannel(&dc, 0);
330 #else
331  AColor::LightMIDIChannel(&dc, 0);
332 #endif
333  AColor::Line(dc,
334  box.x + box.width - 1, box.y,
335  box.x + box.width - 1, box.y + box.height - 1);
336  AColor::Line(dc,
337  box.x, box.y + box.height - 1,
338  box.x + box.width - 1, box.y + box.height - 1);
339 
340  }
341 
342  wxString text;
343  wxCoord w;
344  wxCoord h;
345 
346  text.Printf(wxT("%d"), chanName);
347  dc.GetTextExtent(text, &w, &h);
348 
349  dc.DrawText(text, box.x + (box.width - w) / 2, box.y + (box.height - h) / 2);
350  }
351  }
352  dc.SetTextForeground(theTheme.Colour(clrTrackPanelText));
353  AColor::MIDIChannel(&dc, 0); // always return with gray color selected
354 }
355 
356 int NoteTrack::FindChannel(const wxRect &rect, int mx, int my)
357 {
358  wxASSERT_MSG(rect.width % 4 == 0, "Midi channel control rect width must be divisible by 4");
359  wxASSERT_MSG(rect.height % 4 == 0, "Midi channel control rect height must be divisible by 4");
360 
361  auto cellWidth = rect.width / 4;
362  auto cellHeight = rect.height / 4;
363 
364  int col = (mx - rect.x) / cellWidth;
365  int row = (my - rect.y) / cellHeight;
366 
367  return row * 4 + col;
368 }
369 
370 
371 // Handles clicking within the midi controls rect (same as DrawLabelControls).
372 // This is somewhat oddly written, as these aren't real buttons - they act
373 // when the mouse goes down; you can't hold it pressed and move off of it.
374 // Left-clicking toggles a single channel; right-clicking turns off all other channels.
375 bool NoteTrack::LabelClick(const wxRect &rect, int mx, int my, bool right)
376 {
377  auto channel = FindChannel(rect, mx, my);
378  if (right)
379  SoloVisibleChan(channel);
380  else
381  ToggleVisibleChan(channel);
382 
383  return true;
384 }
385 
386 void NoteTrack::SetSequence(std::unique_ptr<Alg_seq> &&seq)
387 {
388  mSeq = std::move(seq);
389 }
390 
392 {
393  FILE *debugOutput;
394 
395  debugOutput = fopen("debugOutput.txt", "wt");
396  wxFprintf(debugOutput, "Importing MIDI...\n");
397 
398  // This is called for debugging purposes. Do not compute mSeq on demand
399  // with GetSeq()
400  if (mSeq) {
401  int i = 0;
402 
403  while(i < mSeq->length()) {
404  wxFprintf(debugOutput, "--\n");
405  wxFprintf(debugOutput, "type: %c\n",
406  ((Alg_event_ptr)mSeq->track_list.tracks[i])->get_type());
407  wxFprintf(debugOutput, "time: %f\n",
408  ((Alg_event_ptr)mSeq->track_list.tracks[i])->time);
409  wxFprintf(debugOutput, "channel: %li\n",
410  ((Alg_event_ptr)mSeq->track_list.tracks[i])->chan);
411 
412  if(((Alg_event_ptr)mSeq->track_list.tracks[i])->get_type() == wxT('n'))
413  {
414  wxFprintf(debugOutput, "pitch: %f\n",
415  ((Alg_note_ptr)mSeq->track_list.tracks[i])->pitch);
416  wxFprintf(debugOutput, "duration: %f\n",
417  ((Alg_note_ptr)mSeq->track_list.tracks[i])->dur);
418  wxFprintf(debugOutput, "velocity: %f\n",
419  ((Alg_note_ptr)mSeq->track_list.tracks[i])->loud);
420  }
421  else if(((Alg_event_ptr)mSeq->track_list.tracks[i])->get_type() == wxT('n'))
422  {
423  wxFprintf(debugOutput, "key: %li\n", ((Alg_update_ptr)mSeq->track_list.tracks[i])->get_identifier());
424  wxFprintf(debugOutput, "attribute type: %c\n", ((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.attr_type());
425  wxFprintf(debugOutput, "attribute: %s\n", ((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.attr_name());
426 
427  if(((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.attr_type() == wxT('r'))
428  {
429  wxFprintf(debugOutput, "value: %f\n", ((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.r);
430  }
431  else if(((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.attr_type() == wxT('i')) {
432  wxFprintf(debugOutput, "value: %li\n", ((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.i);
433  }
434  else if(((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.attr_type() == wxT('s')) {
435  wxFprintf(debugOutput, "value: %s\n", ((Alg_update_ptr)mSeq->track_list.tracks[i])->parameter.s);
436  }
437  else {}
438  }
439 
440  i++;
441  }
442  }
443  else {
444  wxFprintf(debugOutput, "No sequence defined!\n");
445  }
446 
447  fclose(debugOutput);
448 }
449 
450 Track::Holder NoteTrack::Cut(double t0, double t1)
451 {
452  if (t1 < t0)
454 
455  double len = t1-t0;
456  //auto delta = -(
457  //( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
458  //);
459 
460  auto newTrack = std::make_shared<NoteTrack>();
461 
462  newTrack->Init(*this);
463 
464  auto &seq = GetSeq();
465  seq.convert_to_seconds();
466  newTrack->mSeq.reset(seq.cut(t0 - GetOffset(), len, false));
467  newTrack->SetOffset(0);
468 
469  // Not needed
470  // Alg_seq::cut seems to handle this
471  //AddToDuration( delta );
472 
473  // What should be done with the rest of newTrack's members?
474  //(mBottomNote,
475  // mSerializationBuffer, mSerializationLength, mVisibleChannels)
476 
477  return newTrack;
478 }
479 
480 Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
481 {
482  if (t1 < t0)
484 
485  double len = t1-t0;
486 
487  auto newTrack = std::make_shared<NoteTrack>();
488 
489  newTrack->Init(*this);
490 
491  auto &seq = GetSeq();
492  seq.convert_to_seconds();
493  newTrack->mSeq.reset(seq.copy(t0 - GetOffset(), len, false));
494  newTrack->SetOffset(0);
495 
496  // What should be done with the rest of newTrack's members?
497  // (mBottomNote, mSerializationBuffer,
498  // mSerializationLength, mVisibleChannels)
499 
500  return newTrack;
501 }
502 
503 bool NoteTrack::Trim(double t0, double t1)
504 {
505  if (t1 < t0)
506  return false;
507  auto &seq = GetSeq();
508  //auto delta = -(
509  //( GetEndTime() - std::min( GetEndTime(), t1 ) ) +
510  //( std::max(t0, GetStartTime()) - GetStartTime() )
511  //);
512  seq.convert_to_seconds();
513  // DELETE way beyond duration just in case something is out there:
514  seq.clear(t1 - GetOffset(), seq.get_dur() + 10000.0, false);
515  // Now that stuff beyond selection is cleared, clear before selection:
516  seq.clear(0.0, t0 - GetOffset(), false);
517  // want starting time to be t0
518  SetOffset(t0);
519 
520  // Not needed
521  // Alg_seq::clear seems to handle this
522  //AddToDuration( delta );
523 
524  return true;
525 }
526 
527 void NoteTrack::Clear(double t0, double t1)
528 {
529  if (t1 < t0)
531 
532  double len = t1-t0;
533 
534  auto &seq = GetSeq();
535 
536  auto offset = GetOffset();
537  auto start = t0 - offset;
538  if (start < 0.0) {
539  // AlgSeq::clear will shift the cleared interval, not changing len, if
540  // start is negative. That's not what we want to happen.
541  if (len > -start) {
542  seq.clear(0, len + start, false);
543  SetOffset(t0);
544  }
545  else
546  SetOffset(offset - len);
547  }
548  else {
549  //auto delta = -(
550  //( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
551  //);
552  seq.clear(start, len, false);
553 
554  // Not needed
555  // Alg_seq::clear seems to handle this
556  // AddToDuration( delta );
557  }
558 }
559 
560 void NoteTrack::Paste(double t, const Track *src)
561 {
562  // Paste inserts src at time t. If src has a positive offset,
563  // the offset is treated as silence which is also inserted. If
564  // the offset is negative, the offset is ignored and the ENTIRE
565  // src is inserted (otherwise, we would either lose data from
566  // src by not inserting things at negative times, or inserting
567  // things at negative times could overlap things already in
568  // the destination track).
569 
570  //Check that src is a non-NULL NoteTrack
571  bool bOk = src && src->TypeSwitch< bool >( [&](const NoteTrack *other) {
572 
573  auto myOffset = this->GetOffset();
574  if (t < myOffset) {
575  // workaround strange behavior described at
576  // http://bugzilla.audacityteam.org/show_bug.cgi?id=1735#c3
577  SetOffset(t);
578  InsertSilence(t, myOffset - t);
579  }
580 
581  double delta = 0.0;
582  auto &seq = GetSeq();
583  auto offset = other->GetOffset();
584  if ( offset > 0 ) {
585  seq.convert_to_seconds();
586  seq.insert_silence( t - GetOffset(), offset );
587  t += offset;
588  // Is this needed or does Alg_seq::insert_silence take care of it?
589  //delta += offset;
590  }
591 
592  // This seems to be needed:
593  delta += std::max( 0.0, t - GetEndTime() );
594 
595  // This, not:
596  //delta += other->GetSeq().get_real_dur();
597 
598  seq.paste(t - GetOffset(), &other->GetSeq());
599 
600  AddToDuration( delta );
601 
602  return true;
603  });
604 
605  if ( !bOk )
606  // THROW_INCONSISTENCY_EXCEPTION; // ?
607  (void)0;// intentionally do nothing
608 }
609 
610 void NoteTrack::Silence(double t0, double t1)
611 {
612  if (t1 < t0)
614 
615  auto len = t1 - t0;
616 
617  auto &seq = GetSeq();
618  seq.convert_to_seconds();
619  // XXX: do we want to set the all param?
620  // If it's set, then it seems like notes are silenced if they start or end in the range,
621  // otherwise only if they start in the range. --Poke
622  seq.silence(t0 - GetOffset(), len, false);
623 }
624 
625 void NoteTrack::InsertSilence(double t, double len)
626 {
627  if (len < 0)
629 
630  auto &seq = GetSeq();
631  seq.convert_to_seconds();
632  seq.insert_silence(t - GetOffset(), len);
633 
634  // is this needed?
635  // AddToDuration( len );
636 }
637 
638 #ifdef EXPERIMENTAL_MIDI_OUT
639 void NoteTrack::SetVelocity(float velocity)
640 {
641  if (mVelocity != velocity) {
642  mVelocity = velocity;
643  Notify();
644  }
645 }
646 #endif
647 
648 // Call this function to manipulate the underlying sequence data. This is
649 // NOT the function that handles horizontal dragging.
650 bool NoteTrack::Shift(double t) // t is always seconds
651 {
652  if (t > 0) {
653  auto &seq = GetSeq();
654  // insert an even number of measures
655  seq.convert_to_beats();
656  // get initial tempo
657  double tempo = seq.get_tempo(0.0);
658  double beats_per_measure = seq.get_bar_len(0.0);
659  int m = ROUND(t * tempo / beats_per_measure);
660  // need at least 1 measure, so if we rounded down to zero, fix it
661  if (m == 0) m = 1;
662  // compute NEW tempo so that m measures at NEW tempo take t seconds
663  tempo = beats_per_measure * m / t; // in beats per second
664  seq.insert_silence(0.0, beats_per_measure * m);
665  seq.set_tempo(tempo * 60.0 /* bpm */, 0.0, beats_per_measure * m);
666  seq.write("afterShift.gro");
667  } else if (t < 0) {
668  auto &seq = GetSeq();
669  seq.convert_to_seconds();
670  seq.clear(0, t, true);
671  } else { // offset is zero, no modifications
672  return false;
673  }
674  return true;
675 }
676 
678 {
679  // Alg_seq knows nothing about offset, so remove offset time
680  double seq_time = time - GetOffset();
681  double beat;
682  auto &seq = GetSeq();
683  seq_time = seq.nearest_beat_time(seq_time, &beat);
684  // add the offset back in to get "actual" audacity track time
685  return { seq_time + GetOffset(), beat };
686 }
687 
688 static const Track::TypeInfo &typeInfo()
689 {
690  static const Track::TypeInfo info{
691  { "note", "midi", XO("Note Track") }, true,
693  return info;
694 }
695 
696 auto NoteTrack::GetTypeInfo() const -> const TypeInfo &
697 {
698  return typeInfo();
699 }
700 
702 {
703  return typeInfo();
704 }
705 
707 {
708  auto pNewTrack = std::make_shared<NoteTrack>();
709  pNewTrack->Paste(0.0, this);
710  return pNewTrack;
711 }
712 
714 {
715  ConstIntervals results;
716  results.emplace_back( GetStartTime(), GetEndTime() );
717  return results;
718 }
719 
721 {
722  Intervals results;
723  results.emplace_back( GetStartTime(), GetEndTime() );
724  return results;
725 }
726 
727 void NoteTrack::AddToDuration( double delta )
728 {
729  auto &seq = GetSeq();
730 #if 0
731  // PRL: Would this be better ?
732  seq.set_real_dur( seq.get_real_dur() + delta );
733 #else
734  seq.convert_to_seconds();
735  seq.set_dur( seq.get_dur() + delta );
736 #endif
737 }
738 
740  ( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur )
741 {
742  auto &seq = GetSeq();
743  bool result = seq.stretch_region( t0.second, t1.second, newDur );
744  if (result) {
745  const auto oldDur = t1.first - t0.first;
746  AddToDuration( newDur - oldDur );
747  }
748  return result;
749 }
750 
751 namespace
752 {
753  void swap(std::unique_ptr<Alg_seq> &a, std::unique_ptr<Alg_seq> &b)
754  {
755  std::unique_ptr<Alg_seq> tmp = std::move(a);
756  a = std::move(b);
757  b = std::move(tmp);
758  }
759 }
760 
761 Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
762 {
763  cleanup.reset();
764  double offset = GetOffset();
765  if (offset == 0)
766  return &GetSeq();
767  // make a copy, deleting events that are shifted before time 0
768  double start = -offset;
769  if (start < 0) start = 0;
770  // notes that begin before "start" are not included even if they
771  // extend past "start" (because "all" parameter is set to false)
772  cleanup.reset( GetSeq().copy(start, GetSeq().get_dur() - start, false) );
773  auto seq = cleanup.get();
774  if (offset > 0) {
775  {
776  // swap cleanup and mSeq so that Shift operates on the NEW copy
777  swap( this->mSeq, cleanup );
778  auto cleanup2 = finally( [&] { swap( this->mSeq, cleanup ); } );
779 
780  const_cast< NoteTrack *>( this )->Shift(offset);
781  }
782 #ifdef OLD_CODE
783  // now shift events by offset. This must be done with an integer
784  // number of measures, so first, find the beats-per-measure
785  double beats_per_measure = 4.0;
786  Alg_time_sig_ptr tsp = NULL;
787  if (seq->time_sig.length() > 0 && seq->time_sig[0].beat < ALG_EPS) {
788  // there is an initial time signature
789  tsp = &(seq->time_sig[0]);
790  beats_per_measure = (tsp->num * 4) / tsp->den;
791  }
792  // also need the initial tempo
793  double bps = ALG_DEFAULT_BPM / 60;
794  Alg_time_map_ptr map = seq->get_time_map();
795  Alg_beat_ptr bp = &(map->beats[0]);
796  if (bp->time < ALG_EPS) { // tempo change at time 0
797  if (map->beats.len > 1) { // compute slope to get tempo
798  bps = (map->beats[1].beat - map->beats[0].beat) /
799  (map->beats[1].time - map->beats[0].time);
800  } else if (seq->get_time_map()->last_tempo_flag) {
801  bps = seq->get_time_map()->last_tempo;
802  }
803  }
804  // find closest number of measures to fit in the gap
805  // number of measures is offset / measure_time
806  double measure_time = beats_per_measure / bps; // seconds per measure
807  int n = ROUND(offset / measure_time);
808  if (n == 0) n = 1;
809  // we will insert n measures. Compute the desired duration of each.
810  measure_time = offset / n;
811  bps = beats_per_measure / measure_time;
812  // insert integer multiple of measures at beginning
813  seq->convert_to_beats();
814  seq->insert_silence(0, beats_per_measure * n);
815  // make sure time signature at 0 is correct
816  if (tsp) {
817  seq->set_time_sig(0, tsp->num, tsp->den);
818  }
819  // adjust tempo to match offset
820  seq->set_tempo(bps * 60.0, 0, beats_per_measure * n);
821 #endif
822  } else {
823  auto &mySeq = GetSeq();
824  // if offset is negative, it might not be a multiple of beats, but
825  // we want to preserve the relative positions of measures. I.e. we
826  // should shift barlines and time signatures as well as notes.
827  // Insert a time signature at the first bar-line if necessary.
828 
829  // Translate start from seconds to beats and call it beat:
830  double beat = mySeq.get_time_map()->time_to_beat(start);
831  // Find the time signature in mySeq in effect at start (beat):
832  int i = mySeq.time_sig.find_beat(beat);
833  // i is where you would insert a NEW time sig at beat,
834  // Case 1: beat coincides with a time sig at i. Time signature
835  // at beat means that there is a barline at beat, so when beat
836  // is shifted to 0, the relative barline positions are preserved
837  if (mySeq.time_sig.length() > 0 &&
838  within(beat, mySeq.time_sig[i].beat, ALG_EPS)) {
839  // beat coincides with time signature change, so offset must
840  // be a multiple of beats
841  /* do nothing */ ;
842  // Case 2: there is no time signature before beat.
843  } else if (i == 0 && (mySeq.time_sig.length() == 0 ||
844  mySeq.time_sig[i].beat > beat)) {
845  // If beat does not fall on an implied barline, we need to
846  // insert a time signature.
847  double measures = beat / 4.0;
848  double imeasures = ROUND(measures);
849  if (!within(measures, imeasures, ALG_EPS)) {
850  double bar_offset = ((int)(measures) + 1) * 4.0 - beat;
851  seq->set_time_sig(bar_offset, 4, 4);
852  }
853  // This case should never be true because if i == 0, either there
854  // are no time signatures before beat (Case 2),
855  // or there is one time signature at beat (Case 1)
856  } else if (i == 0) {
857  /* do nothing (might be good to assert(false)) */ ;
858  // Case 3: i-1 must be the effective time sig position
859  } else {
860  i -= 1; // index the time signature in effect at beat
861  Alg_time_sig_ptr tsp = &(mySeq.time_sig[i]);
862  double beats_per_measure = (tsp->num * 4) / tsp->den;
863  double measures = (beat - tsp->beat) / beats_per_measure;
864  int imeasures = ROUND(measures);
865  if (!within(measures, imeasures, ALG_EPS)) {
866  // beat is not on a measure, so we need to insert a time sig
867  // to force a bar line at the first measure location after
868  // beat
869  double bar = tsp->beat + beats_per_measure * ((int)(measures) + 1);
870  double bar_offset = bar - beat;
871  // insert NEW time signature at bar_offset in NEW sequence
872  // It will have the same time signature, but the position will
873  // force a barline to match the barlines in mSeq
874  seq->set_time_sig(bar_offset, tsp->num, tsp->den);
875  }
876  // else beat coincides with a barline, so no need for an extra
877  // time signature to force barline alignment
878  }
879  }
880  return seq;
881 }
882 
883 
884 bool NoteTrack::ExportMIDI(const wxString &f) const
885 {
886  std::unique_ptr<Alg_seq> cleanup;
887  auto seq = MakeExportableSeq(cleanup);
888  bool rslt = seq->smf_write(f.mb_str());
889  return rslt;
890 }
891 
892 bool NoteTrack::ExportAllegro(const wxString &f) const
893 {
894  double offset = GetOffset();
896  auto &seq = GetSeq();
897  if (in_seconds) {
898  seq.convert_to_seconds();
899  } else {
900  seq.convert_to_beats();
901  }
902  return seq.write(f.mb_str(), offset);
903 }
904 
905 
906 namespace {
907 bool IsValidVisibleChannels(const int nValue)
908 {
909  return (nValue >= 0 && nValue < (1 << 16));
910 }
911 }
912 
913 bool NoteTrack::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
914 {
915  if (tag == "notetrack") {
916  for (auto pair : attrs)
917  {
918  auto attr = pair.first;
919  auto value = pair.second;
920 
921  long nValue;
922  double dblValue;
923  if (this->Track::HandleCommonXMLAttribute(attr, value))
924  ;
925  else if (this->NoteTrackBase::HandleXMLAttribute(attr, value))
926  {}
927  else if (attr == "offset" && value.TryGet(dblValue))
928  SetOffset(dblValue);
929  else if (attr == "visiblechannels") {
930  if (!value.TryGet(nValue) ||
931  !IsValidVisibleChannels(nValue))
932  return false;
933  mVisibleChannels = nValue;
934  }
935 #ifdef EXPERIMENTAL_MIDI_OUT
936  else if (attr == "velocity" && value.TryGet(dblValue))
937  mVelocity = (float) dblValue;
938 #endif
939  else if (attr == "bottomnote" && value.TryGet(nValue))
940  SetBottomNote(nValue);
941  else if (attr == "topnote" && value.TryGet(nValue))
942  SetTopNote(nValue);
943  else if (attr == "data") {
944  std::string s(value.ToWString());
945  std::istringstream data(s);
946  mSeq = std::make_unique<Alg_seq>(data, false);
947  }
948  } // while
949  return true;
950  }
951  return false;
952 }
953 
954 XMLTagHandler *NoteTrack::HandleXMLChild(const std::string_view& WXUNUSED(tag))
955 {
956  return NULL;
957 }
958 
959 void NoteTrack::WriteXML(XMLWriter &xmlFile) const
960 // may throw
961 {
962  std::ostringstream data;
963  Track::Holder holder;
964  const NoteTrack *saveme = this;
965  if (!mSeq) {
966  // replace saveme with an (unserialized) duplicate, which is
967  // destroyed at end of function.
968  holder = Clone();
969  saveme = static_cast<NoteTrack*>(holder.get());
970  }
971  saveme->GetSeq().write(data, true);
972  xmlFile.StartTag(wxT("notetrack"));
973  saveme->Track::WriteCommonXMLAttributes( xmlFile );
974  this->NoteTrackBase::WriteXMLAttributes(xmlFile);
975  xmlFile.WriteAttr(wxT("offset"), saveme->GetOffset());
976  xmlFile.WriteAttr(wxT("visiblechannels"), saveme->mVisibleChannels);
977 
978 #ifdef EXPERIMENTAL_MIDI_OUT
979  xmlFile.WriteAttr(wxT("velocity"), (double) saveme->mVelocity);
980 #endif
981  xmlFile.WriteAttr(wxT("bottomnote"), saveme->mBottomNote);
982  xmlFile.WriteAttr(wxT("topnote"), saveme->mTopNote);
983  xmlFile.WriteAttr(wxT("data"), wxString(data.str().c_str(), wxConvUTF8));
984  xmlFile.EndTag(wxT("notetrack"));
985 }
986 
988 {
989  if (note < MinPitch)
990  note = MinPitch;
991  else if (note > 96)
992  note = 96;
993 
994  wxCHECK(note <= mTopNote, );
995 
996  mBottomNote = note;
997 }
998 
999 void NoteTrack::SetTopNote(int note)
1000 {
1001  if (note > MaxPitch)
1002  note = MaxPitch;
1003 
1004  wxCHECK(note >= mBottomNote, );
1005 
1006  mTopNote = note;
1007 }
1008 
1009 void NoteTrack::SetNoteRange(int note1, int note2)
1010 {
1011  // Bounds check
1012  if (note1 > MaxPitch)
1013  note1 = MaxPitch;
1014  else if (note1 < MinPitch)
1015  note1 = MinPitch;
1016  if (note2 > MaxPitch)
1017  note2 = MaxPitch;
1018  else if (note2 < MinPitch)
1019  note2 = MinPitch;
1020  // Swap to ensure ordering
1021  if (note2 < note1) { auto tmp = note1; note1 = note2; note2 = tmp; }
1022 
1023  mBottomNote = note1;
1024  mTopNote = note2;
1025 }
1026 
1028 {
1029  // Ensure everything stays in bounds
1030  if (mBottomNote + offset < MinPitch || mTopNote + offset > MaxPitch)
1031  return;
1032 
1033  mBottomNote += offset;
1034  mTopNote += offset;
1035 }
1036 
1037 #if 0
1038 void NoteTrack::StartVScroll()
1039 {
1040  mStartBottomNote = mBottomNote;
1041 }
1042 
1043 void NoteTrack::VScroll(int start, int end)
1044 {
1045  int ph = GetPitchHeight();
1046  int delta = ((end - start) + ph / 2) / ph;
1047  ShiftNoteRange(delta);
1048 }
1049 #endif
1050 
1051 void NoteTrack::Zoom(const wxRect &rect, int y, float multiplier, bool center)
1052 {
1053  NoteTrackDisplayData data = NoteTrackDisplayData(this, rect);
1054  int clickedPitch = data.YToIPitch(y);
1055  int extent = mTopNote - mBottomNote + 1;
1056  int newExtent = (int) (extent / multiplier);
1057  float position;
1058  if (center) {
1059  // center the pitch that the user clicked on
1060  position = .5;
1061  } else {
1062  // align to keep the pitch that the user clicked on in the same place
1063  position = extent / (clickedPitch - mBottomNote);
1064  }
1065  int newBottomNote = clickedPitch - (newExtent * position);
1066  int newTopNote = clickedPitch + (newExtent * (1 - position));
1067  SetNoteRange(newBottomNote, newTopNote);
1068 }
1069 
1070 
1071 void NoteTrack::ZoomTo(const wxRect &rect, int start, int end)
1072 {
1073  wxRect trackRect(0, rect.GetY(), 1, rect.GetHeight());
1074  NoteTrackDisplayData data = NoteTrackDisplayData(this, trackRect);
1075  int pitch1 = data.YToIPitch(start);
1076  int pitch2 = data.YToIPitch(end);
1077  if (pitch1 == pitch2) {
1078  // Just zoom in instead of zooming to show only one note
1079  Zoom(rect, start, 1, true);
1080  return;
1081  }
1082  // It's fine for this to be in either order
1083  SetNoteRange(pitch1, pitch2);
1084 }
1085 
1087 {
1088  Alg_iterator iterator( &GetSeq(), false );
1089  iterator.begin();
1090  Alg_event_ptr evt;
1091 
1092  // Go through all of the notes, finding the minimum and maximum value pitches.
1093  bool hasNotes = false;
1094  int minPitch = MaxPitch;
1095  int maxPitch = MinPitch;
1096 
1097  while (NULL != (evt = iterator.next())) {
1098  if (evt->is_note()) {
1099  int pitch = (int) evt->get_pitch();
1100  hasNotes = true;
1101  if (pitch < minPitch)
1102  minPitch = pitch;
1103  if (pitch > maxPitch)
1104  maxPitch = pitch;
1105  }
1106  }
1107 
1108  if (!hasNotes) {
1109  // Semi-arbitrary default values:
1110  minPitch = 48;
1111  maxPitch = 72;
1112  }
1113 
1114  SetNoteRange(minPitch, maxPitch);
1115 }
1116 
1118 {
1119  auto span = track->GetTopNote() - track->GetBottomNote() + 1; // + 1 to make sure it includes both
1120 
1121  mMargin = std::min((int) (r.height / (float)(span)) / 2, r.height / 4);
1122 
1123  // Count the number of dividers between B/C and E/F
1124  int numC = 0, numF = 0;
1125  auto botOctave = track->GetBottomNote() / 12, botNote = track->GetBottomNote() % 12;
1126  auto topOctave = track->GetTopNote() / 12, topNote = track->GetTopNote() % 12;
1127  if (topOctave == botOctave)
1128  {
1129  if (botNote == 0) numC = 1;
1130  if (topNote <= 5) numF = 1;
1131  }
1132  else
1133  {
1134  numC = topOctave - botOctave;
1135  numF = topOctave - botOctave - 1;
1136  if (botNote == 0) numC++;
1137  if (botNote <= 5) numF++;
1138  if (topOctave <= 5) numF++;
1139  }
1140  // Effective space, excluding the margins and the lines between some notes
1141  auto effectiveHeight = r.height - (2 * (mMargin + 1)) - numC - numF;
1142  // Guaranteed that both the bottom and top notes will be visible
1143  // (assuming that the clamping below does not happen)
1144  mPitchHeight = effectiveHeight / ((float) span);
1145 
1150 
1151  mBottom = r.y + r.height - GetNoteMargin() - 1 - GetPitchHeight(1) +
1152  botOctave * GetOctaveHeight() + GetNotePos(botNote);
1153 }
1154 
1156 { return mBottom - (p / 12) * GetOctaveHeight() - GetNotePos(p % 12); }
1157 
1159 {
1160  y = mBottom - y; // pixels above pitch 0
1161  int octave = (y / GetOctaveHeight());
1162  y -= octave * GetOctaveHeight();
1163  // result is approximate because C and G are one pixel taller than
1164  // mPitchHeight.
1165  // Poke 1-13-18: However in practice this seems not to be an issue,
1166  // as long as we use mPitchHeight and not the rounded version
1167  return (y / mPitchHeight) + octave * 12;
1168 }
1169 
1170 const float NoteTrack::ZoomStep = powf( 2.0f, 0.25f );
1171 
1172 #include <wx/log.h>
1173 #include <wx/sstream.h>
1174 #include <wx/txtstrm.h>
1175 #include "AudioIOBase.h"
1176 #include "portmidi.h"
1177 
1178 // FIXME: When EXPERIMENTAL_MIDI_IN is added (eventually) this should also be enabled -- Poke
1180 {
1181  wxStringOutputStream o;
1182  wxTextOutputStream s(o, wxEOL_UNIX);
1183 
1184  if (AudioIOBase::Get()->IsStreamActive()) {
1185  return XO("Stream is active ... unable to gather information.\n")
1186  .Translation();
1187  }
1188 
1189 
1190  // XXX: May need to trap errors as with the normal device info
1191  int recDeviceNum = Pm_GetDefaultInputDeviceID();
1192  int playDeviceNum = Pm_GetDefaultOutputDeviceID();
1193  int cnt = Pm_CountDevices();
1194 
1195  // PRL: why only into the log?
1196  wxLogDebug(wxT("PortMidi reports %d MIDI devices"), cnt);
1197 
1198  s << wxT("==============================\n");
1199  s << XO("Default recording device number: %d\n").Format( recDeviceNum );
1200  s << XO("Default playback device number: %d\n").Format( playDeviceNum );
1201 
1202  auto recDevice = MIDIRecordingDevice.Read();
1203  auto playDevice = MIDIPlaybackDevice.Read();
1204 
1205  // This gets info on all available audio devices (input and output)
1206  if (cnt <= 0) {
1207  s << XO("No devices found\n");
1208  return o.GetString();
1209  }
1210 
1211  for (int i = 0; i < cnt; i++) {
1212  s << wxT("==============================\n");
1213 
1214  const PmDeviceInfo* info = Pm_GetDeviceInfo(i);
1215  if (!info) {
1216  s << XO("Device info unavailable for: %d\n").Format( i );
1217  continue;
1218  }
1219 
1220  wxString name = wxSafeConvertMB2WX(info->name);
1221  wxString hostName = wxSafeConvertMB2WX(info->interf);
1222 
1223  s << XO("Device ID: %d\n").Format( i );
1224  s << XO("Device name: %s\n").Format( name );
1225  s << XO("Host name: %s\n").Format( hostName );
1226  /* i18n-hint: Supported, meaning made available by the system */
1227  s << XO("Supports output: %d\n").Format( info->output );
1228  /* i18n-hint: Supported, meaning made available by the system */
1229  s << XO("Supports input: %d\n").Format( info->input );
1230  s << XO("Opened: %d\n").Format( info->opened );
1231 
1232  if (name == playDevice && info->output)
1233  playDeviceNum = i;
1234 
1235  if (name == recDevice && info->input)
1236  recDeviceNum = i;
1237 
1238  // XXX: This is only done because the same was applied with PortAudio
1239  // If PortMidi returns -1 for the default device, use the first one
1240  if (recDeviceNum < 0 && info->input){
1241  recDeviceNum = i;
1242  }
1243  if (playDeviceNum < 0 && info->output){
1244  playDeviceNum = i;
1245  }
1246  }
1247 
1248  bool haveRecDevice = (recDeviceNum >= 0);
1249  bool havePlayDevice = (playDeviceNum >= 0);
1250 
1251  s << wxT("==============================\n");
1252  if (haveRecDevice)
1253  s << XO("Selected MIDI recording device: %d - %s\n").Format( recDeviceNum, recDevice );
1254  else
1255  s << XO("No MIDI recording device found for '%s'.\n").Format( recDevice );
1256 
1257  if (havePlayDevice)
1258  s << XO("Selected MIDI playback device: %d - %s\n").Format( playDeviceNum, playDevice );
1259  else
1260  s << XO("No MIDI playback device found for '%s'.\n").Format( playDevice );
1261 
1262  // Mention our conditional compilation flags for Alpha only
1263 #ifdef IS_ALPHA
1264 
1265  // Not internationalizing these alpha-only messages
1266  s << wxT("==============================\n");
1267 #ifdef EXPERIMENTAL_MIDI_OUT
1268  s << wxT("EXPERIMENTAL_MIDI_OUT is enabled\n");
1269 #else
1270  s << wxT("EXPERIMENTAL_MIDI_OUT is NOT enabled\n");
1271 #endif
1272 #ifdef EXPERIMENTAL_MIDI_IN
1273  s << wxT("EXPERIMENTAL_MIDI_IN is enabled\n");
1274 #else
1275  s << wxT("EXPERIMENTAL_MIDI_IN is NOT enabled\n");
1276 #endif
1277 
1278 #endif
1279 
1280  return o.GetString();
1281 }
1282 
1283 StringSetting MIDIPlaybackDevice{ L"/MidiIO/PlaybackDevice", L"" };
1284 StringSetting MIDIRecordingDevice{ L"/MidiIO/RecordingDevice", L"" };
1285 IntSetting MIDISynthLatency_ms{ L"/MidiIO/SynthLatency", 5 };
1286 
1287 #endif // USE_MIDI
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:26
SonifyBeginSonification
#define SonifyBeginSonification()
Definition: NoteTrack.h:288
AudioIOBase.h
NoteTrackDisplayData::NoteTrackDisplayData
NoteTrackDisplayData(const NoteTrack *track, const wxRect &r)
Definition: NoteTrack.cpp:1117
Track::GetDefaultName
wxString GetDefaultName() const
Definition: Track.h:425
Track::mOffset
double mOffset
Definition: Track.h:405
NoteTrackDisplayData::IPitchToY
int IPitchToY(int p) const
Definition: NoteTrack.cpp:1155
NoteTrack::ExportAllegro
bool ExportAllegro(const wxString &f) const
Definition: NoteTrack.cpp:892
NoteTrack::PasteInto
Track::Holder PasteInto(AudacityProject &) const override
Find or create the destination track for a paste, maybe in a different project.
Definition: NoteTrack.cpp:706
TimeWarper
Transforms one point in time to another point. For example, a time stretching effect might use one to...
Definition: TimeWarper.h:62
NoteTrack::ClassTypeInfo
static const TypeInfo & ClassTypeInfo()
Definition: NoteTrack.cpp:701
NoteTrack::LabelClick
bool LabelClick(const wxRect &rect, int x, int y, bool right)
Definition: NoteTrack.cpp:375
readerEntry
static ProjectFileIORegistry::ObjectReaderEntry readerEntry
Definition: NoteTrack.cpp:110
NoteTrack::WriteXML
void WriteXML(XMLWriter &xmlFile) const override
Definition: NoteTrack.cpp:959
NoteTrackDisplayData::mMargin
int mMargin
Definition: NoteTrack.h:242
NoteTrackDisplayData::MinPitchHeight
@ MinPitchHeight
Definition: NoteTrack.h:244
IntSetting
Specialization of Setting for int.
Definition: Prefs.h:213
NoteTrack::MakeExportableSeq
Alg_seq * MakeExportableSeq(std::unique_ptr< Alg_seq > &cleanup) const
Definition: NoteTrack.cpp:761
NoteTrack::GetOffset
double GetOffset() const override
Definition: NoteTrack.cpp:208
NoteTrack::ShiftNoteRange
void ShiftNoteRange(int offset)
Shifts all notes vertically by the given pitch.
Definition: NoteTrack.cpp:1027
AllThemeResources.h
Project.h
Track::ConstIntervals
std::vector< ConstInterval > ConstIntervals
Definition: Track.h:316
InconsistencyException.h
MessageBoxException for violation of preconditions or assertions.
NoteTrack::InsertSilence
void InsertSilence(double t, double len) override
Definition: NoteTrack.cpp:625
TimeWarper.h
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper,...
NoteTrack::DrawLabelControls
static void DrawLabelControls(const NoteTrack *pTrack, wxDC &dc, const wxRect &rect, int highlightedChannel=-1)
Definition: NoteTrack.cpp:262
XMLMethodRegistry::ObjectReaderEntry
Definition: XMLMethodRegistry.h:91
AColor::Line
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:109
MIDIRecordingDevice
StringSetting MIDIRecordingDevice
Definition: NoteTrack.cpp:1284
MIDISynthLatency_ms
IntSetting MIDISynthLatency_ms
Definition: NoteTrack.cpp:1285
NoteTrack::ToggleVisibleChan
void ToggleVisibleChan(int c)
Definition: NoteTrack.h:182
typeInfo
static const Track::TypeInfo & typeInfo()
Definition: NoteTrack.cpp:688
ALL_CHANNELS
#define ALL_CHANNELS
Definition: NoteTrack.h:175
XO
#define XO(s)
Definition: Internat.h:31
NoteTrack.h
NoteTrack::SetSequence
void SetSequence(std::unique_ptr< Alg_seq > &&seq)
Definition: NoteTrack.cpp:386
Track::SetOffset
virtual void SetOffset(double o)
Definition: Track.h:442
SonifyEndSonification
#define SonifyEndSonification()
Definition: NoteTrack.h:289
AColor::MIDIChannel
static void MIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:573
QuantizedTimeAndBeat
std::pair< double, double > QuantizedTimeAndBeat
Definition: NoteTrack.h:60
NoteTrack::SetTopNote
void SetTopNote(int note)
Sets the top note (a pitch), making sure that it is never less than the bottom note.
Definition: NoteTrack.cpp:999
NoteTrack::ZoomAllNotes
void ZoomAllNotes()
Zooms so that all notes are visible.
Definition: NoteTrack.cpp:1086
Setting::Read
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:127
NoteTrack::GetTypeInfo
const TypeInfo & GetTypeInfo() const override
Definition: NoteTrack.cpp:696
NoteTrack::IsVisibleChan
bool IsVisibleChan(int c) const
Definition: NoteTrack.h:177
StringSetting
Specialization of Setting for strings.
Definition: Prefs.h:227
ImportExportPrefs::AllegroStyleSetting
static EnumSetting< bool > AllegroStyleSetting
Definition: ImportExportPrefs.h:31
PmStream
void PmStream
Definition: MIDIPlay.h:19
NoteTrack::New
static NoteTrack * New(AudacityProject &project)
Definition: NoteTrack.cpp:115
NoteTrack::ZoomStep
static const float ZoomStep
Definition: NoteTrack.h:227
NoteTrack::Clear
void Clear(double t0, double t1) override
Definition: NoteTrack.cpp:527
NoteTrackDisplayData::mPitchHeight
float mPitchHeight
Definition: NoteTrack.h:237
NoteTrack::~NoteTrack
virtual ~NoteTrack()
Definition: NoteTrack.cpp:141
AudioTrack::WriteXMLAttributes
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const
Definition: Track.h:864
AColor::DarkMIDIChannel
static void DarkMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:607
NoteTrack::StretchRegion
bool StretchRegion(QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur)
Definition: NoteTrack.cpp:740
NoteTrackDisplayData
Data used to display a note track.
Definition: NoteTrack.h:235
NoteTrack::WarpAndTransposeNotes
void WarpAndTransposeNotes(double t0, double t1, const TimeWarper &warper, double semitones)
Definition: NoteTrack.cpp:223
Track::Holder
std::shared_ptr< Track > Holder
Definition: Track.h:347
NoteTrack::mTopNote
int mTopNote
Definition: NoteTrack.h:216
Track::SetDefaultName
void SetDefaultName(const wxString &n)
Definition: Track.h:426
AColor::LightMIDIChannel
static void LightMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:589
NoteTrackDisplayData::YToIPitch
int YToIPitch(int y) const
Definition: NoteTrack.cpp:1158
NoteTrack::Copy
Track::Holder Copy(double t0, double t1, bool forClipboard=true) const override
Definition: NoteTrack.cpp:480
NoteTrack::GetTopNote
int GetTopNote() const
Gets the current top note (a pitch)
Definition: NoteTrack.h:125
NoteTrack::mBottomNote
int mBottomNote
Definition: NoteTrack.h:216
NoteTrack::GetBottomNote
int GetBottomNote() const
Gets the current bottom note (a pitch)
Definition: NoteTrack.h:123
Theme.h
NoteTrackDisplayData::mBottom
int mBottom
Definition: NoteTrack.h:241
NoteTrack::ZoomTo
void ZoomTo(const wxRect &rect, int start, int end)
Definition: NoteTrack.cpp:1071
AudioTrack
Track subclass holding data representing sound (as notes, or samples, or ...)
Definition: Track.h:855
name
const TranslatableString name
Definition: Distortion.cpp:98
NoteTrack::Clone
Track::Holder Clone() const override
Definition: NoteTrack.cpp:167
AudioIOBase::Get
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:89
THROW_INCONSISTENCY_EXCEPTION
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:79
NoteTrack::Zoom
void Zoom(const wxRect &rect, int y, float multiplier, bool center)
Definition: NoteTrack.cpp:1051
NoteTrack::GetStartTime
double GetStartTime() const override
Definition: NoteTrack.cpp:213
NoteTrack::Silence
void Silence(double t0, double t1) override
Definition: NoteTrack.cpp:610
anonymous_namespace{NoteTrack.cpp}::swap
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:753
NoteTrackDisplayData::MaxPitchHeight
@ MaxPitchHeight
Definition: NoteTrack.h:244
anonymous_namespace{NoteTrack.cpp}::IsValidVisibleChannels
bool IsValidVisibleChannels(const int nValue)
Definition: NoteTrack.cpp:907
NoteTrack::Paste
void Paste(double t, const Track *src) override
Definition: NoteTrack.cpp:560
NoteTrack::ExportMIDI
bool ExportMIDI(const wxString &f) const
Definition: NoteTrack.cpp:884
NoteTrack::AddToDuration
void AddToDuration(double delta)
Definition: NoteTrack.cpp:727
NoteTrack::mSerializationLength
long mSerializationLength
Definition: NoteTrack.h:210
ImportExportPrefs.h
TimeWarper::Warp
virtual double Warp(double originalTime) const =0
PlayableTrack::ClassTypeInfo
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1171
NoteTrack::NearestBeatTime
QuantizedTimeAndBeat NearestBeatTime(double time) const
Definition: NoteTrack.cpp:677
theTheme
THEME_API Theme theTheme
Definition: Theme.cpp:82
XMLTagHandler
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:42
Track::Intervals
std::vector< Interval > Intervals
Definition: Track.h:314
SonifyBeginSerialize
#define SonifyBeginSerialize()
Definition: NoteTrack.h:296
NoteTrack::SoloVisibleChan
void SoloVisibleChan(int c)
Definition: NoteTrack.h:185
NoteTrack::Cut
Track::Holder Cut(double t0, double t1) override
Definition: NoteTrack.cpp:450
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
AudioTrack::HandleXMLAttribute
bool HandleXMLAttribute(const std::string_view &, const XMLAttributeValueView &)
Definition: Track.h:867
Track::TypeSwitch
R TypeSwitch(const Functions &...functions)
Use this function rather than testing track type explicitly and making down-casts.
Definition: Track.h:790
NoteTrackDisplayData::GetOctaveHeight
int GetOctaveHeight() const
Definition: NoteTrack.h:251
EnumSetting::ReadEnum
Enum ReadEnum() const
Definition: Prefs.h:372
NoteTrack::Trim
bool Trim(double t0, double t1)
Definition: NoteTrack.cpp:503
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:377
NoteTrack::HandleXMLTag
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
Definition: NoteTrack.cpp:913
Track::SetName
void SetName(const wxString &n)
Definition: Track.cpp:77
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:224
NoteTrack::MinPitch
@ MinPitch
Definition: NoteTrack.h:226
Track::Notify
void Notify(int code=-1)
Definition: Track.cpp:210
NoteTrackDisplayData::GetNoteMargin
int GetNoteMargin() const
Definition: NoteTrack.h:250
_
#define _(s)
Definition: Internat.h:75
SonifyEndSerialize
#define SonifyEndSerialize()
Definition: NoteTrack.h:297
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
NoteTrack::GetIntervals
ConstIntervals GetIntervals() const override
Report times on the track where important intervals begin and end, for UI to snap to.
Definition: NoteTrack.cpp:713
NoteTrack::PrintSequence
void PrintSequence()
Definition: NoteTrack.cpp:391
NoteTrackDisplayData::GetNotePos
int GetNotePos(int p) const
Definition: NoteTrack.h:268
NoteTrack::mVisibleChannels
int mVisibleChannels
Definition: NoteTrack.h:229
NoteTrackDisplayData::GetPitchHeight
int GetPitchHeight(int factor) const
Definition: NoteTrack.h:248
NoteTrack::SetBottomNote
void SetBottomNote(int note)
Sets the bottom note (a pitch), making sure that it is never greater than the top note.
Definition: NoteTrack.cpp:987
NoteTrack::HandleXMLChild
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
Definition: NoteTrack.cpp:954
NoteTrack::GetSeq
Alg_seq & GetSeq() const
Definition: NoteTrack.cpp:145
Prefs.h
GetMIDIDeviceInfo
wxString GetMIDIDeviceInfo()
Definition: NoteTrack.cpp:1179
NoteTrack::NoteTrack
NoteTrack()
Definition: NoteTrack.cpp:123
NoteTrack::GetEndTime
double GetEndTime() const override
Definition: NoteTrack.cpp:218
ROUND
#define ROUND(x)
Definition: NoteTrack.cpp:33
NoteTrack::mSerializationBuffer
std::unique_ptr< char[]> mSerializationBuffer
Definition: NoteTrack.h:209
Track::TypeInfo
Definition: Track.h:325
ThemeBase::Colour
wxColour & Colour(int iIndex)
safenew
#define safenew
Definition: MemoryX.h:10
NoteTrack::FindChannel
int FindChannel(const wxRect &rect, int mx, int my)
Definition: NoteTrack.cpp:356
AColor.h
within
bool within(A a, B b, DIST d)
Definition: TrackPanel.cpp:163
NoteTrack::MaxPitch
@ MaxPitch
Definition: NoteTrack.h:226
NoteTrack::mSeq
std::unique_ptr< Alg_seq > mSeq
Definition: NoteTrack.h:208
Track::HandleCommonXMLAttribute
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
Definition: Track.cpp:1132
AttributesList
std::vector< Attribute > AttributesList
Definition: XMLTagHandler.h:40
NoteTrack::SetNoteRange
void SetNoteRange(int note1, int note2)
Sets the top and bottom note (both pitches) automatically, swapping them if needed.
Definition: NoteTrack.cpp:1009
NoteTrack
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:67
MIDIPlaybackDevice
StringSetting MIDIPlaybackDevice
Definition: NoteTrack.cpp:1283
NoteTrack::Shift
bool Shift(double t)
Definition: NoteTrack.cpp:650