git.haldean.org qb / 95f1dda
access control Haldean Brown 4 months ago
4 changed file(s) with 109 addition(s) and 87 deletion(s). Raw diff Collapse all Expand all
33
44 #include "AudioFile/AudioFile.h"
55 #include "AudioFFT/AudioFFT.h"
6
7 #include <cmath>
68
79 namespace qb {
810 audio_node::audio_node(const context *c, const std::string &path)
2527 static constexpr int hires_frame_limit = 1000;
2628
2729 const std::vector<float>& audio_node::rendered() const {
28 double visframes = 2 * _c->visrad;
30 double visframes = 2 * _c->visrad();
2931 return visframes > hires_frame_limit ? _rlo : _rhi;
3032 }
3133
3234 double audio_node::rsamplerate() const {
33 double visframes = 2 * _c->visrad;
35 double visframes = 2 * _c->visrad();
3436 return (visframes > hires_frame_limit ? render_rate_lo : render_rate_hi);
3537 }
3638
1818 switch (node->loadstate().stage) {
1919 case load_state::loading:
2020 ImGui::Text("%c loading: %s",
21 (node->ctx()->spinstep / 90 % 2) ? '.' : ' ',
21 (node->ctx()->spinstep() / 90 % 2) ? '.' : ' ',
2222 node->loadstate().message.c_str());
2323 break;
2424
6262
6363 const context *c = node->ctx();
6464 const double rsamplerate = node->rsamplerate();
65 const int64_t s0 = (long) floor(rsamplerate * (c->playhead - c->visrad));
66 const int64_t s1 = (long) ceil(rsamplerate * (c->playhead + c->visrad));
65 const int64_t s0 = (long) floor(rsamplerate * (c->playhead() - c->visrad()));
66 const int64_t s1 = (long) ceil(rsamplerate * (c->playhead() + c->visrad()));
6767
6868 const float width = corner.x - origin.x;
6969 const float scale = width / (s1 - s0);
8282 ImGui::Text(node->name().c_str());
8383
8484 const qb::context *ctx = node->ctx();
85 const int width = ctx->width;
86 const float vismin = (float) (ctx->playhead - ctx->visrad);
87 const float vismax = (float) (ctx->playhead + ctx->visrad);
85 const int width = ctx->width();
86 const float vismin = (float) (ctx->playhead() - ctx->visrad());
87 const float vismax = (float) (ctx->playhead() + ctx->visrad());
8888 const float fadetime = 0.5f * ctx->framerate();
8989
9090 ImDrawList * const drawlist = ImGui::GetWindowDrawList();
9999
100100 const bool hit = std::binary_search(track.hits.begin(),
101101 track.hits.end(),
102 (int) ctx->playhead);
102 (int) ctx->playhead());
103103 float hitdist = -1;
104104 if (!hit) {
105105 const auto iter =
106106 std::lower_bound(track.hits.begin(),
107107 track.hits.end(),
108 (int) ctx->playhead);
108 (int) ctx->playhead());
109109 if (iter != track.hits.end() && iter != track.hits.begin()) {
110 hitdist = (float) (ctx->playhead - *std::prev(iter));
110 hitdist = (float) (ctx->playhead() - *std::prev(iter));
111111 }
112112 }
113113
4242 }
4343
4444 int context::frame2pixel(double frame) {
45 const double off = frame - playhead;
46 const double ndc = off / (double)visrad;
47 const int px = (int) round(width * (ndc + 1) / 2.0);
48 if (px < 0 || px >= width) {
45 const double off = frame - _playhead;
46 const double ndc = off / (double)_visrad;
47 const int px = (int) round(_width * (ndc + 1) / 2.0);
48 if (px < 0 || px >= _width) {
4949 return -1;
5050 }
5151 return px;
5353
5454 void context::frame(int width, int height) {
5555 taskpool::get().frame();
56 spinstep++;
57 this->width = width;
58 this->height = height;
56 _spinstep++;
57 _width = width;
58 _height = height;
5959
6060 ImGui::SetNextWindowPos(ImVec2(0, 0));
61 ImGui::SetNextWindowSize(ImVec2((float) width, (float) height));
61 ImGui::SetNextWindowSize(ImVec2((float) _width, (float) _height));
6262 ImGui::PushStyleColor(ImGuiCol_WindowBg, qb::theme::colors[qb::theme::background]);
6363 ImGui::Begin("MainWindow", nullptr,
6464 ImGuiWindowFlags_NoDecoration);
7070 maxframe = std::max(maxframe, (double) n->maxframe());
7171 }
7272
73 if (!audioinit) {
73 if (!_audioinit) {
7474 initaudio();
7575 }
76 if (audioinit) {
77 if (ImGui::IsKeyPressed(SAPP_KEYCODE_SPACE, /* repeat */ false)) {
78 playing = !playing;
79 }
80 if (!playing) {
81 if (ImGui::IsKeyPressed(SAPP_KEYCODE_LEFT)) {
82 playhead = floor(playhead) - 1.0;
83 } else if (ImGui::IsKeyPressed(SAPP_KEYCODE_RIGHT)) {
84 playhead = floor(playhead) + 1.0;
85 } else if (ImGui::IsKeyPressed(SAPP_KEYCODE_UP)) {
86 playhead = minframe;
87 } else if (ImGui::IsKeyPressed(SAPP_KEYCODE_DOWN)) {
88 playhead = maxframe;
89 }
90 } else {
91 if (playhead > maxframe) {
92 playing = false;
93 }
94 }
95 pushaudio();
96 }
76
77 if (ImGui::IsKeyPressed(SAPP_KEYCODE_SPACE, /* repeat */ false)) {
78 _playing = !_playing;
79 }
80 if (!_playing) {
81 if (ImGui::IsKeyPressed(SAPP_KEYCODE_LEFT)) {
82 _playhead = floor(_playhead) - 1.0;
83 } else if (ImGui::IsKeyPressed(SAPP_KEYCODE_RIGHT)) {
84 _playhead = floor(_playhead) + 1.0;
85 } else if (ImGui::IsKeyPressed(SAPP_KEYCODE_UP)) {
86 _playhead = minframe;
87 } else if (ImGui::IsKeyPressed(SAPP_KEYCODE_DOWN)) {
88 _playhead = maxframe;
89 }
90 } else {
91 if (_playhead > maxframe) {
92 _playing = false;
93 }
94 }
95 pushaudio();
9796
9897 if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
9998 const ImVec2 delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Middle);
100 const bool horiz = !playing && abs(delta.x) > abs(delta.y);
101 const double pxperframe = width / (2.0 * std::max(visrad, 1.0));
99 const bool horiz = !_playing && abs(delta.x) > abs(delta.y);
100 const double pxperframe = _width / (2.0 * std::max(_visrad, 1.0));
102101 const double framedist = horiz ? 0.1 * delta.x / pxperframe : 0;
103102 const double zoomdist = !horiz ? 0.1 * delta.y / pxperframe : 0;
104 playhead = clamp((double)playhead + framedist, minframe, maxframe);
105 visrad = clamp((double)visrad + zoomdist, 3.0, maxframe - minframe);
106 }
107
108 // draw playhead
103 _playhead = clamp((double)_playhead + framedist, minframe, maxframe);
104 _visrad = clamp((double)_visrad + zoomdist, 3.0, maxframe - minframe);
105 }
106 if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
107 const ImVec2 delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left);
108
109 }
110
111 // draw _playhead
109112 {
110113 ImDrawList * const drawlist = ImGui::GetWindowDrawList();
111 const float f0x = (float) frame2pixel(playhead);
112 const float f1x = (float) frame2pixel(playhead + 1);
114 const float f0x = (float) frame2pixel(_playhead);
115 const float f1x = (float) frame2pixel(_playhead + 1);
113116 if (f1x > 0 && f1x != f0x) {
114 drawlist->AddRectFilled(ImVec2(f0x, 0.0), ImVec2(f1x, (float) height),
117 drawlist->AddRectFilled(ImVec2(f0x, 0.0), ImVec2(f1x, (float) _height),
115118 qb::theme::colors[qb::theme::color::playhead]);
116119 } else {
117 drawlist->AddLine(ImVec2(f0x, 0), ImVec2(f0x, (float) height),
120 drawlist->AddLine(ImVec2(f0x, 0), ImVec2(f0x, (float) _height),
118121 qb::theme::colors[qb::theme::color::playhead]);
119122 }
120123 }
145148 if (!audio) {
146149 return;
147150 }
148
149 saudio_desc d = {0};
151 _audiosource = audio;
152
153 saudio_desc d;
150154 d.sample_rate = audio->data()->getSampleRate();
151155 d.num_channels = audio->data()->getNumChannels();
156 d.buffer_frames = 0;
157 d.packet_frames = 0;
158 d.num_packets = 0;
159 d.stream_cb = nullptr;
160 d.stream_userdata_cb = nullptr;
161 d.user_data = nullptr;
152162
153163 saudio_setup(&d);
154 audioinit = saudio_isvalid();
155 if (!audioinit) {
156 ImGui::Text("could not initialize audio engine");
157 } else {
158 audiosource = audio;
164 _audioinit = saudio_isvalid();
165 if (!_audioinit) {
166 ImGui::Text(
167 "could not initialize audio engine (rate=%d, channels=%d)",
168 d.sample_rate, d.num_channels);
159169 }
160170 }
161171
162172 void context::pushaudio() {
163 if (!audiosource) {
173 if (!_audioinit or !_audiosource) {
174 if (_playing) {
175 const double elapsed = ImGui::GetIO().DeltaTime;
176 _playhead += elapsed * _framerate;
177 }
164178 return;
165179 }
166180
167 const audio_node *audio = static_cast<const audio_node*>(audiosource);
181 const audio_node *audio = static_cast<const audio_node*>(_audiosource);
168182 const int samplerate = audio->data()->getSampleRate();
169183 const double sampleperframe = (double) samplerate / (double) _framerate;
170184 const std::vector<float> &s = audio->packed();
171185
172 if (!playing) {
173 if ((int) playhead == playedframe) {
186 if (!_playing) {
187 if ((int) _playhead == _playedframe) {
174188 return;
175189 }
176 const double firstsample = floor(playhead) * sampleperframe;
190 const double firstsample = floor(_playhead) * sampleperframe;
177191 const size_t s0 = (size_t) floor(firstsample);
178192 if (s0 >= s.size()) {
179193 return;
180194 }
181195 const int topush = std::min((int) sampleperframe, (int) (s.size() - s0));
182196 const int pushed = saudio_push(&s[s0], topush);
183 playedframe = (int) playhead;
184 pushedsample = (int64_t) (s0 + sampleperframe - 1);
197 _playedframe = (int) _playhead;
198 _pushedsample = (int64_t) (s0 + sampleperframe - 1);
185199
186200 } else {
187201 const double elapsed = ImGui::GetIO().DeltaTime;
188 playhead += elapsed * _framerate;
189
190 const int64_t nextsample = (int64_t) floor(playhead * sampleperframe);
202 _playhead += elapsed * _framerate;
203
204 const int64_t nextsample = (int64_t) floor(_playhead * sampleperframe);
191205
192206 // buffer 125ms of audio
193207 const int64_t buffersamples = samplerate / 8;
195209 const int64_t bufferrefill = samplerate / 20;
196210
197211 int64_t fillfrom = -1;
198 int64_t dist = pushedsample - nextsample;
199 if (pushedsample < 0) {
212 int64_t dist = _pushedsample - nextsample;
213 if (_pushedsample < 0) {
200214 fillfrom = nextsample;
201215 } else if (dist < bufferrefill) {
202 fillfrom = pushedsample + 1;
216 fillfrom = _pushedsample + 1;
203217 }
204218
205219 if (fillfrom >= 0 && (size_t) fillfrom < s.size()) {
206220 const int fillamount =
207221 (int) std::min(buffersamples, (int64_t)(s.size() - fillfrom - 1));
208222 const int pushed = saudio_push(&s[fillfrom], fillamount);
209 pushedsample = fillfrom + pushed;
223 _pushedsample = fillfrom + pushed;
210224 }
211225 }
212226 }
6363 int framerate() const { return _framerate; }
6464 void framerate(int fr) { _framerate = fr; }
6565
66 double visrad() const { return _visrad; }
67 double playhead() const { return _playhead; }
68 uint32_t spinstep() const { return _spinstep; }
69 int width() const { return _width; }
70 int height() const { return _height; }
71
6672 void frame(int width, int height);
6773 void shutdown();
6874
6975 int frame2pixel(double frame);
7076
71 bool playing = false;
72
73 double playhead = 0;
74 double visrad = 240;
75 int width = 0;
76 int height = 0;
77
78 // for ui animations
79 uint32_t spinstep = 0;
80
8177 private:
8278 void initaudio();
8379 void pushaudio();
8480
85 bool audioinit = false;
86 int playedframe = -1;
87 int64_t pushedsample = -1;
81 bool _playing = false;
8882
89 const node *audiosource = nullptr;
83 double _playhead = 0;
84 double _visrad = 240;
85 int _width = 0;
86 int _height = 0;
87
88 // for ui animations
89 uint32_t _spinstep = 0;
90
91 bool _audioinit = false;
92 int _playedframe = -1;
93 int64_t _pushedsample = -1;
94
95 const node *_audiosource = nullptr;
9096
9197 int _framerate = 24;
9298 std::vector<qb::node_ptr> _nodes;