Audacity  3.0.3
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 "effects/TimeWarper.h"
43 #include "tracks/ui/TrackView.h"
45 
46 #include "AllThemeResources.h"
47 #include "Theme.h"
48 
49 #ifdef SONIFY
50 #include <portmidi.h>
51 
52 #define SON_PROGRAM 0
53 #define SON_AutoSave 67
54 #define SON_ModifyState 60
55 #define SON_NoteBackground 72
56 #define SON_NoteForeground 74
57 #define SON_Measures 76 /* "bar line" */
58 #define SON_Serialize 77
59 #define SON_Unserialize 79
60 #define SON_VEL 100
61 
62 
63 PmStream *sonMidiStream;
64 bool sonificationStarted = false;
65 
67 {
68  PmError err = Pm_OpenOutput(&sonMidiStream, Pm_GetDefaultOutputDeviceID(),
69  NULL, 0, NULL, NULL, 0);
70  if (err) sonMidiStream = NULL;
71  if (sonMidiStream)
72  Pm_WriteShort(sonMidiStream, 0, Pm_Message(0xC0, SON_PROGRAM, 0));
73  sonificationStarted = true;
74 }
75 
76 
78 {
79  if (sonMidiStream) Pm_Close(sonMidiStream);
80  sonificationStarted = false;
81 }
82 
83 
84 
85 
86 void SonifyNoteOnOff(int p, int v)
87 {
88  if (!sonificationStarted)
90  if (sonMidiStream)
91  Pm_WriteShort(sonMidiStream, 0, Pm_Message(0x90, p, v));
92 }
93 
94 #define SONFNS(name) \
95  void SonifyBegin ## name() { SonifyNoteOnOff(SON_ ## name, SON_VEL); } \
96  void SonifyEnd ## name() { SonifyNoteOnOff(SON_ ## name, 0); }
97 
98 SONFNS(NoteBackground)
99 SONFNS(NoteForeground)
100 SONFNS(Measures)
101 SONFNS(Serialize)
102 SONFNS(Unserialize)
103 SONFNS(ModifyState)
104 SONFNS(AutoSave)
105 
106 #undef SONFNS
107 
108 #endif
109 
110 
111 
113  wxT( "notetrack" ),
114  []( AudacityProject &project ){
115  auto &tracks = TrackList::Get( project );
116  auto result = tracks.Add( std::make_shared<NoteTrack>());
117  TrackView::Get( *result );
118  TrackControls::Get( *result );
119  return result;
120  }
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 
689 {
690  auto pNewTrack = std::make_shared<NoteTrack>();
691  pNewTrack->Paste(0.0, this);
692  return pNewTrack;
693 }
694 
696 {
697  ConstIntervals results;
698  results.emplace_back( GetStartTime(), GetEndTime() );
699  return results;
700 }
701 
703 {
704  Intervals results;
705  results.emplace_back( GetStartTime(), GetEndTime() );
706  return results;
707 }
708 
709 void NoteTrack::AddToDuration( double delta )
710 {
711  auto &seq = GetSeq();
712 #if 0
713  // PRL: Would this be better ?
714  seq.set_real_dur( seq.get_real_dur() + delta );
715 #else
716  seq.convert_to_seconds();
717  seq.set_dur( seq.get_dur() + delta );
718 #endif
719 }
720 
722  ( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur )
723 {
724  auto &seq = GetSeq();
725  bool result = seq.stretch_region( t0.second, t1.second, newDur );
726  if (result) {
727  const auto oldDur = t1.first - t0.first;
728  AddToDuration( newDur - oldDur );
729  }
730  return result;
731 }
732 
733 namespace
734 {
735  void swap(std::unique_ptr<Alg_seq> &a, std::unique_ptr<Alg_seq> &b)
736  {
737  std::unique_ptr<Alg_seq> tmp = std::move(a);
738  a = std::move(b);
739  b = std::move(tmp);
740  }
741 }
742 
743 Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
744 {
745  cleanup.reset();
746  double offset = GetOffset();
747  if (offset == 0)
748  return &GetSeq();
749  // make a copy, deleting events that are shifted before time 0
750  double start = -offset;
751  if (start < 0) start = 0;
752  // notes that begin before "start" are not included even if they
753  // extend past "start" (because "all" parameter is set to false)
754  cleanup.reset( GetSeq().copy(start, GetSeq().get_dur() - start, false) );
755  auto seq = cleanup.get();
756  if (offset > 0) {
757  {
758  // swap cleanup and mSeq so that Shift operates on the NEW copy
759  swap( this->mSeq, cleanup );
760  auto cleanup2 = finally( [&] { swap( this->mSeq, cleanup ); } );
761 
762  const_cast< NoteTrack *>( this )->Shift(offset);
763  }
764 #ifdef OLD_CODE
765  // now shift events by offset. This must be done with an integer
766  // number of measures, so first, find the beats-per-measure
767  double beats_per_measure = 4.0;
768  Alg_time_sig_ptr tsp = NULL;
769  if (seq->time_sig.length() > 0 && seq->time_sig[0].beat < ALG_EPS) {
770  // there is an initial time signature
771  tsp = &(seq->time_sig[0]);
772  beats_per_measure = (tsp->num * 4) / tsp->den;
773  }
774  // also need the initial tempo
775  double bps = ALG_DEFAULT_BPM / 60;
776  Alg_time_map_ptr map = seq->get_time_map();
777  Alg_beat_ptr bp = &(map->beats[0]);
778  if (bp->time < ALG_EPS) { // tempo change at time 0
779  if (map->beats.len > 1) { // compute slope to get tempo
780  bps = (map->beats[1].beat - map->beats[0].beat) /
781  (map->beats[1].time - map->beats[0].time);
782  } else if (seq->get_time_map()->last_tempo_flag) {
783  bps = seq->get_time_map()->last_tempo;
784  }
785  }
786  // find closest number of measures to fit in the gap
787  // number of measures is offset / measure_time
788  double measure_time = beats_per_measure / bps; // seconds per measure
789  int n = ROUND(offset / measure_time);
790  if (n == 0) n = 1;
791  // we will insert n measures. Compute the desired duration of each.
792  measure_time = offset / n;
793  bps = beats_per_measure / measure_time;
794  // insert integer multiple of measures at beginning
795  seq->convert_to_beats();
796  seq->insert_silence(0, beats_per_measure * n);
797  // make sure time signature at 0 is correct
798  if (tsp) {
799  seq->set_time_sig(0, tsp->num, tsp->den);
800  }
801  // adjust tempo to match offset
802  seq->set_tempo(bps * 60.0, 0, beats_per_measure * n);
803 #endif
804  } else {
805  auto &mySeq = GetSeq();
806  // if offset is negative, it might not be a multiple of beats, but
807  // we want to preserve the relative positions of measures. I.e. we
808  // should shift barlines and time signatures as well as notes.
809  // Insert a time signature at the first bar-line if necessary.
810 
811  // Translate start from seconds to beats and call it beat:
812  double beat = mySeq.get_time_map()->time_to_beat(start);
813  // Find the time signature in mySeq in effect at start (beat):
814  int i = mySeq.time_sig.find_beat(beat);
815  // i is where you would insert a NEW time sig at beat,
816  // Case 1: beat coincides with a time sig at i. Time signature
817  // at beat means that there is a barline at beat, so when beat
818  // is shifted to 0, the relative barline positions are preserved
819  if (mySeq.time_sig.length() > 0 &&
820  within(beat, mySeq.time_sig[i].beat, ALG_EPS)) {
821  // beat coincides with time signature change, so offset must
822  // be a multiple of beats
823  /* do nothing */ ;
824  // Case 2: there is no time signature before beat.
825  } else if (i == 0 && (mySeq.time_sig.length() == 0 ||
826  mySeq.time_sig[i].beat > beat)) {
827  // If beat does not fall on an implied barline, we need to
828  // insert a time signature.
829  double measures = beat / 4.0;
830  double imeasures = ROUND(measures);
831  if (!within(measures, imeasures, ALG_EPS)) {
832  double bar_offset = ((int)(measures) + 1) * 4.0 - beat;
833  seq->set_time_sig(bar_offset, 4, 4);
834  }
835  // This case should never be true because if i == 0, either there
836  // are no time signatures before beat (Case 2),
837  // or there is one time signature at beat (Case 1)
838  } else if (i == 0) {
839  /* do nothing (might be good to assert(false)) */ ;
840  // Case 3: i-1 must be the effective time sig position
841  } else {
842  i -= 1; // index the time signature in effect at beat
843  Alg_time_sig_ptr tsp = &(mySeq.time_sig[i]);
844  double beats_per_measure = (tsp->num * 4) / tsp->den;
845  double measures = (beat - tsp->beat) / beats_per_measure;
846  int imeasures = ROUND(measures);
847  if (!within(measures, imeasures, ALG_EPS)) {
848  // beat is not on a measure, so we need to insert a time sig
849  // to force a bar line at the first measure location after
850  // beat
851  double bar = tsp->beat + beats_per_measure * ((int)(measures) + 1);
852  double bar_offset = bar - beat;
853  // insert NEW time signature at bar_offset in NEW sequence
854  // It will have the same time signature, but the position will
855  // force a barline to match the barlines in mSeq
856  seq->set_time_sig(bar_offset, tsp->num, tsp->den);
857  }
858  // else beat coincides with a barline, so no need for an extra
859  // time signature to force barline alignment
860  }
861  }
862  return seq;
863 }
864 
865 
866 bool NoteTrack::ExportMIDI(const wxString &f) const
867 {
868  std::unique_ptr<Alg_seq> cleanup;
869  auto seq = MakeExportableSeq(cleanup);
870  bool rslt = seq->smf_write(f.mb_str());
871  return rslt;
872 }
873 
874 bool NoteTrack::ExportAllegro(const wxString &f) const
875 {
876  double offset = GetOffset();
878  auto &seq = GetSeq();
879  if (in_seconds) {
880  seq.convert_to_seconds();
881  } else {
882  seq.convert_to_beats();
883  }
884  return seq.write(f.mb_str(), offset);
885 }
886 
887 
888 namespace {
889 bool IsValidVisibleChannels(const int nValue)
890 {
891  return (nValue >= 0 && nValue < (1 << 16));
892 }
893 }
894 
895 bool NoteTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
896 {
897  if (!wxStrcmp(tag, wxT("notetrack"))) {
898  while (*attrs) {
899  const wxChar *attr = *attrs++;
900  const wxChar *value = *attrs++;
901  if (!value)
902  break;
903  const wxString strValue = value;
904  long nValue;
905  double dblValue;
906  if (this->Track::HandleCommonXMLAttribute(attr, strValue))
907  ;
908  else if (this->NoteTrackBase::HandleXMLAttribute(attr, value))
909  {}
910  else if (!wxStrcmp(attr, wxT("offset")) &&
911  XMLValueChecker::IsGoodString(strValue) &&
912  Internat::CompatibleToDouble(strValue, &dblValue))
913  SetOffset(dblValue);
914  else if (!wxStrcmp(attr, wxT("visiblechannels"))) {
915  if (!XMLValueChecker::IsGoodInt(strValue) ||
916  !strValue.ToLong(&nValue) ||
917  !IsValidVisibleChannels(nValue))
918  return false;
919  mVisibleChannels = nValue;
920  }
921 #ifdef EXPERIMENTAL_MIDI_OUT
922  else if (!wxStrcmp(attr, wxT("velocity")) &&
923  XMLValueChecker::IsGoodString(strValue) &&
924  Internat::CompatibleToDouble(strValue, &dblValue))
925  mVelocity = (float) dblValue;
926 #endif
927  else if (!wxStrcmp(attr, wxT("bottomnote")) &&
928  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
929  SetBottomNote(nValue);
930  else if (!wxStrcmp(attr, wxT("topnote")) &&
931  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
932  SetTopNote(nValue);
933  else if (!wxStrcmp(attr, wxT("data"))) {
934  std::string s(strValue.mb_str(wxConvUTF8));
935  std::istringstream data(s);
936  mSeq = std::make_unique<Alg_seq>(data, false);
937  }
938  } // while
939  return true;
940  }
941  return false;
942 }
943 
944 XMLTagHandler *NoteTrack::HandleXMLChild(const wxChar * WXUNUSED(tag))
945 {
946  return NULL;
947 }
948 
949 void NoteTrack::WriteXML(XMLWriter &xmlFile) const
950 // may throw
951 {
952  std::ostringstream data;
953  Track::Holder holder;
954  const NoteTrack *saveme = this;
955  if (!mSeq) {
956  // replace saveme with an (unserialized) duplicate, which is
957  // destroyed at end of function.
958  holder = Clone();
959  saveme = static_cast<NoteTrack*>(holder.get());
960  }
961  saveme->GetSeq().write(data, true);
962  xmlFile.StartTag(wxT("notetrack"));
963  saveme->Track::WriteCommonXMLAttributes( xmlFile );
964  this->NoteTrackBase::WriteXMLAttributes(xmlFile);
965  xmlFile.WriteAttr(wxT("offset"), saveme->GetOffset());
966  xmlFile.WriteAttr(wxT("visiblechannels"), saveme->mVisibleChannels);
967 
968 #ifdef EXPERIMENTAL_MIDI_OUT
969  xmlFile.WriteAttr(wxT("velocity"), (double) saveme->mVelocity);
970 #endif
971  xmlFile.WriteAttr(wxT("bottomnote"), saveme->mBottomNote);
972  xmlFile.WriteAttr(wxT("topnote"), saveme->mTopNote);
973  xmlFile.WriteAttr(wxT("data"), wxString(data.str().c_str(), wxConvUTF8));
974  xmlFile.EndTag(wxT("notetrack"));
975 }
976 
978 {
979  if (note < MinPitch)
980  note = MinPitch;
981  else if (note > 96)
982  note = 96;
983 
984  wxCHECK(note <= mTopNote, );
985 
986  mBottomNote = note;
987 }
988 
989 void NoteTrack::SetTopNote(int note)
990 {
991  if (note > MaxPitch)
992  note = MaxPitch;
993 
994  wxCHECK(note >= mBottomNote, );
995 
996  mTopNote = note;
997 }
998 
999 void NoteTrack::SetNoteRange(int note1, int note2)
1000 {
1001  // Bounds check
1002  if (note1 > MaxPitch)
1003  note1 = MaxPitch;
1004  else if (note1 < MinPitch)
1005  note1 = MinPitch;
1006  if (note2 > MaxPitch)
1007  note2 = MaxPitch;
1008  else if (note2 < MinPitch)
1009  note2 = MinPitch;
1010  // Swap to ensure ordering
1011  if (note2 < note1) { auto tmp = note1; note1 = note2; note2 = tmp; }
1012 
1013  mBottomNote = note1;
1014  mTopNote = note2;
1015 }
1016 
1018 {
1019  // Ensure everything stays in bounds
1020  if (mBottomNote + offset < MinPitch || mTopNote + offset > MaxPitch)
1021  return;
1022 
1023  mBottomNote += offset;
1024  mTopNote += offset;
1025 }
1026 
1027 #if 0
1028 void NoteTrack::StartVScroll()
1029 {
1030  mStartBottomNote = mBottomNote;
1031 }
1032 
1033 void NoteTrack::VScroll(int start, int end)
1034 {
1035  int ph = GetPitchHeight();
1036  int delta = ((end - start) + ph / 2) / ph;
1037  ShiftNoteRange(delta);
1038 }
1039 #endif
1040 
1041 void NoteTrack::Zoom(const wxRect &rect, int y, float multiplier, bool center)
1042 {
1043  NoteTrackDisplayData data = NoteTrackDisplayData(this, rect);
1044  int clickedPitch = data.YToIPitch(y);
1045  int extent = mTopNote - mBottomNote + 1;
1046  int newExtent = (int) (extent / multiplier);
1047  float position;
1048  if (center) {
1049  // center the pitch that the user clicked on
1050  position = .5;
1051  } else {
1052  // align to keep the pitch that the user clicked on in the same place
1053  position = extent / (clickedPitch - mBottomNote);
1054  }
1055  int newBottomNote = clickedPitch - (newExtent * position);
1056  int newTopNote = clickedPitch + (newExtent * (1 - position));
1057  SetNoteRange(newBottomNote, newTopNote);
1058 }
1059 
1060 
1061 void NoteTrack::ZoomTo(const wxRect &rect, int start, int end)
1062 {
1063  wxRect trackRect(0, rect.GetY(), 1, rect.GetHeight());
1064  NoteTrackDisplayData data = NoteTrackDisplayData(this, trackRect);
1065  int pitch1 = data.YToIPitch(start);
1066  int pitch2 = data.YToIPitch(end);
1067  if (pitch1 == pitch2) {
1068  // Just zoom in instead of zooming to show only one note
1069  Zoom(rect, start, 1, true);
1070  return;
1071  }
1072  // It's fine for this to be in either order
1073  SetNoteRange(pitch1, pitch2);
1074 }
1075 
1077 {
1078  Alg_iterator iterator( &GetSeq(), false );
1079  iterator.begin();
1080  Alg_event_ptr evt;
1081 
1082  // Go through all of the notes, finding the minimum and maximum value pitches.
1083  bool hasNotes = false;
1084  int minPitch = MaxPitch;
1085  int maxPitch = MinPitch;
1086 
1087  while (NULL != (evt = iterator.next())) {
1088  if (evt->is_note()) {
1089  int pitch = (int) evt->get_pitch();
1090  hasNotes = true;
1091  if (pitch < minPitch)
1092  minPitch = pitch;
1093  if (pitch > maxPitch)
1094  maxPitch = pitch;
1095  }
1096  }
1097 
1098  if (!hasNotes) {
1099  // Semi-arbitrary default values:
1100  minPitch = 48;
1101  maxPitch = 72;
1102  }
1103 
1104  SetNoteRange(minPitch, maxPitch);
1105 }
1106 
1108 {
1109  auto span = track->GetTopNote() - track->GetBottomNote() + 1; // + 1 to make sure it includes both
1110 
1111  mMargin = std::min((int) (r.height / (float)(span)) / 2, r.height / 4);
1112 
1113  // Count the number of dividers between B/C and E/F
1114  int numC = 0, numF = 0;
1115  auto botOctave = track->GetBottomNote() / 12, botNote = track->GetBottomNote() % 12;
1116  auto topOctave = track->GetTopNote() / 12, topNote = track->GetTopNote() % 12;
1117  if (topOctave == botOctave)
1118  {
1119  if (botNote == 0) numC = 1;
1120  if (topNote <= 5) numF = 1;
1121  }
1122  else
1123  {
1124  numC = topOctave - botOctave;
1125  numF = topOctave - botOctave - 1;
1126  if (botNote == 0) numC++;
1127  if (botNote <= 5) numF++;
1128  if (topOctave <= 5) numF++;
1129  }
1130  // Effective space, excluding the margins and the lines between some notes
1131  auto effectiveHeight = r.height - (2 * (mMargin + 1)) - numC - numF;
1132  // Guaranteed that both the bottom and top notes will be visible
1133  // (assuming that the clamping below does not happen)
1134  mPitchHeight = effectiveHeight / ((float) span);
1135 
1140 
1141  mBottom = r.y + r.height - GetNoteMargin() - 1 - GetPitchHeight(1) +
1142  botOctave * GetOctaveHeight() + GetNotePos(botNote);
1143 }
1144 
1146 { return mBottom - (p / 12) * GetOctaveHeight() - GetNotePos(p % 12); }
1147 
1149 {
1150  y = mBottom - y; // pixels above pitch 0
1151  int octave = (y / GetOctaveHeight());
1152  y -= octave * GetOctaveHeight();
1153  // result is approximate because C and G are one pixel taller than
1154  // mPitchHeight.
1155  // Poke 1-13-18: However in practice this seems not to be an issue,
1156  // as long as we use mPitchHeight and not the rounded version
1157  return (y / mPitchHeight) + octave * 12;
1158 }
1159 
1160 const float NoteTrack::ZoomStep = powf( 2.0f, 0.25f );
1161 
1162 #include <wx/log.h>
1163 #include <wx/sstream.h>
1164 #include <wx/txtstrm.h>
1165 #include "AudioIOBase.h"
1166 #include "portmidi.h"
1167 
1168 // FIXME: When EXPERIMENTAL_MIDI_IN is added (eventually) this should also be enabled -- Poke
1170 {
1171  wxStringOutputStream o;
1172  wxTextOutputStream s(o, wxEOL_UNIX);
1173 
1174  if (AudioIOBase::Get()->IsStreamActive()) {
1175  return XO("Stream is active ... unable to gather information.\n")
1176  .Translation();
1177  }
1178 
1179 
1180  // XXX: May need to trap errors as with the normal device info
1181  int recDeviceNum = Pm_GetDefaultInputDeviceID();
1182  int playDeviceNum = Pm_GetDefaultOutputDeviceID();
1183  int cnt = Pm_CountDevices();
1184 
1185  // PRL: why only into the log?
1186  wxLogDebug(wxT("PortMidi reports %d MIDI devices"), cnt);
1187 
1188  s << wxT("==============================\n");
1189  s << XO("Default recording device number: %d\n").Format( recDeviceNum );
1190  s << XO("Default playback device number: %d\n").Format( playDeviceNum );
1191 
1192  auto recDevice = MIDIRecordingDevice.Read();
1193  auto playDevice = MIDIPlaybackDevice.Read();
1194 
1195  // This gets info on all available audio devices (input and output)
1196  if (cnt <= 0) {
1197  s << XO("No devices found\n");
1198  return o.GetString();
1199  }
1200 
1201  for (int i = 0; i < cnt; i++) {
1202  s << wxT("==============================\n");
1203 
1204  const PmDeviceInfo* info = Pm_GetDeviceInfo(i);
1205  if (!info) {
1206  s << XO("Device info unavailable for: %d\n").Format( i );
1207  continue;
1208  }
1209 
1210  wxString name = wxSafeConvertMB2WX(info->name);
1211  wxString hostName = wxSafeConvertMB2WX(info->interf);
1212 
1213  s << XO("Device ID: %d\n").Format( i );
1214  s << XO("Device name: %s\n").Format( name );
1215  s << XO("Host name: %s\n").Format( hostName );
1216  /* i18n-hint: Supported, meaning made available by the system */
1217  s << XO("Supports output: %d\n").Format( info->output );
1218  /* i18n-hint: Supported, meaning made available by the system */
1219  s << XO("Supports input: %d\n").Format( info->input );
1220  s << XO("Opened: %d\n").Format( info->opened );
1221 
1222  if (name == playDevice && info->output)
1223  playDeviceNum = i;
1224 
1225  if (name == recDevice && info->input)
1226  recDeviceNum = i;
1227 
1228  // XXX: This is only done because the same was applied with PortAudio
1229  // If PortMidi returns -1 for the default device, use the first one
1230  if (recDeviceNum < 0 && info->input){
1231  recDeviceNum = i;
1232  }
1233  if (playDeviceNum < 0 && info->output){
1234  playDeviceNum = i;
1235  }
1236  }
1237 
1238  bool haveRecDevice = (recDeviceNum >= 0);
1239  bool havePlayDevice = (playDeviceNum >= 0);
1240 
1241  s << wxT("==============================\n");
1242  if (haveRecDevice)
1243  s << XO("Selected MIDI recording device: %d - %s\n").Format( recDeviceNum, recDevice );
1244  else
1245  s << XO("No MIDI recording device found for '%s'.\n").Format( recDevice );
1246 
1247  if (havePlayDevice)
1248  s << XO("Selected MIDI playback device: %d - %s\n").Format( playDeviceNum, playDevice );
1249  else
1250  s << XO("No MIDI playback device found for '%s'.\n").Format( playDevice );
1251 
1252  // Mention our conditional compilation flags for Alpha only
1253 #ifdef IS_ALPHA
1254 
1255  // Not internationalizing these alpha-only messages
1256  s << wxT("==============================\n");
1257 #ifdef EXPERIMENTAL_MIDI_OUT
1258  s << wxT("EXPERIMENTAL_MIDI_OUT is enabled\n");
1259 #else
1260  s << wxT("EXPERIMENTAL_MIDI_OUT is NOT enabled\n");
1261 #endif
1262 #ifdef EXPERIMENTAL_MIDI_IN
1263  s << wxT("EXPERIMENTAL_MIDI_IN is enabled\n");
1264 #else
1265  s << wxT("EXPERIMENTAL_MIDI_IN is NOT enabled\n");
1266 #endif
1267 
1268 #endif
1269 
1270  return o.GetString();
1271 }
1272 
1273 StringSetting MIDIPlaybackDevice{ L"/MidiIO/PlaybackDevice", L"" };
1274 StringSetting MIDIRecordingDevice{ L"/MidiIO/RecordingDevice", L"" };
1275 IntSetting MIDISynthLatency_ms{ L"/MidiIO/SynthLatency", 5 };
1276 
1277 #endif // USE_MIDI
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:23
SonifyBeginSonification
#define SonifyBeginSonification()
Definition: NoteTrack.h:282
AudioIOBase.h
NoteTrackDisplayData::NoteTrackDisplayData
NoteTrackDisplayData(const NoteTrack *track, const wxRect &r)
Definition: NoteTrack.cpp:1107
Track::GetDefaultName
wxString GetDefaultName() const
Definition: Track.h:428
Track::mOffset
double mOffset
Definition: Track.h:408
NoteTrackDisplayData::IPitchToY
int IPitchToY(int p) const
Definition: NoteTrack.cpp:1145
NoteTrack::ExportAllegro
bool ExportAllegro(const wxString &f) const
Definition: NoteTrack.cpp:874
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:688
TimeWarper
Transforms one point in time to another point. For example, a time stretching effect might use one to...
Definition: TimeWarper.h:62
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:112
NoteTrack::WriteXML
void WriteXML(XMLWriter &xmlFile) const override
Definition: NoteTrack.cpp:949
NoteTrackDisplayData::mMargin
int mMargin
Definition: NoteTrack.h:238
NoteTrackDisplayData::MinPitchHeight
@ MinPitchHeight
Definition: NoteTrack.h:240
IntSetting
Specialization of Setting for int.
Definition: Prefs.h:214
TrackView::Get
static TrackView & Get(Track &)
Definition: TrackView.cpp:63
NoteTrack::MakeExportableSeq
Alg_seq * MakeExportableSeq(std::unique_ptr< Alg_seq > &cleanup) const
Definition: NoteTrack.cpp:743
NoteTrack::GetOffset
double GetOffset() const override
Definition: NoteTrack.cpp:208
XMLValueChecker::IsGoodInt
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
Definition: XMLTagHandler.cpp:157
NoteTrack::ShiftNoteRange
void ShiftNoteRange(int offset)
Shifts all notes vertically by the given pitch.
Definition: NoteTrack.cpp:1017
AllThemeResources.h
TrackView.h
Project.h
Track::ConstIntervals
std::vector< ConstInterval > ConstIntervals
Definition: Track.h:331
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,...
TrackControls::Get
static TrackControls & Get(Track &track)
Definition: TrackControls.cpp:25
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:82
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:1274
MIDISynthLatency_ms
IntSetting MIDISynthLatency_ms
Definition: NoteTrack.cpp:1275
NoteTrack::ToggleVisibleChan
void ToggleVisibleChan(int c)
Definition: NoteTrack.h:179
ALL_CHANNELS
#define ALL_CHANNELS
Definition: NoteTrack.h:172
XO
#define XO(s)
Definition: Internat.h:31
NoteTrack.h
NoteTrack::SetSequence
void SetSequence(std::unique_ptr< Alg_seq > &&seq)
Definition: NoteTrack.cpp:386
AudioTrack::HandleXMLAttribute
bool HandleXMLAttribute(const wxChar *, const wxChar *)
Definition: Track.h:848
Track::SetOffset
virtual void SetOffset(double o)
Definition: Track.h:445
SonifyEndSonification
#define SonifyEndSonification()
Definition: NoteTrack.h:283
AColor::MIDIChannel
static void MIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:573
Track::HandleCommonXMLAttribute
bool HandleCommonXMLAttribute(const wxChar *attr, const wxChar *value)
Definition: Track.cpp:1269
QuantizedTimeAndBeat
std::pair< double, double > QuantizedTimeAndBeat
Definition: NoteTrack.h:60
XMLValueChecker::IsGoodString
static bool IsGoodString(const wxString &str)
Definition: XMLTagHandler.cpp:38
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:989
NoteTrack::HandleXMLChild
XMLTagHandler * HandleXMLChild(const wxChar *tag) override
Definition: NoteTrack.cpp:944
NoteTrack::ZoomAllNotes
void ZoomAllNotes()
Zooms so that all notes are visible.
Definition: NoteTrack.cpp:1076
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:128
NoteTrack::IsVisibleChan
bool IsVisibleChan(int c) const
Definition: NoteTrack.h:174
StringSetting
Specialization of Setting for strings.
Definition: Prefs.h:228
NoteTrack::HandleXMLTag
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override
Definition: NoteTrack.cpp:895
ImportExportPrefs::AllegroStyleSetting
static EnumSetting< bool > AllegroStyleSetting
Definition: ImportExportPrefs.h:31
PmStream
void PmStream
Definition: MIDIPlay.h:20
NoteTrack::ZoomStep
static const float ZoomStep
Definition: NoteTrack.h:223
NoteTrack::Clear
void Clear(double t0, double t1) override
Definition: NoteTrack.cpp:527
NoteTrackDisplayData::mPitchHeight
float mPitchHeight
Definition: NoteTrack.h:233
NoteTrack::~NoteTrack
virtual ~NoteTrack()
Definition: NoteTrack.cpp:141
AudioTrack::WriteXMLAttributes
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const
Definition: Track.h:845
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:722
NoteTrackDisplayData
Data used to display a note track.
Definition: NoteTrack.h:231
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:336
NoteTrack::mTopNote
int mTopNote
Definition: NoteTrack.h:212
Track::SetDefaultName
void SetDefaultName(const wxString &n)
Definition: Track.h:429
AColor::LightMIDIChannel
static void LightMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:589
NoteTrackDisplayData::YToIPitch
int YToIPitch(int y) const
Definition: NoteTrack.cpp:1148
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:122
NoteTrack::mBottomNote
int mBottomNote
Definition: NoteTrack.h:212
NoteTrack::GetBottomNote
int GetBottomNote() const
Gets the current bottom note (a pitch)
Definition: NoteTrack.h:120
Theme.h
NoteTrackDisplayData::mBottom
int mBottom
Definition: NoteTrack.h:237
NoteTrack::ZoomTo
void ZoomTo(const wxRect &rect, int start, int end)
Definition: NoteTrack.cpp:1061
AudioTrack
Track subclass holding data representing sound (as notes, or samples, or ...)
Definition: Track.h:838
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
TrackControls.h
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:1041
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:735
NoteTrackDisplayData::MaxPitchHeight
@ MaxPitchHeight
Definition: NoteTrack.h:240
anonymous_namespace{NoteTrack.cpp}::IsValidVisibleChannels
bool IsValidVisibleChannels(const int nValue)
Definition: NoteTrack.cpp:889
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:866
NoteTrack::AddToDuration
void AddToDuration(double delta)
Definition: NoteTrack.cpp:709
NoteTrack::mSerializationLength
long mSerializationLength
Definition: NoteTrack.h:206
ImportExportPrefs.h
TimeWarper::Warp
virtual double Warp(double originalTime) const =0
NoteTrack::NearestBeatTime
QuantizedTimeAndBeat NearestBeatTime(double time) const
Definition: NoteTrack.cpp:677
theTheme
THEME_API Theme theTheme
Definition: Theme.cpp:79
XMLTagHandler
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:62
Track::Intervals
std::vector< Interval > Intervals
Definition: Track.h:329
SonifyBeginSerialize
#define SonifyBeginSerialize()
Definition: NoteTrack.h:290
NoteTrack::SoloVisibleChan
void SoloVisibleChan(int c)
Definition: NoteTrack.h:182
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
Track::TypeSwitch
R TypeSwitch(const Functions &...functions)
Use this function rather than testing track type explicitly and making down-casts.
Definition: Track.h:709
NoteTrackDisplayData::GetOctaveHeight
int GetOctaveHeight() const
Definition: NoteTrack.h:247
EnumSetting::ReadEnum
Enum ReadEnum() const
Definition: Prefs.h:373
NoteTrack::Trim
bool Trim(double t0, double t1)
Definition: NoteTrack.cpp:503
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:506
Track::SetName
void SetName(const wxString &n)
Definition: Track.cpp:82
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:239
NoteTrack::MinPitch
@ MinPitch
Definition: NoteTrack.h:222
Track::Notify
void Notify(int code=-1)
Definition: Track.cpp:286
NoteTrackDisplayData::GetNoteMargin
int GetNoteMargin() const
Definition: NoteTrack.h:246
_
#define _(s)
Definition: Internat.h:75
SonifyEndSerialize
#define SonifyEndSerialize()
Definition: NoteTrack.h:291
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
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:695
NoteTrack::PrintSequence
void PrintSequence()
Definition: NoteTrack.cpp:391
NoteTrackDisplayData::GetNotePos
int GetNotePos(int p) const
Definition: NoteTrack.h:264
NoteTrack::mVisibleChannels
int mVisibleChannels
Definition: NoteTrack.h:225
NoteTrackDisplayData::GetPitchHeight
int GetPitchHeight(int factor) const
Definition: NoteTrack.h:244
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1189
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:977
NoteTrack::GetSeq
Alg_seq & GetSeq() const
Definition: NoteTrack.cpp:145
Prefs.h
GetMIDIDeviceInfo
wxString GetMIDIDeviceInfo()
Definition: NoteTrack.cpp:1169
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:205
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:222
NoteTrack::mSeq
std::unique_ptr< Alg_seq > mSeq
Definition: NoteTrack.h:204
NoteTrack::SetNoteRange
void SetNoteRange(int note1, int note2)
Sets the top and bottom note (both pitches) automatically, swapping them if needed.
Definition: NoteTrack.cpp:999
NoteTrack
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:67
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:134
MIDIPlaybackDevice
StringSetting MIDIPlaybackDevice
Definition: NoteTrack.cpp:1273
NoteTrack::Shift
bool Shift(double t)
Definition: NoteTrack.cpp:650