git.haldean.org qb / 6abaa4d
do my own drawing, process audio better haldean 1 year, 1 month ago
3 changed file(s) with 110 addition(s) and 43 deletion(s). Raw diff Collapse all Expand all
1212
1313 switch (node->loadstate().stage) {
1414 case load_state::loading:
15 ImGui::Text("loading: %s", node->loadstate().message.c_str());
15 ImGui::Text("%c loading: %s",
16 (node->ctx()->spinstep / 90 % 2) ? '.' : ' ',
17 node->loadstate().message.c_str());
1618 return;
1719
1820 case load_state::failed:
3537 data->getNumChannels(),
3638 minutes, seconds);
3739
38 static int len = 500;
39 static int off = 0;
40 ImGui::DragInt("len", &len, 1.0, 0, INT_MAX);
41 ImGui::DragInt("off", &off, 1.0, 0, INT_MAX);
40 const std::vector<float> &r = node->rendered();
41 ImGui::Text("rendered to %lu samples", r.size());
4242
43 const std::vector<float> &r = node->rendered();
44 /*
45 ImGui::PlotLines("samples", r.data(),
46 std::min((int)r.size() - off, len),
47 std::min((int)r.size() - 1, off));
48 */
43 const context *c = node->ctx();
44 const int rad = 100;
45 const double rsamplerate = node->rsamplerate();
46 const size_t s0 = floor(rsamplerate * c->vismin);
47 const size_t s1 = ceil(rsamplerate * c->vismax);
4948
50 //ImPlot::SetNextPlotLimitsY(-1.0, 1.0, ImGuiCond_Always);
51 if (ImPlot::BeginPlot("samples", "frame", "amp", ImVec2(-1, 0),
52 ImPlotFlags_NoTitle | ImPlotFlags_NoLegend,
53 ImPlotAxisFlags_AutoFit)) {
54 ImPlot::PlotLine("mono", r.data(), r.size());
55 ImPlot::EndPlot();
49 ImGui::InvisibleButton("##empty", ImVec2(-1, 2 * rad));
50 const ImVec2 origin = ImGui::GetItemRectMin();
51 const ImVec2 corner = ImGui::GetItemRectMax();
52 const double width = corner.x - origin.x;
53 const double scale = width / (s1 - s0);
54
55 ImDrawList *drawlist = ImGui::GetWindowDrawList();
56 for (size_t s = s0 + 1; s <= s1 && s < r.size(); s++) {
57 const ImVec2 a(origin.x + scale * (s - 1 - s0),
58 origin.y + rad * (1.0 - r[s - 1]));
59 const ImVec2 b(origin.x + scale * (s - s0),
60 origin.y + rad * (1.0 - r[s]));
61 drawlist->AddLine(a, b, IM_COL32(255, 255, 0, 255));
5662 }
5763 }
5864 }
1111
1212 void context::frame(int width, int height) {
1313 taskpool::get().frame();
14 spinstep++;
1415
1516 ImGui::SetNextWindowPos(ImVec2(0, 0));
1617 ImGui::SetNextWindowSize(ImVec2((float) width, (float) height));
1718 ImGui::Begin("MainWindow", nullptr, ImGuiWindowFlags_NoDecoration);
18 ImGui::Text("%d nodes in context", _nodes.size());
19
20 int minframe = 0, maxframe = 0;
21 for (const auto &n : _nodes) {
22 minframe = std::min(minframe, n->minframe());
23 maxframe = std::max(maxframe, n->maxframe());
24 }
25 ImGui::DragIntRange2("visible", &vismin, &vismax, 1.0, minframe, maxframe);
26
1927 for (const auto &n : _nodes) {
2028 draw_node(n);
2129 }
2230 ImGui::End();
31
32 ImGui::ShowDemoWindow();
2333 }
2434
2535 void context::shutdown() {
3646 taskpool::get().submit_async([this]() { this->load_async(); });
3747 }
3848
49 static constexpr double render_rate_hi = 8;
50 static constexpr double render_rate_lo = 2;
51 static constexpr int hires_frame_limit = 1000;
52
53 const std::vector<float>& audio_node::rendered() const {
54 int visframes = _c->vismax - _c->vismin;
55 return visframes > hires_frame_limit ? _rlo : _rhi;
56 }
57
58 double audio_node::rsamplerate() const {
59 int visframes = _c->vismax - _c->vismin;
60 return (visframes > hires_frame_limit ? render_rate_lo : render_rate_hi);
61 }
62
3963 void audio_node::load_async() {
4064 taskpool::get().submit_frame([this]() {
4165 this->_loadstate = load_state(load_state::loading, "loading file");
5074 }
5175
5276 taskpool::get().submit_frame([this]() {
53 this->_loadstate = load_state(load_state::loading, "generating preview");
77 this->_loadstate =
78 load_state(load_state::loading, "generating hi-res preview");
5479 });
55 render_samples();
80 _rhi = render_samples(render_rate_hi);
81
82 taskpool::get().submit_frame([this]() {
83 this->_loadstate =
84 load_state(load_state::loading, "generating lo-res preview");
85 });
86 _rlo = render_samples(render_rate_lo);
5687
5788 // update load state on main thread: this is what governs whether the UI
5889 // accesses the rest of the state here or not.
6192 });
6293 }
6394
64 void audio_node::render_samples() {
95 std::vector<float> audio_node::render_samples(double framesamples) {
6596 const size_t samples = _data->getNumSamplesPerChannel();
66 const double framerate = _c->framerate();
67
68 const double spf = (double)_data->getSampleRate() / framerate;
69 const int frames = 8 * (int) ceil(samples / spf);
97 const double spr =
98 (double)_data->getSampleRate() / (framesamples * _c->framerate());
99 const size_t rsamples = ceil((double)samples / spr);
70100 const int channels = _data->getNumChannels();
71101
72 _rendered.resize(frames);
73 for (int i = 0; i < frames; i++) {
74 const double s1 = i * spf;
75 const double s2 = (i + 1) * spf;
76 float sum = 0;
102 std::vector<float> rendered;
103 rendered.resize(rsamples);
104 float absmax = 0;
105 for (int i = 0; i < rsamples; i++) {
106 const double s1 = i * spr;
107 const double s2 = (i + 1) * spr;
108 double sum = 0;
77109 for (int chan = 0; chan < channels; chan++) {
78 for (size_t s = (size_t)floor(s1); s < (size_t)ceil(s2); s++) {
79 const float sample = s >= samples ? 0 : _data->samples[chan][s];
80 if (s == floor(s1) && (double)s != s1) {
81 sum += sample * (ceil(s1) - s1);
82 } else if (s == ceil(s2) && (double)s != s2) {
83 sum += sample * (s2 - floor(s2));
84 } else {
85 sum += sample;
86 }
110 for (size_t s = (size_t)floor(s1);
111 s < (size_t)ceil(s2) && s < _data->samples[chan].size();
112 s++) {
113 double x = _data->samples[chan][s];
114 sum += (x < 0 ? -1.0 : 1.0) * sqrt(fabs(x));
87115 }
88116 }
89 _rendered[i] = (float)(sum / (spf * (double)channels));
117 const float r = (float) (sum / (spr * channels));
118 absmax = std::max(absmax, fabsf(r));
119 rendered[i] = r;
90120 }
121 for (float &f : rendered) {
122 f /= absmax;
123 }
124 return rendered;
125 }
126
127 int audio_node::minframe() const {
128 return 0;
129 }
130
131 int audio_node::maxframe() const {
132 if (_loadstate.stage != load_state::loaded) {
133 return 0;
134 }
135 return (int) ceil(_data->getLengthInSeconds() * _c->framerate());
91136 }
92137
93138 audio_node::~audio_node() = default;
3838
3939 const std::string &name() const { return _name; }
4040 node_type type() const { return _type; }
41 const context* ctx() const { return _c; }
42
43 virtual int minframe() const = 0;
44 virtual int maxframe() const = 0;
4145
4246 protected:
4347 node(const context *c, const std::string &n, node_type t);
6064 const std::string &path() const { return _path; }
6165 load_state loadstate() const { return _loadstate; }
6266 const audio_file_cptr data() const { return _data; }
63 const std::vector<float>& rendered() const { return _rendered; }
67
68 const std::vector<float>& rendered() const;
69 // number of render samples per frame
70 double rsamplerate() const;
71
72 int minframe() const override;
73 int maxframe() const override;
6474
6575 private:
6676 void load_async();
67 void render_samples();
77 std::vector<float> render_samples(double rsamplerate);
6878
6979 const std::string _path;
7080 audio_file_ptr _data;
71 std::vector<float> _rendered;
81 std::vector<float> _rhi;
82 std::vector<float> _rlo;
7283 load_state _loadstate;
7384 };
7485
8899 void frame(int width, int height);
89100 void shutdown();
90101
102 int vismin = 0;
103 int vismax = 240;
104
105 uint32_t spinstep = 0;
106
91107 private:
92108 int _framerate = 24;
93109 std::vector<qb::node_ptr> _nodes;