git.haldean.org qb / main audionode.cpp
main

Tree @main (Download .tar.gz)

audionode.cpp @main

b340f54
cd552ee
b340f54
cd552ee
b340f54
98b3e9d
b340f54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include "qb.hpp"
#include "audionode.hpp"
#include "task.hpp"

#include "AudioFile/AudioFile.h"
#include "AudioFFT/AudioFFT.h"

namespace qb {
  audio_node::audio_node(const context *c, const std::string &path)
    : node(c, path, qb::node_type::audio)
    , _path(path)
    , _loadstate(load_state::loading)
  {
    taskpool::get().submit_async([this]() { this->load_async(); });
  }

  const std::vector<float>& audio_node::packed() const {
    if (!_data || _data->getNumChannels() != 1) {
      return _packed;
    }
    return _data->samples[0];
  }

  static constexpr double render_rate_hi = 16;
  static constexpr double render_rate_lo = 4;
  static constexpr int hires_frame_limit = 1000;

  const std::vector<float>& audio_node::rendered() const {
    double visframes = 2 * _c->visrad;
    return visframes > hires_frame_limit ? _rlo : _rhi;
  }

  double audio_node::rsamplerate() const {
    double visframes = 2 * _c->visrad;
    return (visframes > hires_frame_limit ? render_rate_lo : render_rate_hi);
  }

  void audio_node::load_async() {
    taskpool::get().submit_frame([this]() {
        this->_loadstate = load_state(load_state::loading, "loading file");
      });
    _data.reset(new audio_file());
    const bool success = _data->load(_path);
    if (!success) {
      taskpool::get().submit_frame([this]() {
          this->_loadstate = load_state(load_state::failed);
        });
      return;
    }

    taskpool::get().submit_frame([this]() {
        this->_loadstate =
          load_state(load_state::loading, "generating hi-res preview");
      });
    _rhi = render_samples(render_rate_hi);

    taskpool::get().submit_frame([this]() {
        this->_loadstate =
          load_state(load_state::loading, "generating lo-res preview");
      });
    _rlo = render_samples(render_rate_lo);

    taskpool::get().submit_frame([this]() {
        this->_loadstate =
          load_state(load_state::loading, "packing samples for playback");
      });
    if (_data->getNumChannels() != 1) {
      const size_t channels = _data->getNumChannels();
      const size_t n = _data->getNumSamplesPerChannel();
      _packed.reserve(channels * n);
      for (size_t i = 0; i < n; i++) {
        for (size_t c = 0; c < channels; c++) {
          _packed.push_back(_data->samples[c][i]);
        }
      }
    }

    // update load state on main thread: this is what governs whether the UI
    // accesses the rest of the state here or not.
    taskpool::get().submit_frame([this]() {
        this->_loadstate = load_state(load_state::loaded);
      });
  }

  std::vector<float> audio_node::render_samples(double framesamples) {
    const size_t samples = _data->getNumSamplesPerChannel();
    const double spr =
      (double)_data->getSampleRate() / (framesamples * _c->framerate());
    const size_t rsamples = (size_t) ceil((double)samples / spr);
    const int channels = _data->getNumChannels();

    std::vector<float> rendered;
    rendered.resize(rsamples);
    float absmax = 0;
    for (int i = 0; i < rsamples; i++) {
      const double s1 = i * spr;
      const double s2 = (i + 1) * spr;
      double sum = 0;
      for (int chan = 0; chan < channels; chan++) {
        for (size_t s = (size_t)floor(s1);
             s < (size_t)ceil(s2) && s < _data->samples[chan].size();
             s++) {
          double x = _data->samples[chan][s];
          sum += x;
        }
      }
      const float r = (float) (sum / (spr * channels));
      absmax = std::max(absmax, fabsf(r));
      rendered[i] = r;
    }
    for (float &f : rendered) {
      f /= absmax;
    }
    return rendered;
  }

  int audio_node::minframe() const {
    return 0;
  }

  int audio_node::maxframe() const {
    if (_loadstate.stage != load_state::loaded) {
      return 0;
    }
    return (int) ceil(_data->getLengthInSeconds() * _c->framerate());
  }

  audio_node::~audio_node() = default;
}