test_sdk/gui/gtl_gui_record_chart.cpp

463 lines
14 KiB
C++
Raw Normal View History

#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<config_record_chart_axis_x*>(_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<chart_series*>(series)->clear();
int k = 0;
int channels = (int)_series->size();
for (int idx = idx_min; idx < idx_max/* - step*/; idx += step, k++)
{
std::vector<std::vector<qreal>> buffers_min(channels);
std::vector<std::vector<qreal>> buffers_max(channels);
if (step == 1)
{
std::vector<qreal> 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<qreal> buffer_min;
std::vector<qreal> 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<qreal> 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<qreal>::iterator iter_min = std::min_element(buffers_min[i].begin(), buffers_min[i].end());
std::vector<qreal>::iterator iter_max = std::max_element(buffers_max[i].begin(), buffers_max[i].end());
if (k % 2 == 0)
{
static_cast<chart_series*>(_series->at(i))->add(QPointF(idx*_file->dt(), *iter_min));
static_cast<chart_series*>(_series->at(i))->add(QPointF(idx*_file->dt(), *iter_max));
}
else
{
static_cast<chart_series*>(_series->at(i))->add(QPointF(idx*_file->dt(), *iter_max));
static_cast<chart_series*>(_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<qreal> buffer(sampes_read*channels);
std::vector<std::vector<std::vector<float>>> min(channels);
std::vector<std::vector<std::vector<float>>> max(channels);
for (int i = 0; i < channels; i++)
{
min[i].resize(1);
max[i].resize(1);
}
std::vector<std::vector<float>> 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<float>());
max[c].push_back(std::vector<float>());
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<float>());
}
}
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<QFile*>::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<std::vector<qreal> > data) const
{
if (_series->empty())
return;
if (deg == 0)
{
std::vector<qreal> 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<float> 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);
}
}
}
}