// Unlink (clone) local hash data. void qtractorMidiClip::unlinkHashData (void) { if (m_pData == NULL) return; if (m_pData->count() < 2) return; m_pData->detach(this); Data *pNewData = new Data(); qtractorMidiSequence *pOldSeq = m_pData->sequence(); qtractorMidiSequence *pNewSeq = pNewData->sequence(); pNewSeq->setName(pOldSeq->name()); pNewSeq->setChannel(pOldSeq->channel()); pNewSeq->setBank(pOldSeq->bank()); pNewSeq->setProg(pOldSeq->prog()); pNewSeq->setTicksPerBeat(pOldSeq->ticksPerBeat()); pNewSeq->setTimeOffset(pOldSeq->timeOffset()); pNewSeq->setTimeLength(pOldSeq->timeLength()); pNewSeq->setDuration(pOldSeq->duration()); pNewSeq->setNoteMin(pOldSeq->noteMin()); pNewSeq->setNoteMax(pOldSeq->noteMax()); pNewSeq->copyEvents(pOldSeq); m_pData = pNewData; m_pData->attach(this); updateHashKey(); insertHashKey(); }
void Position::move(const Move m) { // std::cerr << "Position::move(" << m.toString() << ");" << std::endl; // move_history.emplace_back(m); updateHashKey (m, m.from()); // needs to know square where moving tower is, i.e., pre or post move value -= moveValue (m, m.from()); // " bitb.move(m.from(), m.to(), turn); std::swap(tower[m.to()], tower[m.from()]); // swap towers assert (bitb.isOfficer(m.to(), turn) == tower[m.to()].isOfficer()); if (m.isOnPromotionSquare() && !tower[m.to()].isOfficer()) { // move promotes tower[m.to()].promote(); bitb.bits[turn+2] |= (1<<m.to()); } if (m.isJump()) { unsigned int other = !turn; for (unsigned int j = 0; j<m.size(); j++) { // captures unsigned int over = m.over(j); const bool iso = tower[over].isOfficer(); tower[m.to()].push_back(tower[over].pop()); // add captured stone to turns tower if ( iso && !tower[over].isOfficer()) bitb.degrade (over, other); else if (!iso && tower[over].isOfficer()) bitb.promote (over, other); // bitb.setGrade(over, other, tower[over].isOfficer()); if (tower[over].empty()) { // last stone of tower captured bitb.erase(over, other); } else if (tower[over].color() == turn) { // color switch bitb.switchColor(over, turn); } // if color remains same after hit, nothing to do. } } checkTowerKeys (m); // beside constructor only place where hash keys for a tower may not exist updateHashKey (m, m.to()); // needs to know square where moving tower is, i.e., pre or post move value += moveValue (m, m.to()); // " pos_stack.emplace(pos_key); assert(pos_key == generateKey()); assert(value == evaluateTowers()); switchTurn(); }
void Position::undo(const Move m) { // std::cerr << "Position::undo(" << m.toString() << ");" << std::endl; // move_history.pop_back(); switchTurn(); value -= moveValue (m, m.to()); updateHashKey (m, m.to()); bitb.move(m.to(), m.from(), turn); std::swap(tower[m.to()], tower[m.from()]); if (m.isPromotion()) { tower[m.from()].degrade(); bitb.bits[turn+2] &= ~(1<<m.from()); } if (m.isJump()) { unsigned int other = !turn; for (unsigned int j = m.size(); j; j--) { // cause unsigned unsigned int over = m.over(j-1); // substract here if (tower[over].empty()) { // last stone of tower was captured bitb.set(over, tower[m.from()].back()); } else if (tower[over].color() == turn) { // color change bitb.switchColor(over, other); } const bool iso = tower[over].isOfficer(); tower[over].push(tower[m.from()].pop_back()); // restore stone on top of tower if ( iso && !tower[over].isOfficer()) bitb.degrade (over, other); else if (!iso && tower[over].isOfficer()) bitb.promote (over, other); } } updateHashKey (m, m.from()); value += moveValue (m, m.from()); pos_stack.pop(); assert(pos_key == generateKey()); assert(value == evaluateTowers()); }
// Relink local hash data. void qtractorMidiClip::relinkHashData (void) { if (m_pData == NULL) return; if (m_pData->count() > 1) return; removeHashKey(); updateHashKey(); Data *pNewData = g_hashTable.value(*m_pKey, NULL); if (pNewData == NULL) { delete m_pKey; m_pKey = NULL; } else { m_pData->detach(this); delete m_pData; m_pData = pNewData; m_pData->attach(this); } insertHashKey(); }
// The main use method. bool qtractorMidiClip::openMidiFile ( const QString& sFilename, int iTrackChannel, int iMode ) { closeMidiFile(); qtractorTrack *pTrack = track(); if (pTrack == NULL) return false; qtractorSession *pSession = pTrack->session(); if (pSession == NULL) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::openMidiFile(\"%s\", %d, %d)", this, sFilename.toUtf8().constData(), iTrackChannel, iMode); #endif // Check file primordial state... const bool bWrite = (iMode & qtractorMidiFile::Write); // Set local properties... setFilename(sFilename); setDirty(false); // Register file path... pSession->files()->addClipItem(qtractorFileList::Midi, this, bWrite); // New key-data sequence... if (!bWrite) { m_pKey = new Key(this); m_pData = g_hashTable.value(*m_pKey, NULL); if (m_pData) { m_pData->attach(this); qtractorMidiSequence *pSeq = m_pData->sequence(); // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(pSeq->name()); if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Uh oh... m_playCursor.reset(pSeq); m_drawCursor.reset(pSeq); return true; } } // Create and open up the real MIDI file... m_pFile = new qtractorMidiFile(); if (!m_pFile->open(sFilename, iMode)) { delete m_pFile; m_pFile = NULL; return false; } // Initialize MIDI event container... m_pData = new Data(); m_pData->attach(this); qtractorMidiSequence *pSeq = m_pData->sequence(); pSeq->clear(); pSeq->setTicksPerBeat(pSession->ticksPerBeat()); const unsigned long iClipStart = clipStart(); const unsigned long iClipOffset = clipOffset(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(iClipStart); const unsigned long t0 = pNode->tickFromFrame(iClipStart); const unsigned long iClipOffset2 = iClipStart + iClipOffset; // pNode = cursor.seekFrame(iClipOffset2); pSeq->setTimeOffset(pNode->tickFromFrame(iClipOffset2) - t0); const unsigned long iClipLength = clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; pNode = cursor.seekFrame(iClipEnd); pSeq->setTimeLength(pNode->tickFromFrame(iClipEnd) - t0); // Initial statistics... pSeq->setNoteMin(m_noteMin); pSeq->setNoteMax(m_noteMax); // Are we on a pre-writing status? if (bWrite) { // On write mode, iTrackChannel holds the SMF format, // so we'll convert it here as properly. const unsigned short iFormat = qtractorMidiClip::defaultFormat(); unsigned short iTracks = 1; if (iFormat == 1) { // SMF format 1 (2 tracks, 1 channel) iTrackChannel = 1; ++iTracks; } // That's it. setFormat(iFormat); // Write SMF header... if (m_pFile->writeHeader(iFormat, iTracks, pSeq->ticksPerBeat())) { // Set initial local properties... if (m_pFile->tempoMap()) { m_pFile->tempoMap()->fromTimeScale( pSession->timeScale(), pSeq->timeOffset()); } } // And initial clip name... pSeq->setName(shortClipName(QFileInfo(m_pFile->filename()).baseName())); pSeq->setChannel(pTrack->midiChannel()); // Nothing more as for writing... } else { // On read mode, SMF format is properly given by open file. setFormat(m_pFile->format()); // Read the event sequence in... m_pFile->readTrack(pSeq, iTrackChannel); // For immediate feedback, once... m_noteMin = pSeq->noteMin(); m_noteMax = pSeq->noteMax(); // FIXME: On demand, set session time properties from MIDI file... if (m_bSessionFlag) { #if 0 // Import eventual SysEx setup... // - take care that given track might not be currently open, // so that we'll resolve MIDI output bus somehow... qtractorMidiBus *pMidiBus = NULL; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { pMidiBus = static_cast<qtractorMidiBus *> ( pMidiEngine->findOutputBus(pTrack->outputBusName())); if (pMidiBus == NULL) { for (qtractorBus *pBus = pMidiEngine->buses().first(); pBus; pBus = pBus->next()) { if (pBus->busMode() & qtractorBus::Output) { pMidiBus = static_cast<qtractorMidiBus *> (pBus); break; } } } } // Import eventual SysEx setup... if (pMidiBus) pMidiBus->importSysexList(pSeq); #endif // Import tempo map as well... qtractorMidiFileTempo *pTempoMap = m_pFile->tempoMap(); if (pTempoMap) { pTempoMap->intoTimeScale(pSession->timeScale(), t0); pSession->updateTimeScale(); } // Reset session flag now. m_bSessionFlag = false; } // We should have events, otherwise this clip is of no use... //if (m_pSeq->events().count() < 1) // return false; } // Actual track-channel is set by now... setTrackChannel(iTrackChannel); // Make it a brand new revision... // setRevision(1); // Default clip length will be whole sequence duration. if (iClipLength == 0) { const unsigned long t1 = t0 + pSeq->timeLength(); setClipLength(pSession->frameFromTick(t1) - iClipStart); } // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(pSeq->name()); if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Uh oh... m_playCursor.reset(pSeq); m_drawCursor.reset(pSeq); // Something might have changed... updateHashKey(); insertHashKey(); // Update/reset MIDI clip editor if any... if (m_pMidiEditorForm) m_pMidiEditorForm->setup(this); return true; }