// PartialsSpecgramGraphicsScene class implementation // // The QGraphicsScene-derived partials spectrogram view // // QATSH Copyright 2009 Jean-Philippe MEURET #include #include #include #include #include #include #include #include #include #include #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(qmiSelected.internalPointer()); if (pmiSelected) { // std::cout << " Selecting " // << qmiSelected.row() << ',' << qmiSelected.column() // << ':' << pmiSelected->type() << std::endl; switch (pmiSelected->type()) { case ATSModelItem::ePartial: { const QVector* 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(qmiDeselected.internalPointer()); if (pmiDeselected) { // std::cout << " Deselecting " // << qmiDeselected.row() << ',' << qmiDeselected.column() // << ':' << pmiDeselected->type() << std::endl; switch (pmiDeselected->type()) { case ATSModelItem::ePartial: { const QVector* 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() << 2 << 5, QList() << 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() << 2 << 5, QList() << 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::max(); ATSPartialsProxyModel* pPartsModel = _pModelMgr->partialsModel(); //QGraphicsItemGroup* pgigPartials = new QGraphicsItemGroup; _pqvgiPartials = new QVector* >(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(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); }