1 /********************************************************************** 2 3 Audacity: A Digital Audio Editor 4 5 NoteTrack.h 6 7 Dominic Mazzoni 8 9 **********************************************************************/ 10 11 #ifndef __AUDACITY_NOTETRACK__ 12 #define __AUDACITY_NOTETRACK__ 13 14 15 16 17 18 #include <utility> 19 #include "Prefs.h" 20 #include "Track.h" 21 22 #if defined(USE_MIDI) 23 24 // define this switch to play MIDI during redisplay to sonify run times 25 // Note that if SONIFY is defined, the default MIDI device will be opened 26 // and may block normal MIDI playback. 27 //#define SONIFY 1 28 29 #ifdef SONIFY 30 31 #define SONFNS(name) \ 32 void Begin ## name(); \ 33 void End ## name(); 34 35 SONFNS(NoteBackground) 36 SONFNS(NoteForeground) 37 SONFNS(Measures) 38 SONFNS(Serialize) 39 SONFNS(Unserialize) 40 SONFNS(ModifyState) 41 SONFNS(AutoSave) 42 43 #undef SONFNS 44 45 #endif 46 47 class wxDC; 48 class wxRect; 49 50 class Alg_seq; // from "allegro.h" 51 52 using NoteTrackBase = 53 #ifdef EXPERIMENTAL_MIDI_OUT 54 PlayableTrack 55 #else 56 AudioTrack 57 #endif 58 ; 59 60 using QuantizedTimeAndBeat = std::pair< double, double >; 61 62 class StretchHandle; 63 class TimeWarper; 64 65 class AUDACITY_DLL_API NoteTrack final 66 : public NoteTrackBase 67 { 68 public: 69 NoteTrack(); 70 virtual ~NoteTrack(); 71 72 using Holder = std::shared_ptr<NoteTrack>; 73 74 private: 75 Track::Holder Clone() const override; 76 77 public: 78 double GetOffset() const override; 79 double GetStartTime() const override; 80 double GetEndTime() const override; 81 82 Alg_seq &GetSeq() const; 83 84 void WarpAndTransposeNotes(double t0, double t1, 85 const TimeWarper &warper, double semitones); 86 87 static void DrawLabelControls 88 ( const NoteTrack *pTrack, wxDC & dc, const wxRect &rect, 89 int highlightedChannel = -1 ); 90 int FindChannel(const wxRect &rect, int mx, int my); 91 bool LabelClick(const wxRect &rect, int x, int y, bool right); 92 93 void SetSequence(std::unique_ptr<Alg_seq> &&seq); 94 void PrintSequence(); 95 96 Alg_seq *MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const; 97 bool ExportMIDI(const wxString &f) const; 98 bool ExportAllegro(const wxString &f) const; 99 100 // High-level editing 101 Track::Holder Cut (double t0, double t1) override; 102 Track::Holder Copy (double t0, double t1, bool forClipboard = true) const override; 103 bool Trim (double t0, double t1) /* not override */; 104 void Clear(double t0, double t1) override; 105 void Paste(double t, const Track *src) override; 106 void Silence(double t0, double t1) override; 107 void InsertSilence(double t, double len) override; 108 bool Shift(double t) /* not override */; 109 110 #ifdef EXPERIMENTAL_MIDI_OUT GetVelocity()111 float GetVelocity() const { return mVelocity; } 112 void SetVelocity(float velocity); 113 #endif 114 115 QuantizedTimeAndBeat NearestBeatTime( double time ) const; 116 bool StretchRegion 117 ( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur ); 118 119 /// Gets the current bottom note (a pitch) GetBottomNote()120 int GetBottomNote() const { return mBottomNote; } 121 /// Gets the current top note (a pitch) GetTopNote()122 int GetTopNote() const { return mTopNote; } 123 /// Sets the bottom note (a pitch), making sure that it is never greater than the top note. 124 void SetBottomNote(int note); 125 /// Sets the top note (a pitch), making sure that it is never less than the bottom note. 126 void SetTopNote(int note); 127 /// Sets the top and bottom note (both pitches) automatically, swapping them if needed. 128 void SetNoteRange(int note1, int note2); 129 130 /// Zooms so that all notes are visible 131 void ZoomAllNotes(); 132 /// Zooms so that the entire track is visible ZoomMaxExtent()133 void ZoomMaxExtent() { SetNoteRange(MinPitch, MaxPitch); } 134 /// Shifts all notes vertically by the given pitch 135 void ShiftNoteRange(int offset); 136 137 /// Zooms out a constant factor (subject to zoom limits) ZoomOut(const wxRect & rect,int y)138 void ZoomOut(const wxRect &rect, int y) { Zoom(rect, y, 1.0f / ZoomStep, true); } 139 /// Zooms in a constant factor (subject to zoom limits) ZoomIn(const wxRect & rect,int y)140 void ZoomIn(const wxRect &rect, int y) { Zoom(rect, y, ZoomStep, true); } 141 /// Zoom the note track around y. 142 /// If center is true, the result will be centered at y. 143 void Zoom(const wxRect &rect, int y, float multiplier, bool center); 144 void ZoomTo(const wxRect &rect, int start, int end); 145 146 #if 0 147 // Vertical scrolling is performed by dragging the keyboard at 148 // left of track. Protocol is call StartVScroll, then update by 149 // calling VScroll with original and final mouse position. 150 // These functions are not used -- instead, zooming/dragging works like 151 // audio track zooming/dragging. The vertical scrolling is nice however, 152 // so I left these functions here for possible use in the future. 153 void StartVScroll(); 154 void VScroll(int start, int end); 155 #endif 156 157 bool HandleXMLTag(const std::string_view& tag, const AttributesList& attrs) override; 158 XMLTagHandler *HandleXMLChild(const std::string_view& tag) override; 159 void WriteXML(XMLWriter &xmlFile) const override; 160 161 // channels are numbered as integers 0-15, visible channels 162 // (mVisibleChannels) is a bit set. Channels are displayed as 163 // integers 1-16. 164 165 // Allegro's data structure does not restrict channels to 16. 166 // Since there is not way to select more than 16 channels, 167 // map all channel numbers mod 16. This will have no effect 168 // on MIDI files, but it will allow users to at least select 169 // all channels on non-MIDI event sequence data. 170 #define NUM_CHANNELS 16 171 // Bitmask with all NUM_CHANNELS bits set 172 #define ALL_CHANNELS (1 << NUM_CHANNELS) - 1 173 #define CHANNEL_BIT(c) (1 << (c % NUM_CHANNELS)) IsVisibleChan(int c)174 bool IsVisibleChan(int c) const { 175 return (mVisibleChannels & CHANNEL_BIT(c)) != 0; 176 } SetVisibleChan(int c)177 void SetVisibleChan(int c) { mVisibleChannels |= CHANNEL_BIT(c); } ClearVisibleChan(int c)178 void ClearVisibleChan(int c) { mVisibleChannels &= ~CHANNEL_BIT(c); } ToggleVisibleChan(int c)179 void ToggleVisibleChan(int c) { mVisibleChannels ^= CHANNEL_BIT(c); } 180 // Solos the given channel. If it's the only channel visible, all channels 181 // are enabled; otherwise, it is set to the only visible channel. SoloVisibleChan(int c)182 void SoloVisibleChan(int c) { 183 if (mVisibleChannels == CHANNEL_BIT(c)) 184 mVisibleChannels = ALL_CHANNELS; 185 else 186 mVisibleChannels = CHANNEL_BIT(c); 187 } 188 189 Track::Holder PasteInto( AudacityProject & ) const override; 190 191 ConstIntervals GetIntervals() const override; 192 Intervals GetIntervals() override; 193 194 private: 195 GetKind()196 TrackKind GetKind() const override { return TrackKind::Note; } 197 198 void AddToDuration( double delta ); 199 200 // These are mutable to allow NoteTrack to switch details of representation 201 // in logically const methods 202 // At most one of the two pointers is not null at any time. 203 // Both are null in a newly constructed NoteTrack. 204 mutable std::unique_ptr<Alg_seq> mSeq; 205 mutable std::unique_ptr<char[]> mSerializationBuffer; 206 mutable long mSerializationLength; 207 208 #ifdef EXPERIMENTAL_MIDI_OUT 209 float mVelocity; // velocity offset 210 #endif 211 212 int mBottomNote, mTopNote; 213 #if 0 214 // Also unused from vertical scrolling 215 int mStartBottomNote; 216 #endif 217 218 // Remember continuous variation for zooming, 219 // but it is rounded off whenever drawing: 220 float mPitchHeight; 221 222 enum { MinPitch = 0, MaxPitch = 127 }; 223 static const float ZoomStep; 224 225 int mVisibleChannels; // bit set of visible channels 226 227 std::weak_ptr<StretchHandle> mStretchHandle; 228 }; 229 230 /// Data used to display a note track 231 class NoteTrackDisplayData { 232 private: 233 float mPitchHeight; 234 // mBottom is the Y offset of pitch 0 (normally off screen) 235 // Used so that mBottomNote is located at 236 // mY + mHeight - (GetNoteMargin() + 1 + GetPitchHeight()) 237 int mBottom; 238 int mMargin; 239 240 enum { MinPitchHeight = 1, MaxPitchHeight = 25 }; 241 public: 242 NoteTrackDisplayData(const NoteTrack* track, const wxRect &r); 243 GetPitchHeight(int factor)244 int GetPitchHeight(int factor) const 245 { return std::max(1, (int)(factor * mPitchHeight)); } GetNoteMargin()246 int GetNoteMargin() const { return mMargin; }; GetOctaveHeight()247 int GetOctaveHeight() const { return GetPitchHeight(12) + 2; } 248 // IPitchToY returns Y coordinate of top of pitch p 249 int IPitchToY(int p) const; 250 // compute the window coordinate of the bottom of an octave: This is 251 // the bottom of the line separating B and C. GetOctaveBottom(int oct)252 int GetOctaveBottom(int oct) const { 253 return IPitchToY(oct * 12) + GetPitchHeight(1) + 1; 254 } 255 // Y coordinate for given floating point pitch (rounded to int) PitchToY(double p)256 int PitchToY(double p) const { 257 return IPitchToY((int) (p + 0.5)); 258 } 259 // Integer pitch corresponding to a Y coordinate 260 int YToIPitch(int y) const; 261 // map pitch class number (0-11) to pixel offset from bottom of octave 262 // (the bottom of the black line between B and C) to the top of the 263 // note. Note extra pixel separates B(11)/C(0) and E(4)/F(5). GetNotePos(int p)264 int GetNotePos(int p) const 265 { return 1 + GetPitchHeight(p + 1) + (p > 4); } 266 // get pixel offset to top of ith black key note GetBlackPos(int i)267 int GetBlackPos(int i) const { return GetNotePos(i * 2 + 1 + (i > 1)); } 268 // GetWhitePos tells where to draw lines between keys as an offset from 269 // GetOctaveBottom. GetWhitePos(0) returns 1, which matches the location 270 // of the line separating B and C GetWhitePos(int i)271 int GetWhitePos(int i) const { return 1 + (i * GetOctaveHeight()) / 7; } 272 }; 273 274 extern AUDACITY_DLL_API StringSetting MIDIPlaybackDevice; 275 extern AUDACITY_DLL_API StringSetting MIDIRecordingDevice; 276 extern AUDACITY_DLL_API IntSetting MIDISynthLatency_ms; 277 278 #endif // USE_MIDI 279 280 #ifndef SONIFY 281 // no-ops: 282 #define SonifyBeginSonification() 283 #define SonifyEndSonification() 284 #define SonifyBeginNoteBackground() 285 #define SonifyEndNoteBackground() 286 #define SonifyBeginNoteForeground() 287 #define SonifyEndNoteForeground() 288 #define SonifyBeginMeasures() 289 #define SonifyEndMeasures() 290 #define SonifyBeginSerialize() 291 #define SonifyEndSerialize() 292 #define SonifyBeginUnserialize() 293 #define SonifyEndUnserialize() 294 #define SonifyBeginAutoSave() 295 #define SonifyEndAutoSave() 296 #define SonifyBeginModifyState() 297 #define SonifyEndModifyState() 298 #endif 299 300 301 AUDACITY_DLL_API wxString GetMIDIDeviceInfo(); 302 303 #endif 304