#include "gtl_gui_record_chart.h" #include "hw/gtl_hw_player_file_gtr.h" #include "hw/gtl_hw_player_file_wav.h" #include "gui/gtl_gui_record_chart_axis_x.h" #include "gui/gtl_gui_record_chart_series.h" namespace gtl { namespace gui { record_chart::record_chart(QWidget* parent) : chart(parent, new config_record_chart_axis_x()) , _cacher(this, &_series) { connect(&_cacher, &record_chart_cacher::progress, static_cast(_axis_x), &config_record_chart_axis_x::show_progress); connect(&_cacher, &record_chart_cacher::finished, this, &record_chart::set_series_data); connect(&_cacher, &record_chart_cacher::finished, this, &record_chart::fit_all); connect(_axis_x, &::chart::axis::signal_range_changed, this, &record_chart::set_series_data); _axis_y_mode_action = new QAction("Multy axes y"); _axis_y_mode_action->setCheckable(true); _menu->addAction(_axis_y_mode_action); connect(_axis_y_mode_action, &QAction::toggled, this, &chart::set_axis_y_mode); } void record_chart::set_file(QString path) { _axis_x->blockSignals(true); remove_series(); gtl::hw::player_file* file = NULL; QFileInfo info(path); if(info.suffix().toLower() == "gtr") file =new gtl::hw::player_file_gtr(this, path); else if(info.suffix().toLower() == "wav") file =new gtl::hw::player_file_wav(this, path); if(file) { if(file->is_ok()) { for(int i = 0; i < file->channels(); i++) { add_series(new record_chart_series(file->channel(i), file->color(i), _axis_x, _axis_y)); } qreal dt = 1.0/file->rate(); _axis_x->set_boundaries(0.0, file->time() - dt); _axis_x->set_range(0.0, file->time() - dt); } else { delete file; file = NULL; } } _axis_x->blockSignals(false); _cacher.start(file); } chart_series *record_chart::create_series(analog_data */*ai*/) { return NULL; } void record_chart::fit_axis(::chart::axis *axis) { if(axis == _axis_x) axis->set_range(axis->min_boundary(), axis->max_boundary()); else ::chart::widget::fit_axis(axis); } void record_chart::resizeEvent(QResizeEvent *event) { chart::resizeEvent(event); set_series_data(); } hw::player_file *record_chart::file() { return _cacher.file(); } void record_chart::set_series_data() { _cacher.set_series_data(_axis_x->min(), _axis_x->max()); } record_chart_cacher::record_chart_cacher(QObject *parent, std::vector<::chart::series::series*> *series) : QThread(parent) , _file(NULL) , _series(series) { } record_chart_cacher::~record_chart_cacher() { stop(); clear(); } void record_chart_cacher::start(hw::player_file *file) { stop(); clear(); _file = file; if(_file) QThread::start(QThread::LowPriority); } void record_chart_cacher::stop() { _is_running = false; wait(); } void record_chart_cacher::set_series_data(qreal min, qreal max) { if(_series->empty()) return; if (isRunning()) return; if (_file == NULL) return; int samples = _file->samples(); int idx_min = std::max(0, qRound(min / _file->dt()) - 1); int idx_max = std::min(qRound(max / _file->dt()) + 1, samples - 1); int width = _series->front()->axis_x()->boundingRect().width()/* * 2*/; if (width == 0) return; if (width < 0 || width > 10000) width = 1000; int step = qRound((qreal)(idx_max - idx_min) / width); if (step < 1) step = 1; for (auto series : *_series) static_cast(series)->clear(); int k = 0; int channels = (int)_series->size(); for (int idx = idx_min; idx < idx_max/* - step*/; idx += step, k++) { std::vector> buffers_min(channels); std::vector> buffers_max(channels); if (step == 1) { std::vector buffer; get_cache_data(0, idx, std::back_inserter(buffer)); for (int i = 0; i < channels; i++) { buffers_min[i].push_back(buffer[i*2 + 0]); buffers_max[i].push_back(buffer[i*2 + 1]); } //add(QPointF(idx*_dt, _min[0][idx])); } else { int idx_start = idx; int idx_stop = std::min(idx + step, samples - 1); std::vector buffer_min; std::vector buffer_max; while (idx_start != idx_stop) { for (int deg = (int)_cache.size()/* - 1*/; deg >= 0; deg--) { int size = (1 << deg); if (idx_start % size == 0 && idx_start + size <= idx_stop) { std::vector buffer; get_cache_data(deg, idx_start / size, std::back_inserter(buffer)); for (int c = 0; c < channels; c++) { buffers_min[c].push_back(buffer[c*2 + 0]); buffers_max[c].push_back(buffer[c*2 + 1]); } /* buffer_min.push_back(_min[i][idx_start / size]); buffer_max.push_back(_max[i][idx_start / size]); */ idx_start += size; break; } } } } for (int i = 0; i < channels; i++) { std::vector::iterator iter_min = std::min_element(buffers_min[i].begin(), buffers_min[i].end()); std::vector::iterator iter_max = std::max_element(buffers_max[i].begin(), buffers_max[i].end()); if (k % 2 == 0) { static_cast(_series->at(i))->add(QPointF(idx*_file->dt(), *iter_min)); static_cast(_series->at(i))->add(QPointF(idx*_file->dt(), *iter_max)); } else { static_cast(_series->at(i))->add(QPointF(idx*_file->dt(), *iter_max)); static_cast(_series->at(i))->add(QPointF(idx*_file->dt(), *iter_min)); } } } } hw::player_file *record_chart_cacher::file() { return _file; } void record_chart_cacher::run() { QStringList paths_tmp = QStandardPaths::standardLocations(QStandardPaths::CacheLocation); QDir().mkpath(paths_tmp.front()); QString path_cache = paths_tmp.front() + "/" + QFileInfo(_file->path()).completeBaseName(); // qreal dt = 1.0 / _file->rate(); _is_running = true; int channels = _file->channels(); if (channels == 0) return; int floats = 2097152 / 4; int sampes_read = /*65536*/floats / channels; int samples_total = _file->samples(); std::vector buffer(sampes_read*channels); std::vector>> min(channels); std::vector>> max(channels); for (int i = 0; i < channels; i++) { min[i].resize(1); max[i].resize(1); } std::vector> buffers; QElapsedTimer timer; timer.start(); int iterval_100 = 1; _file->seek_to_start(); emit progress(0); int idx = 0; bool is_complete = false; while (/*idx < samples_total*/!is_complete) { int samples = sampes_read; is_complete = _file->get_data(&buffer[0], idx, samples); // idx += samples; for (int s = 0; s < samples; s++) { for (int c = 0; c < channels; c++) { min[c][0].push_back(buffer[s*channels + c]); max[c][0].push_back(buffer[s*channels + c]); for (int d = 0; d < min[c].size(); d++) { int cnt_values = (int)min[c][d].size(); if (cnt_values == 2) { if (d == min[c].size() - 1) { min[c].push_back(std::vector()); max[c].push_back(std::vector()); if (_cache.size() < d + 1) { QFile* file = new QFile(path_cache + "_" + QString::number(d + 1) + ".cache"); if (file->open(QIODevice::ReadWrite)) { } _cache.push_back(file); buffers.push_back(std::vector()); } } int idx0 = cnt_values - 2; int idx1 = cnt_values - 1; float min_value = std::min(min[c][d][idx0], min[c][d][idx1]); float max_value = std::max(max[c][d][idx0], max[c][d][idx1]); min[c][d + 1].push_back(min_value); max[c][d + 1].push_back(max_value); /* files[d]->write((char*)(&min_value), sizeof(min_value)); files[d]->write((char*)(&max_value), sizeof(max_value)); */ buffers[d].push_back(min_value); buffers[d].push_back(max_value); min[c][d].clear(); max[c][d].clear(); } } } for (int d = 0; d < buffers.size(); d++) { if (buffers[d].size() >= floats) { _cache.at(d)->write((char*)(&buffers[d][0]), buffers[d].size()*sizeof(float)); buffers[d].clear(); } } if (!_is_running) break; } if (timer.elapsed() > iterval_100 * 500) { emit progress(100.0*idx / samples_total); iterval_100++; // msleep(10); } if (!_is_running) break; } emit progress(100.0); for (int d = 0; d < buffers.size(); d++) { if (buffers[d].size() > 0) { _cache.at(d)->write((char*)(&buffers[d][0]), buffers[d].size()*sizeof(float)); buffers[d].clear(); } } qDebug() << "caching time: " << timer.elapsed(); } void record_chart_cacher::clear() { for (std::vector::iterator iter_file = _cache.begin(); iter_file != _cache.end(); iter_file++) { (*iter_file)->close(); (*iter_file)->remove(); } _cache.clear(); if (_file) delete _file; _file = NULL; } void record_chart_cacher::get_cache_data(int deg, int idx, std::back_insert_iterator > data) const { if (_series->empty()) return; if (deg == 0) { std::vector buffer(_series->size()); int samples = 1; _file->get_data(&buffer[0], idx, samples); for (int i = 0; i < _series->size(); i++) { data = buffer[i]; data++; data = buffer[i]; data++; } } else { std::vector buffer(_series->size() * 2); _cache[deg - 1]->seek(idx*buffer.size()*sizeof(float)); _cache[deg - 1]->read((char*)(&buffer[0]), buffer.size()*sizeof(float)); std::copy(buffer.begin(), buffer.end(), data); } } } }