378 lines
11 KiB
C++
378 lines
11 KiB
C++
|
#include "gtl_hw_player.h"
|
||
|
|
||
|
#include <QDateTime>
|
||
|
|
||
|
#include "hw/gtl_hw_player_file_gtr.h"
|
||
|
#include "hw/gtl_hw_player_file_wav.h"
|
||
|
#include "hw/gtl_hw_player_analog_input.h"
|
||
|
|
||
|
namespace gtl
|
||
|
{
|
||
|
namespace hw
|
||
|
{
|
||
|
class contain
|
||
|
{
|
||
|
private:
|
||
|
int _idx;
|
||
|
public:
|
||
|
contain(int idx) : _idx(idx) {}
|
||
|
bool operator()(const std::pair<int, int> range) const { return _idx >= range.first && _idx < range.second; }
|
||
|
};
|
||
|
|
||
|
class greater
|
||
|
{
|
||
|
private:
|
||
|
int _idx;
|
||
|
public:
|
||
|
greater(int idx) : _idx(idx) {}
|
||
|
bool operator()(const std::pair<int, int> range) const { return _idx < range.first; }
|
||
|
};
|
||
|
|
||
|
player::player(QObject* parent)
|
||
|
: device{parent}
|
||
|
, _file(NULL)
|
||
|
, _is_cyclic(false)
|
||
|
, _time(0)
|
||
|
, _speed_factor(1)
|
||
|
, _sampling(1)
|
||
|
{
|
||
|
_device->set_name(tr("player"));
|
||
|
}
|
||
|
|
||
|
QString player::type() const
|
||
|
{
|
||
|
return "player";
|
||
|
}
|
||
|
|
||
|
bool player::start(QString id, qreal rate)
|
||
|
{
|
||
|
stop();
|
||
|
|
||
|
if(_id != id)
|
||
|
{
|
||
|
_id = id;
|
||
|
|
||
|
/*
|
||
|
//_device->remove_children();
|
||
|
while(_device->count() != 0)
|
||
|
{
|
||
|
data_model_node* child = _device->child(0);
|
||
|
// _device->remove(child);
|
||
|
delete child;
|
||
|
}
|
||
|
*/
|
||
|
_device->clear();
|
||
|
|
||
|
if(_file)
|
||
|
{
|
||
|
delete _file;
|
||
|
_file = NULL;
|
||
|
}
|
||
|
|
||
|
QFileInfo info(_id);
|
||
|
if(info.suffix().toLower() == "gtr")
|
||
|
_file =new player_file_gtr(this, _id);
|
||
|
else if(info.suffix().toLower() == "wav")
|
||
|
_file =new player_file_wav(this, _id);
|
||
|
|
||
|
|
||
|
|
||
|
if(_file)
|
||
|
{
|
||
|
if(!_file->is_ok())
|
||
|
{
|
||
|
delete _file;
|
||
|
_file = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(_file)
|
||
|
{
|
||
|
_rate = _file->rate()/_sampling;
|
||
|
for(int i = 0;i < _file->channels(); i++)
|
||
|
_device->add_ai(new player_analog_input(_rate, _file->channel_info(i), _device));
|
||
|
_count_ai = _file->channels();
|
||
|
|
||
|
_device->set_name(tr("player") + "[" + info.fileName() + "]");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_rate = 0;
|
||
|
_device->set_name(tr("player"));
|
||
|
}
|
||
|
|
||
|
|
||
|
_device->set_id(id);
|
||
|
_device->set_rate(_rate);
|
||
|
_time = 0;
|
||
|
|
||
|
}
|
||
|
else if(_file)
|
||
|
{
|
||
|
// if(_rate != _file->rate()/_sampling)
|
||
|
{
|
||
|
_rate = _file->rate()/_sampling;
|
||
|
for(int i = 0; i < _file->channels(); i++)
|
||
|
ai(i)->set_rate(_rate);
|
||
|
|
||
|
_device->set_rate(_rate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(_file && rate == 0)
|
||
|
{
|
||
|
QThread::start(QThread::HighPriority);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void player::save(QDomElement &root_element)
|
||
|
{
|
||
|
_rate = -1;
|
||
|
device::save(root_element);
|
||
|
}
|
||
|
|
||
|
player_file *player::file() const
|
||
|
{
|
||
|
return _file;
|
||
|
}
|
||
|
|
||
|
void player::run()
|
||
|
{
|
||
|
qDebug() << "starting playing";
|
||
|
_is_continue = true;
|
||
|
_is_suspended = false;
|
||
|
|
||
|
quint64 last_time = QDateTime::currentMSecsSinceEpoch();
|
||
|
qreal dt = 1.0 /_file->rate();
|
||
|
std::vector<qreal> buffer;
|
||
|
int idx = 0;
|
||
|
_time = 0;
|
||
|
_pos = 0;
|
||
|
|
||
|
quint64 intervalms = 100;
|
||
|
|
||
|
int sampling_idx = 0;
|
||
|
|
||
|
_ranges_indices.clear();
|
||
|
_file->get_ranges_indices(std::back_inserter(_ranges_indices));
|
||
|
|
||
|
while(_is_continue)
|
||
|
{
|
||
|
quint64 current_time = QDateTime::currentMSecsSinceEpoch();
|
||
|
quint64 time = current_time - last_time;
|
||
|
|
||
|
if(time >= intervalms/1/*00*/)
|
||
|
{
|
||
|
if (!_is_suspended)
|
||
|
{
|
||
|
int samples = qRound(intervalms/dt/1000*_speed_factor);
|
||
|
//buffer.resize(samples * _file->channels());
|
||
|
buffer.clear();
|
||
|
|
||
|
//bool is_complete = _file->get_data(&buffer[0], idx, samples, _is_cyclic);
|
||
|
bool is_complete = get_data(idx, samples, _is_cyclic, std::back_inserter(buffer));
|
||
|
|
||
|
|
||
|
if(_sampling != 1)
|
||
|
{
|
||
|
int cnt = 0;
|
||
|
for(int i = sampling_idx; i < samples; i += _sampling)
|
||
|
{
|
||
|
for(int j = 0; j < _file->channels(); j++)
|
||
|
buffer[cnt* _file->channels() + j] = buffer[i*_file->channels() + j];
|
||
|
|
||
|
cnt++;
|
||
|
}
|
||
|
|
||
|
sampling_idx = _sampling - 1 - (samples - sampling_idx)%_sampling;
|
||
|
samples = cnt;
|
||
|
|
||
|
// qDebug() << sampling_idx << samples;
|
||
|
}
|
||
|
|
||
|
if(samples)
|
||
|
{
|
||
|
set_ai_data(&buffer[0], /*(int)buffer.size()*/samples*_file->channels());
|
||
|
|
||
|
emit received_data();
|
||
|
}
|
||
|
|
||
|
_time += samples*dt;
|
||
|
_pos = idx*dt;
|
||
|
|
||
|
if(is_complete)
|
||
|
{
|
||
|
if(/*idx == 0*/!_is_cyclic)
|
||
|
_time = total_time();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
last_time = current_time;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
QThread::msleep(10);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool player::get_data(int &idx, int &samples, bool is_cyclic, std::back_insert_iterator<std::vector<qreal>> data)
|
||
|
{
|
||
|
if (_file == NULL)
|
||
|
{
|
||
|
samples = 0;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
_buffer_tmp.resize(samples*_file->channels());
|
||
|
|
||
|
if (_buffer_tmp.empty())
|
||
|
{
|
||
|
samples = 0;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
int samples_total = samples;
|
||
|
int samples_readed = 0;
|
||
|
|
||
|
bool is_completed = false;
|
||
|
|
||
|
|
||
|
|
||
|
while (samples_readed < samples_total)
|
||
|
{
|
||
|
|
||
|
int idx_start = idx;
|
||
|
|
||
|
std::vector<std::pair<int, int>>::const_iterator iter_range_start = std::find_if(_ranges_indices.begin(), _ranges_indices.end(), contain(idx_start));
|
||
|
if (iter_range_start == _ranges_indices.end())
|
||
|
{
|
||
|
iter_range_start = std::find_if(_ranges_indices.begin(), _ranges_indices.end(), greater(idx_start));
|
||
|
if (iter_range_start == _ranges_indices.end())
|
||
|
{
|
||
|
if (is_cyclic)
|
||
|
iter_range_start = std::find_if(_ranges_indices.begin(), _ranges_indices.end(), greater(0));
|
||
|
else
|
||
|
{
|
||
|
is_completed = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
idx_start = iter_range_start->first;
|
||
|
}
|
||
|
|
||
|
int idx_stop = idx_start + samples;
|
||
|
std::vector<std::pair<int, int>>::const_iterator iter_range_stop = std::find_if(_ranges_indices.begin(), _ranges_indices.end(), contain(idx_stop));
|
||
|
if (iter_range_stop != iter_range_start)
|
||
|
idx_stop = iter_range_start->second;
|
||
|
|
||
|
idx = idx_start;
|
||
|
int samples_read = idx_stop - idx_start;
|
||
|
if (samples_readed + samples_read > samples_total)
|
||
|
samples_read = samples_total - samples_readed;
|
||
|
|
||
|
|
||
|
is_completed = _file->get_data(&_buffer_tmp[0], idx, samples_read, is_cyclic);
|
||
|
std::copy(_buffer_tmp.begin(), _buffer_tmp.begin() + samples_read*_file->channels(), data);
|
||
|
samples_readed += samples_read;
|
||
|
|
||
|
if (is_completed)
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
samples = samples_readed;
|
||
|
|
||
|
|
||
|
return is_completed;
|
||
|
}
|
||
|
|
||
|
qreal player::total_time()
|
||
|
{
|
||
|
if(_file == nullptr)
|
||
|
return 0;
|
||
|
|
||
|
_ranges_indices.clear();
|
||
|
_file->get_ranges_indices(std::back_inserter(_ranges_indices));
|
||
|
|
||
|
if(_ranges_indices.empty())
|
||
|
return _file->time();
|
||
|
|
||
|
|
||
|
qreal time = 0;
|
||
|
std::pair<int, int> range = _ranges_indices[0];
|
||
|
|
||
|
for(int i = 1; i < _ranges_indices.size(); i++)
|
||
|
{
|
||
|
if(range.second >= _ranges_indices[i].first)
|
||
|
{
|
||
|
range.second = std::max(range.second, _ranges_indices[i].second);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
time += (range.second - range.first) / _file->rate();
|
||
|
range = _ranges_indices[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
time += (range.second - range.first) / _file->rate();
|
||
|
|
||
|
return time;
|
||
|
}
|
||
|
|
||
|
void player::get_parameter(int idx, QVariant &value)
|
||
|
{
|
||
|
if(idx == 0)
|
||
|
value = total_time();
|
||
|
else if(idx == 1)
|
||
|
value = _time;
|
||
|
else if(idx == 2)
|
||
|
value = _is_cyclic;
|
||
|
else if(idx == 3)
|
||
|
value = _speed_factor;
|
||
|
else if(idx == 4 && _file)
|
||
|
value = QVariant(_file->data());
|
||
|
else if(idx == 5)
|
||
|
{
|
||
|
if(_file)
|
||
|
value = _file->rate() / _sampling;
|
||
|
else
|
||
|
value = 0;
|
||
|
}
|
||
|
else if(idx == 6)
|
||
|
value = _pos;
|
||
|
else if(idx == 7)
|
||
|
value = _uuid;
|
||
|
|
||
|
}
|
||
|
|
||
|
void player::set_parameter(int idx, const QVariant &value)
|
||
|
{
|
||
|
if(idx == 2)
|
||
|
_is_cyclic = value.toBool();
|
||
|
else if(idx == 3)
|
||
|
_speed_factor = value.toDouble();
|
||
|
else if(idx == 5)
|
||
|
{
|
||
|
if(_file)
|
||
|
_sampling = qRound(_file->rate()/value.toDouble());
|
||
|
else
|
||
|
_sampling = 1;
|
||
|
|
||
|
if(_sampling < 1)
|
||
|
_sampling = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|