#ifndef XTC_SEQUENCER_WIDGET_HPP #define XTC_SEQUENCER_WIDGET_HPP #include #include #include "xtc/xtc_plugin.h" #include "xtc/xtc_editing.h" #include #include "xtc/xtc_audio.h" #include #include #include class SequencerWidget : public Fl_Group { public: /** * @brief Prepares a model edit using the widget's opaque callback context. * * @return XTC_OK to allow the edit, or an error to cancel it. */ using LoopSelect = xtc_result (*)(void *,uint64_t,uint64_t); LoopSelect loop_select = nullptr; uint64_t new_clip_ticks = 3840; int header_width() const {return header_width_;} void header_width(int width); int header_left() const; int zoom_level() const { return scale_; } xtc_result set_zoom(int scale); void loop_region(uint64_t start,uint64_t end,bool enabled) { loop_begin_=start; loop_end_=end; loop_enabled_=enabled; redraw(); } using OpenClip = void (*)(void *); using InstrumentSelect = void (*)(void *, uint64_t, const xtc_plugin_spec *, bool); OpenClip open_clip = nullptr; InstrumentSelect instrument_select = nullptr; Fl_Choice *instrument_choice(uint64_t track) const; void refresh_instruments(); bool select(uint64_t track, uint64_t clip = UINT64_MAX); xtc_result merge_midi(); xtc_result duplicate_track(const char *name); void duplicate_track_dialog(); xtc_result ripple_time(uint64_t length,bool remove); void ripple_dialog(bool remove); xtc_result marker(const char *name); /* NULL removes at cursor. */ void marker_dialog(); bool toggle_clip(uint64_t clip); void select_all_clips(); bool clip_selected(uint64_t clip) const; std::vector selected_clips() const; xtc_result transform_selection(xtc_clip_operation operation,int64_t amount); xtc_result transform_midi(const xtc_midi_transform &edit); void swing_dialog(); void gate_dialog(); void scale_dialog(); void velocity_dialog(); void humanize_dialog(); xtc_result make_chord(const std::vector &intervals); void chord_dialog(); xtc_result arpeggiate(uint64_t step,unsigned int gate,bool descending); void arpeggio_dialog(); void fades_dialog(); xtc_result repeat_selection(unsigned int copies); void repeat_dialog(); xtc_result copy_selection(); xtc_result paste_selection(); xtc_result nudge_selection(int direction); xtc_result duplicate_selection(); xtc_result split_selection(); xtc_result audio_properties(float gain, uint64_t fade_in, uint64_t fade_out); void audio_properties_dialog(); void refresh_waveforms(); size_t waveform_count() const { return waves_.size(); } xtc_result rename_selection(const char *name); xtc_result move_track(int direction); xtc_result grid(uint64_t ticks); uint64_t grid() const { return grid_; } using BeforeEdit = xtc_result (*)(void *); BeforeEdit before_edit = nullptr; /** * @brief Returns the selected track identifier, or UINT64_MAX when none is selected. * @return Stable track identifier, or UINT64_MAX if no track is selected. */ uint64_t selected_track() const { return track_; } /** * @brief Returns the selected clip identifier, or UINT64_MAX when none is selected. * @return Stable clip identifier, or UINT64_MAX if no clip is selected. */ uint64_t selected_clip() const { return clip_; } /** * @brief Requests a transport seek using an opaque context and musical tick. * * @return XTC_OK on success, or a transport error. */ using Seek = xtc_result (*)(void *, uint64_t); /** * @brief Constructs the timeline widget with a borrowed project and seek callback. * * @param x Left coordinate in pixels. * @param y Top coordinate in pixels. * @param project Borrowed project; must outlive the widget's use of it. * @param seek Callback used to request a transport seek. * @param context Opaque callback context. */ SequencerWidget(int x, int y, xtc_project *project, Seek seek, void *context); /** * @brief Replaces the borrowed project and refreshes the widget's displayed state. * * @param project Borrowed project; must outlive the widget's use of it. * @param reset Whether to clear selection, cursor, and drag state. */ void set_project(xtc_project *project, bool reset = true); /** * @brief Returns the timeline cursor position in musical ticks. * @return Cursor position in musical ticks. */ uint64_t position() const { return cursor_; } /** * @brief Moves the timeline cursor to the supplied musical tick and requests a redraw. * * @param tick Musical position in ticks. */ void position(uint64_t tick) { cursor_ = tick; redraw(); } /** * @brief Creates and selects a track through the arrangement edit hook. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result add_track(); /** * @brief Creates and selects a one-bar silent region at the timeline cursor. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result add_region(); /** * @brief Creates and selects a one-bar MIDI clip at the timeline cursor. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result add_midi_clip(); /** * @brief Removes the selected clip, or the selected track when no clip is selected. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result delete_selection(); /** * @brief Adjusts timeline zoom within its supported bounds and updates the scroll extent. * * @param direction Zoom direction; positive zooms in, nonpositive zooms out. */ void zoom(int direction); /** * @brief Returns the borrowed project currently displayed by the widget. * @return Borrowed project pointer. */ xtc_project *project() const { return project_; } /** * @brief Handles FLTK input events and delegates unhandled events to the base widget. * * @param event FLTK event code. * @return Nonzero if the event was handled, zero otherwise. */ int handle(int event) override; protected: /** * @brief Draws the widget's current model, selection, and interaction feedback. */ void draw() override; private: struct TrackChoice { Fl_Choice *widget = nullptr; uint64_t track = UINT64_MAX; }; std::array track_choices_{}; std::vector catalog_; static void instrument_callback(Fl_Widget *, void *); struct Wave { std::string path; std::unique_ptr data{nullptr, xtc_waveform_destroy}; }; std::vector waves_; std::vector selection_; std::unique_ptr clipboard_{nullptr,xtc_clipboard_destroy}; void draw_waveform(const xtc_clip_info &clip, int left, int top); xtc_project *project_; Seek seek_; void *context_; uint64_t track_ = UINT64_MAX, clip_ = UINT64_MAX, cursor_ = 0, grid_ = 240; uint64_t loop_begin_=0,loop_end_=3840,loop_anchor_=0,loop_preview_=0; bool loop_enabled_=false; int scale_ = 48, drag_x_ = 0, mode_ = 0; int header_width_=180,header_drag_x_=0,header_drag_original_=180; xtc_clip_info original_{}; /** * @brief Converts a screen x coordinate to a bounded timeline tick snapped to the selected grid. * * @param px Screen x coordinate in pixels. * @return Snapped tick within the supported project range. */ uint64_t tick(int px) const; /** * @brief Reports an edit result, refreshes the widget, and notifies listeners on success. * * @param result Result of the attempted operation. */ void changed(xtc_result result); /** * @brief Resizes the timeline's scrollable area to fit its tracks and clip ends. */ void extent(); }; #endif