soundeditor/qatsh/PartialsSpecgramGraphicsSce...

407 lines
14 KiB
C++

// PartialsSpecgramGraphicsScene class implementation
//
// The QGraphicsScene-derived partials spectrogram view
//
// QATSH Copyright 2009 Jean-Philippe MEURET <jpmeuret@free.fr>
#include <limits>
#include <iostream>
#include <QObject>
#include <QList>
#include <QRectF>
#include <QModelIndex>
#include <QPen>
#include <QGraphicsView>
#include <QGraphicsItemGroup>
#include <QItemSelection>
#include "ATSMath.h"
#include "PartialsSpecgramGraphicsScene.h"
#include "PartialsSpecgramGraphicsFrameItem.h"
#include "ATSModelManager.h"
#include "ATSModel.h"
#include "ATSModelItems.h"
#include "ATSPropertiesProxyModel.h"
#include "ATSPartialsProxyModel.h"
// Ids for graphics items custom data.
enum EPartialGraphItemDataType { eMeanAmplitude };
// Constructors / Destructor ===========================================================
PartialsSpecgramGraphicsScene::PartialsSpecgramGraphicsScene(QObject *parent)
: QGraphicsScene(parent), _pModelMgr(0), _pqvgiPartials(0)
{
}
PartialsSpecgramGraphicsScene::~PartialsSpecgramGraphicsScene()
{
clear();
}
void PartialsSpecgramGraphicsScene::clear()
{
// Free the locally stored partial data.
if (_pqvgiPartials)
{
for (int nPartInd = 0; nPartInd < _pqvgiPartials->size(); nPartInd++)
if ((*_pqvgiPartials)[nPartInd])
delete (*_pqvgiPartials)[nPartInd];
delete _pqvgiPartials;
_pqvgiPartials = 0;
}
// Normal QGraphicsScene clearing.
QGraphicsScene::clear();
}
// Constructors / Destructor ===========================================================
void PartialsSpecgramGraphicsScene::setModelManager(ATSModelManager* pModelMgr)
{
// Save pointer to model for later.
_pModelMgr = pModelMgr;
// Connect to any model change.
if (_pModelMgr)
{
connect(_pModelMgr->partialsModel(), SIGNAL(modelReset()), this, SLOT(onModelReset()));
connect(_pModelMgr->partialsModel(),
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
}
// Connect to selection model change.
connect(_pModelMgr->partialsSelectionModel(),
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
// Load scene from model : create graphic items from partials data
loadFromModel();
}
// Model change slots ==================================================================
void PartialsSpecgramGraphicsScene::onModelReset()
{
//std::cout << "PartialsSpecgramGraphicsScene::onModelReset()" << std::endl;
_pModelMgr->partialsSelectionModel()->clear();
clear();
loadFromModel();
}
void PartialsSpecgramGraphicsScene::onDataChanged(const QModelIndex& qmiTopLeft,
const QModelIndex& qmiBotRight)
{
std::cout << "PartialsSpecgramGraphicsScene::onDataChanged : Not yet implemented" << std::endl;
// TODO : Check really changed rect if useful here ...
// TODO : Update item properties (and add/remove items ?)
}
// Selection management ================================================================
void PartialsSpecgramGraphicsScene::selectPartialFrame(int nPartialIndex, int nFramIndex,
QItemSelectionModel::SelectionFlags selFlags)
{
// std::cout << "PartialsSpecgramGraphicsScene::onSelectionChanged : "
// << "selFlags=" << std::hex << selFlags << std::dec << std::endl;
// Select the entier partial (for the moment).
QModelIndex qmiPartFrame = _pModelMgr->partialsModel()->index(0, nPartialIndex, QModelIndex());
_pModelMgr->partialsSelectionModel()->select(qmiPartFrame, selFlags);
}
void PartialsSpecgramGraphicsScene::onSelectionChanged(const QItemSelection& qisSelected,
const QItemSelection& qisDeselected)
{
std::cout << "PartialsSpecgramGraphicsScene::onSelectionChanged : " << std::endl;
// Note: Selected partials get all white, and the Z-value of associated item groups
// is increased above all the unselected ones in order we always can see them.
// Once deselected, they get back to their initial color (frame amplitude)
// and initial Z-value (mean amplitude of the partial).
// Selected partials.
if (qisSelected.count())
{
foreach (QModelIndex qmiSelected, qisSelected.indexes())
{
const ATSModelItem *pmiSelected =
static_cast<const ATSModelItem*>(qmiSelected.internalPointer());
if (pmiSelected)
{
// std::cout << " Selecting "
// << qmiSelected.row() << ',' << qmiSelected.column()
// << ':' << pmiSelected->type() << std::endl;
switch (pmiSelected->type())
{
case ATSModelItem::ePartial:
{
const QVector<PartialsSpecgramGraphicsFrameItem*>* pqvgiFrames =
(*_pqvgiPartials)[qmiSelected.column()];
// if ((*pqvgiFrames)[0])
// (*pqvgiFrames)[0]->parentItem()->setZValue(_dMaxPartialMeanAmpl*1.1);
for (int nFrameInd = 0; nFrameInd < pqvgiFrames->size(); nFrameInd++)
if ((*pqvgiFrames)[nFrameInd])
(*pqvgiFrames)[nFrameInd]->select(true);
}
default:
break;
}
}
}
}
// Deselected partials.
if (qisDeselected.count())
{
foreach (QModelIndex qmiDeselected, qisDeselected.indexes())
{
const ATSModelItem *pmiDeselected =
static_cast<const ATSModelItem*>(qmiDeselected.internalPointer());
if (pmiDeselected)
{
// std::cout << " Deselecting "
// << qmiDeselected.row() << ',' << qmiDeselected.column()
// << ':' << pmiDeselected->type() << std::endl;
switch (pmiDeselected->type())
{
case ATSModelItem::ePartial:
{
const QVector<PartialsSpecgramGraphicsFrameItem*>* pqvgiFrames =
(*_pqvgiPartials)[qmiDeselected.column()];
// if ((*pqvgiFrames)[0])
// {
// const double dPartZValue =
// (*pqvgiFrames)[0]->parentItem()->data(eMeanAmplitude).toDouble();
// (*pqvgiFrames)[0]->parentItem()->setZValue(dPartZValue);
// }
for (int nFrameInd = 0; nFrameInd < pqvgiFrames->size(); nFrameInd++)
if ((*pqvgiFrames)[nFrameInd])
(*pqvgiFrames)[nFrameInd]->select(false);
}
default:
break;
}
}
}
}
}
// Change contrast / brightness ========================================================
// Set amplitude color contrast in [0, 1]
void PartialsSpecgramGraphicsScene::setAmplitudeContrast(double dContrast)
{
//std::cout << "PartialsSpecgramGraphicsScene::setAmplitudeContrast("
// << dContrast << ")" << std::endl;
clear();
_cmAmplColorMap.setContrast(dContrast);
loadPartials();
emit colorMapChanged(&_cmAmplColorMap);
}
// Set amplitude color brightness in [0, 1]
void PartialsSpecgramGraphicsScene::setAmplitudeBrightness(double dBrightness)
{
//std::cout << "PartialsSpecgramGraphicsScene::setAmplitudeBrightness("
// << dBrightness << ")" << std::endl;
clear();
_cmAmplColorMap.setBrightness(dBrightness);
loadPartials();
emit colorMapChanged(&_cmAmplColorMap);
}
// Load scene from model ===============================================================
void PartialsSpecgramGraphicsScene::loadFromModel()
{
ATSPropertiesProxyModel* pPropsModel = _pModelMgr->propertiesModel();
// Initialize scene rect from model
// (x = time from 0 to sound.duration, y = frequency from sound.minFreq to sound.maxFreq).
const QModelIndex miDuration =
pPropsModel->index(0, ATSFilePropertiesItem::eSoundPropDuration, QModelIndex());
const double dMaxTime = pPropsModel->data(miDuration, Qt::DisplayRole).toDouble();
const double dMinTime = 0.0;
const QModelIndex miMaxFreq =
pPropsModel->index(0, ATSFilePropertiesItem::eSoundPropMaxPartialFrequency, QModelIndex());
const double dMaxFreq = pPropsModel->data(miMaxFreq, Qt::DisplayRole).toDouble();
const double dMinFreq = 0.0;
setSceneRect(dMinTime, dMinFreq, dMaxTime - dMinTime, dMaxFreq - dMinFreq);
std::cout << "PartialsSpecgramGraphicsScene::loadFromModel : "
<< "duration = " << dMaxTime
<< ", max. freq. = " << dMaxFreq << std::endl;
// Reset amplitude color map.
const QModelIndex miMaxAmpl =
pPropsModel->index(0, ATSFilePropertiesItem::eSoundPropMaxPartialAmplitude, QModelIndex());
const double dMinAmpl = 0.0;
const double dMaxAmpl = pPropsModel->data(miMaxAmpl, Qt::DisplayRole).toDouble();
_cmAmplColorMap.reset(ColorMap::eStyleStandard, 256, dMinAmpl, dMaxAmpl);
emit colorMapChanged(&_cmAmplColorMap);
// Reset time ruler spec.
const double dTimeRulerBaseStep = pow(10.0, floor(log10(dMaxTime - dMinTime)));
_rsTimeRulerSpec.reset(RulerSpec::eHorizontal, RulerSpec::ePositive, RulerSpec::ePositiveSide,
dMinTime, dMaxTime, 0.0, dTimeRulerBaseStep, 10.0,
QList<double>() << 2 << 5, QList<double>() << 0.6 << 0.3);
emit timeRangeChanged(&_rsTimeRulerSpec);
// Reset frequency ruler spec.
const double dFreqRulerBaseStep = pow(10.0, floor(log10(dMaxFreq - dMinFreq)));
_rsFreqRulerSpec.reset(RulerSpec::eVertical, RulerSpec::ePositive, RulerSpec::ePositiveSide,
dMinFreq, dMaxFreq, 0.0, dFreqRulerBaseStep, 10.0,
QList<double>() << 2 << 5, QList<double>() << 0.4 << 0.2);
emit frequencyRangeChanged(&_rsFreqRulerSpec);
// Draw partials.
loadPartials();
}
void PartialsSpecgramGraphicsScene::loadPartials()
{
ATSPropertiesProxyModel* pPropsModel = _pModelMgr->propertiesModel();
// Create items from model (1 line for each frame of each partial,
// 1 item group of line items for each partial).
// a) Get the number of partials.
const QModelIndex miNbPartials =
pPropsModel->index(0, ATSFilePropertiesItem::eSoundPropNbPartials, QModelIndex());
const int nPartials = pPropsModel->data(miNbPartials, Qt::DisplayRole).toInt();
// b) Get the number of frames.
const QModelIndex miNbFrames =
pPropsModel->index(0, ATSFilePropertiesItem::eSoundPropNbFrames, QModelIndex());
const int nFrames = pPropsModel->data(miNbFrames, Qt::DisplayRole).toInt();
// c) Get the sampling rate.
const QModelIndex miSampRate =
pPropsModel->index(0, ATSFilePropertiesItem::eSoundPropSamplingRate, QModelIndex());
const double dSampRate = pPropsModel->data(miSampRate, Qt::DisplayRole).toDouble();
// d) Get the frame size (in samples).
const QModelIndex miFrameSize =
pPropsModel->index(0, ATSFilePropertiesItem::eSoundPropFrameSize, QModelIndex());
const int nFrameSize = pPropsModel->data(miFrameSize, Qt::DisplayRole).toInt();
// e) Compute frame duration (s).
const double dFrameDur = nFrameSize / dSampRate;
std::cout << "PartialsSpecgramGraphicsScene::loadPartials : "
<< "partials = " << nPartials
<< ", frames = " << nFrames
<< ", samp. rate = " << dSampRate
<< ", frame size = " << nFrameSize
<< ", frame dur. = " << dFrameDur << std::endl;
// f) For each partial :
_dMaxPartialMeanAmpl = -std::numeric_limits<double>::max();
ATSPartialsProxyModel* pPartsModel = _pModelMgr->partialsModel();
//QGraphicsItemGroup* pgigPartials = new QGraphicsItemGroup;
_pqvgiPartials = new QVector<QVector<PartialsSpecgramGraphicsFrameItem*>* >(nPartials);
for (int nPartInd = 0; nPartInd < nPartials; nPartInd++)
{
// g) Get index of "partial" model item of index "nPartInd".
const QModelIndex miPartial = pPartsModel->index(0, nPartInd, QModelIndex());
// if (nPartInd < 2 || nPartInd >= nPartials - 1)
// std::cout << "Partial #" << nPartInd << " : " << std::endl;
// h) For each frame :
double dAmplSum = 0.0; // Use to compute partial mean amplitude for item Z value.
//QGraphicsItemGroup* pgigPartial = new QGraphicsItemGroup;
(*_pqvgiPartials)[nPartInd] = new QVector<PartialsSpecgramGraphicsFrameItem*>(nFrames);
for (int nFrameInd = 0; nFrameInd < nFrames; nFrameInd++)
{
// i) Get partial amplitude for that frame
const QModelIndex miPartialFrameAmpl =
pPartsModel->index(nFrameInd, ATSPartialItem::ePropAmplitude, miPartial);
const double dAmpl =
pPartsModel->data(miPartialFrameAmpl, Qt::DisplayRole).toDouble();
// i) Don't draw frames with nul amplitude.
if (dAmpl <= 0.0)
{
(*(*_pqvgiPartials)[nPartInd])[nFrameInd] = 0;
continue;
}
else
dAmplSum += dAmpl;
// j) Get partial start time and frequency for that frame
const QModelIndex miPartialFrameStartTime =
pPartsModel->index(nFrameInd, ATSPartialItem::ePropTime, miPartial);
const double dStartTime =
pPartsModel->data(miPartialFrameStartTime, Qt::DisplayRole).toDouble();
const QModelIndex miPartialFrameFreq =
pPartsModel->index(nFrameInd, ATSPartialItem::ePropFrequency, miPartial);
const double dFreq =
pPartsModel->data(miPartialFrameFreq, Qt::DisplayRole).toDouble();
// if (nPartInd < 2 || nPartInd >= nPartials - 1)
// std::cout << " Frame #" << nFrameInd
// << " : dStartTime = " << dStartTime
// << ", dAmpl = " << dAmpl
// << ", dFreq = " << dFreq << std::endl;
// k) Create a line item for a constant frequency and color (amplitude)
// along the whole frame (Note: The item has attached custom data :
// its amplitude, used for selection unhighlight).
// TODO? interpolate frequencies between frames (const for the moment)
PartialsSpecgramGraphicsFrameItem* pgilPartFrame =
new PartialsSpecgramGraphicsFrameItem(0.0, 0.0, dFrameDur, 0.0, dAmpl,
&_cmAmplColorMap, nPartInd, nFrameInd);
pgilPartFrame->setPos(dStartTime, dFreq);
pgilPartFrame->setAmplitude(dAmpl); // Fixme: Already done by ctor ?
pgilPartFrame->setFlag(QGraphicsItem::ItemIsSelectable);
pgilPartFrame->setAcceptHoverEvents(true);
(*(*_pqvgiPartials)[nPartInd])[nFrameInd] = pgilPartFrame;
//pgigPartial->addToGroup(pgilPartFrame);
addItem(pgilPartFrame);
// if (nFrameInd == 0)
// {
// std::cout << "Mouse buttons: " << pgilPartFrame->acceptedMouseButtons()
// << ", AcceptHover: " << pgilPartFrame->acceptHoverEvents()
// << ", Enabled: " << pgilPartFrame->isEnabled() << std::endl;
// }
}
// Set partial group Z value equal to the mean amplitude of its frames :
// that way, stronger partials normaly have visual priority over weaker ones.
//const double dMeanAmpl = dAmplSum / nFrames;
//pgigPartial->setZValue(dMeanAmpl);
//pgigPartial->setData(eMeanAmplitude, dMeanAmpl);
// Update max mean amplitude if lower than this partial's mean amplitude.
//if (dMeanAmpl > _dMaxPartialMeanAmpl)
// _dMaxPartialMeanAmpl = dMeanAmpl;
// Add partial to the partials group.
//pgigPartials->addToGroup(pgigPartial);
}
//addItem(pgigPartials);
}