diff options
Diffstat (limited to 'muse_qt4_evolution/muse/midiedit/trackpattern.cpp')
-rw-r--r-- | muse_qt4_evolution/muse/midiedit/trackpattern.cpp | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/muse_qt4_evolution/muse/midiedit/trackpattern.cpp b/muse_qt4_evolution/muse/midiedit/trackpattern.cpp new file mode 100644 index 00000000..d50fffd0 --- /dev/null +++ b/muse_qt4_evolution/muse/midiedit/trackpattern.cpp @@ -0,0 +1,627 @@ +#include "trackpattern.h" +#include "event.h" +#include "al/sig.h" + +class EventList; + +#define MAX(x,y) (x>y?x:y) + +#define EMPTYCHAR "-" +#define NONREADCHAR "*" +#define SPACECHAR "-" +#define PLUSCHAR "+" +#define STOPCHAR "=" +#define SEPCHAR " " +//#define FONT "Console" +#define FONT "Monospace" +//#define FONT "MiscFixed" +#define FONT_HEIGHT 14 +#define OFFSET_HEIGHT 3 +#define OFFSET_Y 3 + +//---------------------------------------------------------- +// EventPat +// has to be derived, can be note or control +//---------------------------------------------------------- +EventPat::EventPat(bool e, bool r) {_isEmpty = e; _isReadable = r;} +EventPat::EventPat() {EventPat(true, true);} +EventPat::~EventPat() {} + +void EventPat::setEmpty(bool e) {_isEmpty = e;} +bool EventPat::getEmpty() {return _isEmpty;} +void EventPat::setReadable(bool r) {_isReadable = r;} +bool EventPat::getReadable() {return _isReadable;} + +//---------------------------------------------------------- +// VoiceEventPat +//---------------------------------------------------------- +VoiceEventPat::VoiceEventPat(int n, int v):EventPat(false, true) { + _noteNum = n; + _velocity = v; +} + +VoiceEventPat::VoiceEventPat(bool e, bool r):EventPat(e, r) {} + +VoiceEventPat::VoiceEventPat():EventPat(true, true) {} + +VoiceEventPat::~VoiceEventPat() {} + + +void VoiceEventPat::setNoteNum(int n) { _noteNum = n; } + +int VoiceEventPat::getNoteNum() { return _noteNum; } + +void VoiceEventPat::setVelocity(int n) { _velocity = n; } + +int VoiceEventPat::getVelocity() { return _velocity; } + +QString VoiceEventPat::str() { + if(_isEmpty) { + return QString(EMPTYCHAR EMPTYCHAR EMPTYCHAR EMPTYCHAR) + + QString(SEPCHAR) + QString(EMPTYCHAR EMPTYCHAR EMPTYCHAR); + } + else if(_isReadable) { + if(_velocity==0) { + return QString(STOPCHAR STOPCHAR STOPCHAR STOPCHAR) + + QString(SEPCHAR) + QString(EMPTYCHAR EMPTYCHAR EMPTYCHAR); + } + else { + int octave = _noteNum/12 - 2; + int note = _noteNum%12; + QString sNote; + switch(note) { + case 0: sNote = QString("C") + QString(SPACECHAR); + break; + case 1: sNote = QString("C#"); + break; + case 2: sNote = QString("D") + QString(SPACECHAR); + break; + case 3: sNote = QString("D#"); + break; + case 4: sNote = QString("E") + QString(SPACECHAR); + break; + case 5: sNote = QString("F") + QString(SPACECHAR); + break; + case 6: sNote = QString("F#"); + break; + case 7: sNote = QString("G") + QString(SPACECHAR); + break; + case 8: sNote = QString("G#"); + break; + case 9: sNote = QString("A") + QString(SPACECHAR); + break; + case 10: sNote = QString("A#"); + break; + case 11: sNote = QString("F") + QString(SPACECHAR); + break; + default: + printf("VoiceEventPat::str() Error : case note not treated\n"); + break; + } + QString sOctave; + sOctave.setNum(octave); + if(octave>=0) { + sOctave = QString(PLUSCHAR) + sOctave; + } + QString sVel; + sVel.setNum(_velocity); + if(_velocity<10) { + sVel = QString("00") + sVel; + } + else if(_velocity<100) { + sVel = QString("0") + sVel; + } + return sNote + sOctave + QString(SEPCHAR) + sVel; + } + } + else { + return QString(NONREADCHAR NONREADCHAR NONREADCHAR NONREADCHAR) + + QString(SEPCHAR) + QString(NONREADCHAR NONREADCHAR NONREADCHAR); + } +} + +//---------------------------------------------------------- +// CtrlEventPat +//---------------------------------------------------------- +CtrlEventPat::CtrlEventPat(int c, int v):EventPat(false, true) { + _ctrlNum = c; + _value = v; +} + +CtrlEventPat::CtrlEventPat():EventPat(true, true) {} + +CtrlEventPat::~CtrlEventPat() {} + + +void CtrlEventPat::setCtrlNum(int n) { _ctrlNum = n; } + +int CtrlEventPat::getCtrlNum() { return _ctrlNum; } + +void CtrlEventPat::setValue(int n) { _value = n; } + +int CtrlEventPat::getValue() { return _value; } + +QString CtrlEventPat::str() { + //TODO + if(_isReadable) { + QString sCtrl; + sCtrl.setNum(_ctrlNum); + QString s = " "; + QString sVal; + sVal.setNum(_value); + return sCtrl + s + sVal; + } + else { + QString s = "***"; + return s; + } +} + +//---------------------------------------------------------- +// BasePat +//---------------------------------------------------------- +BasePat::BasePat() { +} +BasePat::BasePat(QString name, unsigned firstTick, + unsigned lastTick, int quant) { + _name = name; + _firstTick = firstTick; + _lastTick = lastTick; + _quant = quant; +} + +BasePat::~BasePat() {} + +QString BasePat::getName() { + return _name; +} + +bool BasePat::isRow(unsigned tick) { + Pos p = AL::sigmap.raster(_firstTick + tick, _quant); + return p.tick() == _firstTick + tick; +} + +unsigned BasePat::tick2row(unsigned tick) { + return (_firstTick + tick) / _quant - (_firstTick / _quant); +} + +//---------------------------------------------------------- +// VoicePat +//---------------------------------------------------------- +VoicePat::VoicePat(QString name, unsigned firstTick, unsigned lastTick, + int quant):BasePat(name, firstTick, lastTick, quant) { + _events = new EventList(); +} + +VoicePat::~VoicePat() { + delete(_events); +} + + +std::vector<VoiceEventPat*> VoicePat::getEventsCol() { + return _eventsCol; +} + +bool VoicePat::add(const Event* ev, unsigned tick) { + Event* e = new Event(*ev); + if(isFreeSpace(e, tick)) { + //add into the list of events + _events->add(*e, tick); + //add the begin tick into the column + unsigned beginRow = tick2row(tick); + if(beginRow >= _eventsCol.size()) { + for(unsigned i = _eventsCol.size(); i <= beginRow; i++) { + //empty voice event + _eventsCol.push_back(new VoiceEventPat()); + } + } + VoiceEventPat* vbep; + if(isRow(tick)) vbep = new VoiceEventPat(e->pitch(), e->velo()); + else vbep = new VoiceEventPat(false, false); //non-readable + if(_eventsCol[beginRow]) delete(_eventsCol[beginRow]); + _eventsCol[beginRow] = vbep; + + //add the end tick into the column + unsigned endTick = e->lenTick() + tick; + unsigned endRow = tick2row(endTick); + if(endRow >= _eventsCol.size()) { + for(unsigned i = _eventsCol.size(); i <= endRow; i++) { + //empty voice event + _eventsCol.push_back(new VoiceEventPat()); + } + } + VoiceEventPat* veep; + if(isRow(endTick)) veep = new VoiceEventPat(e->pitch(), 0); + else veep = new VoiceEventPat(false, false); //non-readable + if(_eventsCol[endRow] + && + (_eventsCol[endRow]->getEmpty() || tick2row(endTick)==tick2row(tick))) { + delete(_eventsCol[endRow]); + _eventsCol[endRow] = veep; + } + return true; + } + else { + return false; + } +} + +bool VoicePat::isFreeSpace(const Event* e, unsigned tick) { + bool isFree = true; + for(ciEvent ce = _events->begin(); ce != _events->end(); ce++) { + const Event* cevent = &ce->second; + unsigned beginTick = ce->first; + unsigned endTick = ce->first + cevent->lenTick(); + isFree = (endTick <= tick) || (beginTick >= tick + e->lenTick()); + if(!isFree) break; + } + return isFree; +} + +//---------------------------------------------------------- +// CtrlPat +//---------------------------------------------------------- +CtrlPat::CtrlPat(QString /*name*/) { +} + +CtrlPat::~CtrlPat() {} + +//---------------------------------------------------------- +// BaseTrackPat +//---------------------------------------------------------- +BaseTrackPat::BaseTrackPat(QMainWindow* parent, unsigned anr) { + _parent = parent; + _tree = new QTreeWidget(this); + + _absoluteNbrRow = anr; + + _update = false; + + connect(_tree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), + SLOT(currentItemChanged(QTreeWidgetItem*))); + connect(_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), + SLOT(currentItemChanged(QTreeWidgetItem*))); + connect(_parent, SIGNAL(signalMoveCurrentRow(unsigned)), this, + SLOT(moveRowFromSignal(unsigned))); + //connect signalSongChanged to update the pattern + connect(parent, SIGNAL(signalSongChanged(int)), this, SLOT(updatePattern(int))); +} + +BaseTrackPat::~BaseTrackPat() { +} + +void BaseTrackPat::setRowMag() { + _rowMag = (unsigned) height()/_fontHeight - OFFSET_Y; + _lastRow = _firstRow + _rowMag - 1; +} +void BaseTrackPat::setFirstRow(unsigned f) { + _firstRow = f; + _lastRow = f + _rowMag - 1; +} +void BaseTrackPat::setRelativeCurrentRow(unsigned r) { + _relativeCurrentRow = r; + _absoluteCurrentRow = r + _firstRow; +} +void BaseTrackPat::setAbsoluteCurrentRow(unsigned a) { + _absoluteCurrentRow = a; + _relativeCurrentRow = a - _firstRow; +} + +unsigned BaseTrackPat::getRowMag() { + return _rowMag; +} +unsigned BaseTrackPat::getFirstRow() { + return _firstRow; +} +unsigned BaseTrackPat::getLastRow() { + return _lastRow; +} +unsigned BaseTrackPat::getRelativeCurrentRow() { + return _relativeCurrentRow; +} +unsigned BaseTrackPat::getAbsoluteCurrentRow() { + return _absoluteCurrentRow; +} + +unsigned BaseTrackPat::getAbsoluteNbrRow() { + return _absoluteNbrRow; +} + +void BaseTrackPat::moveRelativeCurrentRow(unsigned newIndex) { + if(newIndex==0 && getFirstRow()>0) { + setFirstRow(getFirstRow() - 1); + setRelativeCurrentRow(newIndex + 1); + _update = true; + } + else if(newIndex==getRowMag()-1 && getLastRow()<_absoluteNbrRow-1) { + setFirstRow(getFirstRow() + 1); + setRelativeCurrentRow(newIndex - 1); + _update = true; + } + else setRelativeCurrentRow(newIndex); +} + +//void BaseTrackPat::itemSelectionChanged() { +void BaseTrackPat::currentItemChanged(QTreeWidgetItem* nitem) { + int index; + if(nitem) { + index = _tree->indexOfTopLevelItem(nitem); + + emit moveCurrentRow(index); + } +} + +void BaseTrackPat::moveRowFromSignal(unsigned index) { + moveRelativeCurrentRow(index); + if(_update==true) { + fillPattern(); + _update = false; + } + selectCurrentRow(); +} + +void BaseTrackPat::resizeEvent(QResizeEvent* /*event*/) { + setRowMag(); + fillPattern(); + selectCurrentRow(); +} + +void BaseTrackPat::selectCurrentRow() { + unsigned rcr = getRelativeCurrentRow(); + if(rcr < getRowMag()) { + QTreeWidgetItem* item = _tree->topLevelItem(getRelativeCurrentRow()); + if(item) { + _tree->blockSignals(true); + item->setSelected(true); + _tree->setCurrentItem(item); + _tree->blockSignals(false); + } + } +} + +void BaseTrackPat::updatePattern(int /*type*/) { + clearMatrix(); + buildMatrix(); + fillPattern(); + selectCurrentRow(); +} + +//---------------------------------------------------------- +// TrackPattern +//---------------------------------------------------------- +TrackPattern::TrackPattern(QMainWindow* parent, QString name, + unsigned firstTick, unsigned lastTick, + int quant, PartList* pl, MidiTrack* t, unsigned anr) + : BaseTrackPat(parent, anr), BasePat(name, firstTick, lastTick, quant) { + + //set attributs + _track = t; + + //build the list of parts belonging to track t + _partList = new PartList; + for(ciPart p = pl->begin(); p != pl->end(); p++) { + Part* part = p->second; + if(t==part->track()) + _partList->add(part); + } + + //build the matrix of events + buildMatrix(); + + //configure and add the dockWidget + setWindowTitle(_track->name()); + setFeatures(QDockWidget::DockWidgetClosable |QDockWidget::DockWidgetMovable); + parent->addDockWidget(Qt::LeftDockWidgetArea, this, Qt::Horizontal); + + //build the treeWidget + _tree->setColumnCount(_voiceColumns.size() + _ctrlColumns.size()); + QStringList headerLabels; + for(unsigned i = 0; i < _voiceColumns.size(); i++) { + headerLabels += QStringList(_voiceColumns[i]->getName()); + } + for(unsigned i = 0; i < _ctrlColumns.size(); i++) { + //TODO CTRL + //headerLabels += QStringList(_ctrlColumns[i]->getName()); + } + _tree->setHeaderLabels(headerLabels); + //set some display properties + _tree->setRootIsDecorated(false); + _tree->setUniformRowHeights(true); + _tree->setAlternatingRowColors(true); + QFont font =_tree->font(); + font.setFamily(FONT); + _tree->setFont(font); + font.setPixelSize(FONT_HEIGHT); + _fontHeight = font.pixelSize() + OFFSET_HEIGHT; + setWidget(_tree); + + //set the range of rows to display + setFirstRow(10); //TODO : choose accordingly to current position of muse song + setAbsoluteCurrentRow(10); //TODO : the same + setRowMag(); + + //fill the treeWidget + fillPattern(); + selectCurrentRow(); + + //Resize the columns + for(unsigned i = 0; i < _voiceColumns.size(); i++) + _tree->resizeColumnToContents(i); +} + +TrackPattern::~TrackPattern() { +} + +void TrackPattern::add(const Event* e, unsigned tick) { + if(e->isNote()) { + bool success = false; + for(unsigned i = 0; i < _voiceColumns.size(); i++) { + success = _voiceColumns[i]->add(e, tick); + if(success) break; + } + if(!success) { + QString voiceName; + voiceName.setNum(_voiceColumns.size()); + voiceName = QString("Voice " + voiceName); + VoicePat* vp = new VoicePat(voiceName, _firstTick, 0, _quant); + _voiceColumns.push_back(vp); + bool success = vp->add(e, tick); + if(!success) printf("Error TrackPattern::add\n"); + } + } + else { + //TODO Ctrl + } +} + +void TrackPattern::setQuant(int /*quant*/) { + //TODO +} + +void TrackPattern::clearMatrix() { + _voiceColumns.clear(); + _ctrlColumns.clear(); +} + +void TrackPattern::buildMatrix() { + for(ciPart p = _partList->begin(); p != _partList->end(); p++) { + Part* part = p->second; + EventList* events = part->events(); + for(ciEvent e = events->begin(); e != events->end(); e++) { + const Event* event = &e->second; + unsigned rescaledTick = part->tick() + event->tick() - _firstTick; + add(event, rescaledTick); + } + } +} + +void TrackPattern::fillPattern() { + _tree->blockSignals(true); + + _tree->clear(); + for(unsigned i = 0; i < _voiceColumns.size(); i++) { + for(unsigned j = getFirstRow(); j <= getLastRow(); j++) { + QTreeWidgetItem* item = _tree->topLevelItem(j - getFirstRow()); + if(!item) item = new QTreeWidgetItem(_tree); + VoiceEventPat* vep = (_voiceColumns[i]->getEventsCol())[j]; + if(vep) item->setText(i, vep->str()); + } + } + for(unsigned i = 0; i < _ctrlColumns.size(); i++) { + //TODO CTRL + } + + _tree->blockSignals(false); +} + +//--------------------------------------------------------------- +// TimingEvent +//--------------------------------------------------------------- +TimingEvent::TimingEvent(unsigned row) { + _row = row; +} +TimingEvent::~TimingEvent() { +} + +void TimingEvent::setBarBeatTick(unsigned tick) { + AL::sigmap.tickValues(tick, &_bar, &_beat, &_tick); +} + +QString TimingEvent::barBeatTickStr() { + QString barS; + barS.setNum(_bar + 1); + if(_bar<10) barS = QString("000") + barS; + else if(_bar<100) barS = QString("00") + barS; + else if(_bar<1000) barS = QString("0") + barS; + QString beatS; + beatS.setNum(_beat + 1); + if(_beat<10) beatS = QString("0") + beatS; + QString tickS; + tickS.setNum(_tick); + if(_tick<10) tickS = QString("00") + tickS; + else if(_tick<100) tickS = QString("0") + tickS; + return barS + QString(":") + beatS + QString(":") + tickS; +} + +QString TimingEvent::rowStr() { + QString r; + r.setNum(_row); + if(_row<10) r = QString("00") + r; + else if(_row<100) r = QString("0") + r; + return r; +} + +//--------------------------------------------------------------- +// TimingPattern +//--------------------------------------------------------------- +TimingPattern::TimingPattern(QMainWindow* parent, QString name, + unsigned firstTick, unsigned lastTick, int quant) + : BasePat(name, firstTick, lastTick, quant), BaseTrackPat(parent) { + //build the timing matrix + buildMatrix(); + + //configure and add the dockWidget + setWindowTitle(name); + setFeatures(QDockWidget::DockWidgetClosable |QDockWidget::DockWidgetMovable); + parent->addDockWidget(Qt::LeftDockWidgetArea, this, Qt::Horizontal); + + //build the treeWidget + QStringList headerLabels; + _tree->setHeaderLabels(QStringList("bar:bt:tick") + QStringList("row")); + _tree->setHeaderLabels(headerLabels); + //set some display properties + _tree->setRootIsDecorated(false); + _tree->setUniformRowHeights(true); + _tree->setAlternatingRowColors(true); + QFont font =_tree->font(); + font.setFamily(FONT); + _tree->setFont(font); + font.setPixelSize(FONT_HEIGHT); + _fontHeight = font.pixelSize() + OFFSET_HEIGHT; + setWidget(_tree); + + //set the range of rows to display + setFirstRow(10); //TODO : choose accordingly to current position of muse song + setAbsoluteCurrentRow(10); //TODO : the same + setRowMag(); + + //fill the treeWidget + fillPattern(); + selectCurrentRow(); + + //resize the columns + for(int i = 0; i < _tree->columnCount(); i++) + _tree->resizeColumnToContents(i); +} + +TimingPattern::~TimingPattern() { +} + +void TimingPattern::clearMatrix() { + _timingEvents.clear(); +} + +void TimingPattern::buildMatrix() { + for(unsigned tick = _firstTick; tick <= _lastTick; tick++) { + if(isRow(tick)) { + TimingEvent* te = new TimingEvent(tick2row(tick) - tick2row(_firstTick)); + te->setBarBeatTick(tick); + _timingEvents.push_back(te); + } + } + _absoluteNbrRow = _timingEvents.size(); +} + +void TimingPattern::fillPattern() { + _tree->blockSignals(true); + + _tree->clear(); + for(unsigned i = getFirstRow(); i <= getLastRow(); i++) { + QTreeWidgetItem* item = new QTreeWidgetItem(_tree); + TimingEvent* te = _timingEvents[i]; + item->setText(0, te->barBeatTickStr()); + item->setText(1, te->rowStr()); + } + + _tree->blockSignals(false); +} |