xtc/src/gui/mixer_widget.hpp

202 lines
6.9 KiB
C++

#ifndef XTC_MIXER_WIDGET_HPP
#define XTC_MIXER_WIDGET_HPP
#include "xtc/xtc_mixer.h"
#include "xtc/xtc_backend.h"
#include <FL/Fl_Group.H>
#include <chrono>
#include <vector>
class Fl_Choice;
class Fl_Button;
class Fl_Value_Slider;
class Fl_Check_Button;
class Fl_Progress;
class Fl_Value_Input;
class AutomationLane;
class MixerStrip;
class Fl_Scroll;
class Fl_Box;
class MixerWidget : 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 BeforeEdit = xtc_result (*)(void *);
/**
* @brief Creates the mixer panel and its child controls.
*
* Builds the track chooser, channel strips, pan knobs, mute/solo buttons, output
* meters, automation controls, and lane, then displays the given project.
*
* @param x Left edge in parent coordinates.
* @param y Top edge in parent coordinates.
* @param w Widget width in pixels.
* @param h Widget height in pixels.
* @param p Project to display; borrowed and must outlive its use.
* @param before Callback invoked before each edit; may be nullptr.
* @param context Opaque pointer forwarded to before.
*/
MixerWidget(int x, int y, int w, int h, xtc_project * p, BeforeEdit before, void * context);
/**
* @brief Displays a different project and refreshes the controls.
*
* The current track selection is preserved only when the same project
* pointer is passed again. The project is borrowed and must outlive use.
*
* @param p Project to display; must remain valid while shown.
*/
void set_project(xtc_project * p);
/**
* @brief Updates the left and right output meters.
*
* Meter values are clamped to 1.0, while the over-range color is chosen
* from the unclamped level.
*
* @param left Left-channel peak level.
* @param right Right-channel peak level.
*/
void peaks(float left, float right);
/**
* @brief Returns the ID of the track selected in the track chooser.
*
* @return Selected track ID, or UINT64_MAX when the chooser selection does
* not resolve to a project track.
*/
uint64_t selected_track() const;
/**
* @brief Returns the project currently displayed by the widget.
*
* @return Borrowed project pointer, possibly nullptr; the widget does not
* own it.
*/
xtc_project *project() const
{
return project_;
}
/**
* @brief Returns the automation parameter selected in the chooser.
*
* @return XTC_AUTOMATION_PAN when the chooser selects "Pan", otherwise
* XTC_AUTOMATION_GAIN.
*/
xtc_automation_parameter parameter() const;
/**
* @brief Returns the first tick shown by the automation lane.
*
* @return Clamped first tick in project ticks, or 0 when the input value is
* not finite.
*/
uint64_t lane_start() const;
/**
* @brief Adds, replaces, or removes an automation point.
*
* The constructor's before-edit callback runs first; the edit is skipped
* if it returns an error. On success the widget callback is invoked and
* the controls are refreshed.
*
* @param tick Automation tick to modify; must be within the project range.
* @param value New value; ignored when remove is true.
* @param remove When true, removes the point instead of setting it.
* @return XTC_OK on success, or the before-edit or project mutation error.
*/
xtc_result point(uint64_t tick, float value, bool remove = false);
/**
* @brief Applies gain, pan, mute, and solo to the selected track.
*
* The constructor's before-edit callback runs first; the edit is skipped
* if it returns an error. On success the widget callback is invoked and
* the controls are refreshed.
*
* @param gain Linear gain in the range 0..4.
* @param pan Pan position in the range -1 (left)..1 (right).
* @param mute Mute state: 0 or 1.
* @param solo Solo state: 0 or 1.
* @return XTC_OK on success, or the before-edit or project mutation error.
*/
xtc_result add_bus();
xtc_result route(uint64_t bus);
bool select_track(uint64_t id);
void track_peaks(float left, float right);
/* One control-thread snapshot updates all strips; missing taps decay to silence. */
void meters(const xtc_meter_snapshot &snapshot);
Fl_Group *strip(uint64_t id) const;
size_t strip_count() const { return strips_.size(); }
void resize(int x, int y, int w, int h) override;
void reset_meters();
bool master_clipped() const { return master_[0].clipped || master_[1].clipped; }
xtc_result mix(float gain, float pan, int mute, int solo);
/**
* @brief Handles keyboard shortcuts and forwards other events.
*
* M and S toggle mute and solo, Left/Right adjust gain (Shift adjusts pan),
* and Up/Down change the selected track. Modified key presses fall through.
*
* @param event FLTK event code.
* @return 1 when the event was consumed, otherwise the Fl_Group result.
*/
int handle(int event) override;
private:
xtc_project *project_;
BeforeEdit before_;
void *context_;
Fl_Choice *tracks_, *parameter_, *routes_, *mode_;
std::vector<uint64_t> route_ids_;
Fl_Progress *left_, *right_;
Fl_Scroll *bank_;
Fl_Group *master_panel_;
Fl_Button *bus_, *reset_;
Fl_Box *hint_, *master_readout_;
std::vector<MixerStrip *> strips_;
void sync_strips(bool reset = false);
void layout();
xtc_peak_meter master_[2]{};
uint64_t selected_id_ = UINT64_MAX;
std::chrono::steady_clock::time_point peak_time_ = std::chrono::steady_clock::now();
double meter_elapsed_ = 0.1;
void display_peaks(Fl_Progress *left, Fl_Progress *right, xtc_peak_meter *meters, const char *prefix);
Fl_Value_Input *start_;
AutomationLane *lane_;
/**
* @brief Reports an edit result and refreshes the controls.
*
* A successful result invokes the widget callback so the owner can mark
* the project dirty; a failure shows a modal alert. The controls are
* resynchronized in both cases.
*
* @param r Result of the attempted edit.
* @return The same result passed in.
*/
xtc_result changed(xtc_result r);
/**
* @brief Synchronizes the child controls with the selected track.
*
* Reconcile strip controls with stable model IDs and the selected
* automation target.
*/
void selection();
/**
* @brief FLTK callback for the track, parameter, and first-tick controls.
*
* @param widget Track chooser or automation control that changed.
* @param c Opaque MixerWidget pointer supplied as callback context.
*/
static void select(Fl_Widget * widget, void * c);
};
#endif