/********************************************************************** Audacity: A Digital Audio Editor NoteTrack.h Dominic Mazzoni **********************************************************************/ #ifndef __AUDACITY_NOTETRACK__ #define __AUDACITY_NOTETRACK__ #include #include "Track.h" #if defined(USE_MIDI) // define this switch to play MIDI during redisplay to sonify run times // Note that if SONIFY is defined, the default MIDI device will be opened // and may block normal MIDI playback. //#define SONIFY 1 #ifdef SONIFY #define SONFNS(name) \ void Begin ## name(); \ void End ## name(); SONFNS(NoteBackground) SONFNS(NoteForeground) SONFNS(Measures) SONFNS(Serialize) SONFNS(Unserialize) SONFNS(ModifyState) SONFNS(AutoSave) #undef SONFNS #endif class wxDC; class wxRect; class Alg_seq; // from "allegro.h" using NoteTrackBase = #ifdef EXPERIMENTAL_MIDI_OUT PlayableTrack #else AudioTrack #endif ; using QuantizedTimeAndBeat = std::pair< double, double >; class StretchHandle; class TimeWarper; class AUDACITY_DLL_API NoteTrack final : public NoteTrackBase { public: NoteTrack(); virtual ~NoteTrack(); using Holder = std::shared_ptr; private: Track::Holder Clone() const override; public: double GetOffset() const override; double GetStartTime() const override; double GetEndTime() const override; Alg_seq &GetSeq() const; void WarpAndTransposeNotes(double t0, double t1, const TimeWarper &warper, double semitones); static void DrawLabelControls ( const NoteTrack *pTrack, wxDC & dc, const wxRect &rect, int highlightedChannel = -1 ); int FindChannel(const wxRect &rect, int mx, int my); bool LabelClick(const wxRect &rect, int x, int y, bool right); void SetSequence(std::unique_ptr &&seq); void PrintSequence(); Alg_seq *MakeExportableSeq(std::unique_ptr &cleanup) const; bool ExportMIDI(const wxString &f) const; bool ExportAllegro(const wxString &f) const; // High-level editing Track::Holder Cut (double t0, double t1) override; Track::Holder Copy (double t0, double t1, bool forClipboard = true) const override; bool Trim (double t0, double t1) /* not override */; void Clear(double t0, double t1) override; void Paste(double t, const Track *src) override; void Silence(double t0, double t1) override; void InsertSilence(double t, double len) override; bool Shift(double t) /* not override */; #ifdef EXPERIMENTAL_MIDI_OUT float GetVelocity() const { return mVelocity; } void SetVelocity(float velocity); #endif QuantizedTimeAndBeat NearestBeatTime( double time ) const; bool StretchRegion ( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur ); /// Gets the current bottom note (a pitch) int GetBottomNote() const { return mBottomNote; } /// Gets the current top note (a pitch) int GetTopNote() const { return mTopNote; } /// Sets the bottom note (a pitch), making sure that it is never greater than the top note. void SetBottomNote(int note); /// Sets the top note (a pitch), making sure that it is never less than the bottom note. void SetTopNote(int note); /// Sets the top and bottom note (both pitches) automatically, swapping them if needed. void SetNoteRange(int note1, int note2); /// Zooms so that all notes are visible void ZoomAllNotes(); /// Zooms so that the entire track is visible void ZoomMaxExtent() { SetNoteRange(MinPitch, MaxPitch); } /// Shifts all notes vertically by the given pitch void ShiftNoteRange(int offset); /// Zooms out a constant factor (subject to zoom limits) void ZoomOut(const wxRect &rect, int y) { Zoom(rect, y, 1.0f / ZoomStep, true); } /// Zooms in a constant factor (subject to zoom limits) void ZoomIn(const wxRect &rect, int y) { Zoom(rect, y, ZoomStep, true); } /// Zoom the note track around y. /// If center is true, the result will be centered at y. void Zoom(const wxRect &rect, int y, float multiplier, bool center); void ZoomTo(const wxRect &rect, int start, int end); #if 0 // Vertical scrolling is performed by dragging the keyboard at // left of track. Protocol is call StartVScroll, then update by // calling VScroll with original and final mouse position. // These functions are not used -- instead, zooming/dragging works like // audio track zooming/dragging. The vertical scrolling is nice however, // so I left these functions here for possible use in the future. void StartVScroll(); void VScroll(int start, int end); #endif bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override; XMLTagHandler *HandleXMLChild(const wxChar *tag) override; void WriteXML(XMLWriter &xmlFile) const override; // channels are numbered as integers 0-15, visible channels // (mVisibleChannels) is a bit set. Channels are displayed as // integers 1-16. // Allegro's data structure does not restrict channels to 16. // Since there is not way to select more than 16 channels, // map all channel numbers mod 16. This will have no effect // on MIDI files, but it will allow users to at least select // all channels on non-MIDI event sequence data. #define NUM_CHANNELS 16 // Bitmask with all NUM_CHANNELS bits set #define ALL_CHANNELS (1 << NUM_CHANNELS) - 1 #define CHANNEL_BIT(c) (1 << (c % NUM_CHANNELS)) bool IsVisibleChan(int c) const { return (mVisibleChannels & CHANNEL_BIT(c)) != 0; } void SetVisibleChan(int c) { mVisibleChannels |= CHANNEL_BIT(c); } void ClearVisibleChan(int c) { mVisibleChannels &= ~CHANNEL_BIT(c); } void ToggleVisibleChan(int c) { mVisibleChannels ^= CHANNEL_BIT(c); } // Solos the given channel. If it's the only channel visible, all channels // are enabled; otherwise, it is set to the only visible channel. void SoloVisibleChan(int c) { if (mVisibleChannels == CHANNEL_BIT(c)) mVisibleChannels = ALL_CHANNELS; else mVisibleChannels = CHANNEL_BIT(c); } Track::Holder PasteInto( AudacityProject & ) const override; ConstIntervals GetIntervals() const override; Intervals GetIntervals() override; private: TrackKind GetKind() const override { return TrackKind::Note; } void AddToDuration( double delta ); // These are mutable to allow NoteTrack to switch details of representation // in logically const methods // At most one of the two pointers is not null at any time. // Both are null in a newly constructed NoteTrack. mutable std::unique_ptr mSeq; mutable std::unique_ptr mSerializationBuffer; mutable long mSerializationLength; #ifdef EXPERIMENTAL_MIDI_OUT float mVelocity; // velocity offset #endif int mBottomNote, mTopNote; #if 0 // Also unused from vertical scrolling int mStartBottomNote; #endif // Remember continuous variation for zooming, // but it is rounded off whenever drawing: float mPitchHeight; enum { MinPitch = 0, MaxPitch = 127 }; static const float ZoomStep; int mVisibleChannels; // bit set of visible channels std::weak_ptr mStretchHandle; }; /// Data used to display a note track class NoteTrackDisplayData { private: float mPitchHeight; // mBottom is the Y offset of pitch 0 (normally off screen) // Used so that mBottomNote is located at // mY + mHeight - (GetNoteMargin() + 1 + GetPitchHeight()) int mBottom; int mMargin; enum { MinPitchHeight = 1, MaxPitchHeight = 25 }; public: NoteTrackDisplayData(const NoteTrack* track, const wxRect &r); int GetPitchHeight(int factor) const { return std::max(1, (int)(factor * mPitchHeight)); } int GetNoteMargin() const { return mMargin; }; int GetOctaveHeight() const { return GetPitchHeight(12) + 2; } // IPitchToY returns Y coordinate of top of pitch p int IPitchToY(int p) const; // compute the window coordinate of the bottom of an octave: This is // the bottom of the line separating B and C. int GetOctaveBottom(int oct) const { return IPitchToY(oct * 12) + GetPitchHeight(1) + 1; } // Y coordinate for given floating point pitch (rounded to int) int PitchToY(double p) const { return IPitchToY((int) (p + 0.5)); } // Integer pitch corresponding to a Y coordinate int YToIPitch(int y) const; // map pitch class number (0-11) to pixel offset from bottom of octave // (the bottom of the black line between B and C) to the top of the // note. Note extra pixel separates B(11)/C(0) and E(4)/F(5). int GetNotePos(int p) const { return 1 + GetPitchHeight(p + 1) + (p > 4); } // get pixel offset to top of ith black key note int GetBlackPos(int i) const { return GetNotePos(i * 2 + 1 + (i > 1)); } // GetWhitePos tells where to draw lines between keys as an offset from // GetOctaveBottom. GetWhitePos(0) returns 1, which matches the location // of the line separating B and C int GetWhitePos(int i) const { return 1 + (i * GetOctaveHeight()) / 7; } }; #endif // USE_MIDI #ifndef SONIFY // no-ops: #define SonifyBeginSonification() #define SonifyEndSonification() #define SonifyBeginNoteBackground() #define SonifyEndNoteBackground() #define SonifyBeginNoteForeground() #define SonifyEndNoteForeground() #define SonifyBeginMeasures() #define SonifyEndMeasures() #define SonifyBeginSerialize() #define SonifyEndSerialize() #define SonifyBeginUnserialize() #define SonifyEndUnserialize() #define SonifyBeginAutoSave() #define SonifyEndAutoSave() #define SonifyBeginModifyState() #define SonifyEndModifyState() #endif #endif