46 lines
4.8 KiB
C++
46 lines
4.8 KiB
C++
|
|
#include "sample_editor.hpp"
|
||
|
|
#include "theme.hpp"
|
||
|
|
#include <FL/Fl.H>
|
||
|
|
#include <FL/Fl_Value_Input.H>
|
||
|
|
#include <FL/Fl_Button.H>
|
||
|
|
#include <FL/Fl_Box.H>
|
||
|
|
#include <FL/fl_draw.H>
|
||
|
|
#include <algorithm>
|
||
|
|
#include <cmath>
|
||
|
|
#include <cstdio>
|
||
|
|
class SampleCanvas : public Fl_Widget {
|
||
|
|
SampleEditor &editor_;size_t anchor_=0;
|
||
|
|
size_t frame(int px) const {size_t width=std::max(size_t(1),editor_.frames()/editor_.zoom());return std::min(editor_.frames()-1,editor_.origin()+static_cast<size_t>(std::max(0,std::min(w()-1,px-x())))*width/static_cast<size_t>(w()));}
|
||
|
|
public:
|
||
|
|
SampleCanvas(int x,int y,int w,int h,SampleEditor &e):Fl_Widget(x,y,w,h),editor_(e){}
|
||
|
|
int handle(int event) override {
|
||
|
|
if(event==FL_PUSH && Fl::event_button()==FL_LEFT_MOUSE){anchor_=frame(Fl::event_x());editor_.select(anchor_,anchor_+1);return 1;}
|
||
|
|
if(event==FL_DRAG){size_t f=frame(Fl::event_x());editor_.select(std::min(f,anchor_),std::max(f,anchor_)+1);return 1;}
|
||
|
|
return Fl_Widget::handle(event);
|
||
|
|
}
|
||
|
|
void draw() override {
|
||
|
|
fl_push_clip(x(),y(),w(),h());fl_color(xtc_ui::background());fl_rectf(x(),y(),w(),h());
|
||
|
|
size_t width=std::max(size_t(1),editor_.frames()/editor_.zoom());
|
||
|
|
for(int px=0;px<w();++px){size_t b=editor_.origin()+static_cast<size_t>(px)*width/static_cast<size_t>(w());size_t e=std::min(editor_.frames(),editor_.origin()+static_cast<size_t>(px+1)*width/static_cast<size_t>(w()));if(b>=editor_.frames())break;if(e<=b)e=b+1;
|
||
|
|
if(b<editor_.end() && e>editor_.begin()){fl_color(fl_rgb_color(45,76,80));fl_rectf(x()+px,y(),1,h());}
|
||
|
|
xtc_sample_stats stats{};if(xtc_sample_analyze(editor_.sample(),b,e,&stats)==XTC_OK){fl_color(fl_rgb_color(95,204,182));int a=static_cast<int>(std::max(-1.f,std::min(1.f,stats.maximum))*(h()/2-8));int z=static_cast<int>(std::max(-1.f,std::min(1.f,stats.minimum))*(h()/2-8));fl_line(x()+px,y()+h()/2-a,x()+px,y()+h()/2-z);}
|
||
|
|
}
|
||
|
|
fl_color(xtc_ui::border());fl_line(x(),y()+h()/2,x()+w(),y()+h()/2);fl_pop_clip();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
SampleEditor::SampleEditor(xtc_sample *sample):Fl_Double_Window(1100,650,"xtc — Sample editor"),sample_(sample,xtc_sample_destroy)
|
||
|
|
{
|
||
|
|
xtc_sample_info(sample_.get(),&rate_,&frames_);end_=frames_;set_modal();Fl_Group::begin();
|
||
|
|
auto *hint=new Fl_Box(16,12,1068,34,"Source frames / mono downmix — drag a range; numeric endpoints are exclusive. Original file unchanged.");hint->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
|
||
|
|
begin_input_=new Fl_Value_Input(90,58,140,28,"Start frame");end_input_=new Fl_Value_Input(330,58,140,28,"End frame");origin_input_=new Fl_Value_Input(580,58,140,28,"View start");
|
||
|
|
for(auto *input:{begin_input_,end_input_,origin_input_}){input->step(1);input->bounds(0,static_cast<double>(frames_));input->when(FL_WHEN_RELEASE);input->callback([](Fl_Widget *,void *c){auto *e=static_cast<SampleEditor *>(c);double b=e->begin_input_->value(),z=e->end_input_->value(),o=e->origin_input_->value();if(std::isfinite(b)&&std::isfinite(z)&&std::isfinite(o)&&b>=0&&z>=0&&o>=0&&b<=e->frames_&&z<=e->frames_&&o<e->frames_){e->select(static_cast<size_t>(b),static_cast<size_t>(z));e->view(static_cast<size_t>(o),e->zoom_);}e->refresh();},this);}
|
||
|
|
auto *plus=new Fl_Button(750,58,100,28,"Zoom in");plus->callback([](Fl_Widget *,void *c){auto *e=static_cast<SampleEditor *>(c);e->view(e->begin_,std::min(64U,e->zoom_*2));},this);
|
||
|
|
auto *minus=new Fl_Button(860,58,100,28,"Zoom out");minus->callback([](Fl_Widget *,void *c){auto *e=static_cast<SampleEditor *>(c);e->view(e->origin_,std::max(1U,e->zoom_/2));},this);
|
||
|
|
canvas_=new SampleCanvas(16,104,1068,340,*this);stats_=new Fl_Box(16,452,1068,50);stats_->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
|
||
|
|
auto *close=new Fl_Button(970,604,110,30,"Close");close->callback([](Fl_Widget *,void *c){static_cast<SampleEditor *>(c)->hide();},this);callback([](Fl_Widget *,void *c){static_cast<SampleEditor *>(c)->hide();},this);
|
||
|
|
Fl_Group::end();xtc_ui::style(this);refresh();
|
||
|
|
}
|
||
|
|
xtc_result SampleEditor::select(size_t b,size_t e){if(b>=e||e>frames_)return XTC_ERR_INVALID_ARGUMENT;begin_=b;end_=e;refresh();return XTC_OK;}
|
||
|
|
xtc_result SampleEditor::view(size_t b,unsigned int zoom){if(b>=frames_||!zoom||zoom>64)return XTC_ERR_INVALID_ARGUMENT;origin_=b;zoom_=zoom;refresh();return XTC_OK;}
|
||
|
|
void SampleEditor::refresh(){begin_input_->value(static_cast<double>(begin_));end_input_->value(static_cast<double>(end_));origin_input_->value(static_cast<double>(origin_));xtc_sample_stats s{};xtc_sample_analyze(sample_.get(),begin_,end_,&s);char text[256];std::snprintf(text,sizeof(text),"%u Hz / %zu source frames / selection %zu..%zu (%0.6f s) / zoom %ux\nMin %.6f Max %.6f RMS %.6f DC %.6f",rate_,frames_,begin_,end_,static_cast<double>(end_-begin_)/rate_,zoom_,s.minimum,s.maximum,s.rms,s.mean);stats_->copy_label(text);canvas_->redraw();}
|
||
|
|
void SampleEditor::run(){show();while(shown())Fl::wait();}
|