void Fader::mousePressEvent( QMouseEvent* mouseEvent ) { if( mouseEvent->button() == Qt::LeftButton && ! ( mouseEvent->modifiers() & Qt::ControlModifier ) ) { AutomatableModel *thisModel = model(); if( thisModel ) { thisModel->addJournalCheckPoint(); thisModel->saveJournallingState( false ); } if( mouseEvent->y() >= knobPosY() - ( *m_knob ).height() && mouseEvent->y() < knobPosY() ) { updateTextFloat(); s_textFloat->show(); m_moveStartPoint = mouseEvent->globalY(); m_startValue = model()->value(); mouseEvent->accept(); } else { m_moveStartPoint = -1; } } else { AutomatableModelView::mousePressEvent( mouseEvent ); } }
void AutomatableModelViewSlots::execConnectionDialog() { // TODO[pg]: Display a dialog with list of controllers currently in the song // in addition to any system MIDI controllers AutomatableModel* m = m_amv->modelUntyped(); m->displayName(); ControllerConnectionDialog d( (QWidget*) engine::mainWindow(), m ); if( d.exec() == 1 ) { // Actually chose something if( d.chosenController() ) { // Update if( m->controllerConnection() ) { m->controllerConnection()->setController( d.chosenController() ); } // New else { ControllerConnection* cc = new ControllerConnection( d.chosenController() ); m->setControllerConnection( cc ); //cc->setTargetName( m->displayName() ); } } // no controller, so delete existing connection else { removeConnection(); } } }
bool Controller::hasModel( const Model * m ) { QObjectList chldren = children(); for( int i = 0; i < chldren.size(); ++i ) { QObject * c = chldren.at(i); AutomatableModel * am = qobject_cast<AutomatableModel*>(c); if( am != NULL ) { if( am == m ) { return true; } ControllerConnection * cc = am->controllerConnection(); if( cc != NULL ) { if( cc->getController()->hasModel( m ) ) { return true; } } } } return false; }
float AutomatableModel::controllerValue( int frameOffset ) const { if( m_controllerConnection ) { float v = 0; switch(m_scaleType) { case Linear: v = minValue<float>() + ( range() * controllerConnection()->currentValue( frameOffset ) ); break; case Logarithmic: v = logToLinearScale( controllerConnection()->currentValue( frameOffset )); break; default: qFatal("AutomatableModel::controllerValue(int)" "lacks implementation for a scale type"); break; } if( typeInfo<float>::isEqual( m_step, 1 ) && m_hasStrictStepSize ) { return qRound( v ); } return v; } AutomatableModel* lm = m_linkedModels.first(); if( lm->controllerConnection() ) { return fittedValue( lm->controllerValue( frameOffset ) ); } return fittedValue( lm->m_value ); }
void AutomatableModelViewSlots::removeConnection() { AutomatableModel* m = m_amv->modelUntyped(); if( m->controllerConnection() ) { delete m->controllerConnection(); m->setControllerConnection( NULL ); } }
void Fader::mouseReleaseEvent( QMouseEvent * mouseEvent ) { if( mouseEvent && mouseEvent->button() == Qt::LeftButton ) { AutomatableModel *thisModel = model(); if( thisModel ) { thisModel->restoreJournallingState(); } } s_textFloat->hide(); }
void Knob::mouseReleaseEvent( QMouseEvent* event ) { if( event && event->button() == Qt::LeftButton ) { AutomatableModel *thisModel = model(); if( thisModel ) { thisModel->restoreJournallingState(); } } m_buttonPressed = false; emit sliderReleased(); QApplication::restoreOverrideCursor(); s_textFloat->hide(); }
void knob::dropEvent( QDropEvent * _de ) { QString type = stringPairDrag::decodeKey( _de ); QString val = stringPairDrag::decodeValue( _de ); if( type == "float_value" ) { model()->setValue( val.toFloat() ); _de->accept(); } else if( type == "automatable_model" ) { AutomatableModel * mod = dynamic_cast<AutomatableModel *>( engine::projectJournal()-> journallingObject( val.toInt() ) ); if( mod != NULL ) { AutomatableModel::linkModels( model(), mod ); mod->setValue( model()->value() ); } } }
void Knob::mousePressEvent( QMouseEvent * _me ) { if( _me->button() == Qt::LeftButton && ! ( _me->modifiers() & Qt::ControlModifier ) && ! ( _me->modifiers() & Qt::ShiftModifier ) ) { AutomatableModel *thisModel = model(); if( thisModel ) { thisModel->addJournalCheckPoint(); thisModel->saveJournallingState( false ); } const QPoint & p = _me->pos(); m_origMousePos = p; m_mouseOffset = QPoint(0, 0); m_leftOver = 0.0f; emit sliderPressed(); QApplication::setOverrideCursor( Qt::BlankCursor ); s_textFloat->setText( displayValue() ); s_textFloat->moveGlobal( this, QPoint( width() + 2, 0 ) ); s_textFloat->show(); m_buttonPressed = true; } else if( _me->button() == Qt::LeftButton && gui->mainWindow()->isShiftPressed() == true ) { new StringPairDrag( "float_value", QString::number( model()->value() ), QPixmap(), this ); } else { FloatModelView::mousePressEvent( _me ); } }
bool MidiImport::readSMF( TrackContainer* tc ) { QString filename = file().fileName(); closeFile(); const int preTrackSteps = 2; QProgressDialog pd( TrackContainer::tr( "Importing MIDI-file..." ), TrackContainer::tr( "Cancel" ), 0, preTrackSteps, gui->mainWindow() ); pd.setWindowTitle( TrackContainer::tr( "Please wait..." ) ); pd.setWindowModality(Qt::WindowModal); pd.setMinimumDuration( 0 ); pd.setValue( 0 ); Alg_seq_ptr seq = new Alg_seq(filename.toLocal8Bit(), true); seq->convert_to_beats(); pd.setMaximum( seq->tracks() + preTrackSteps ); pd.setValue( 1 ); // 128 CC + Pitch Bend smfMidiCC ccs[129]; smfMidiChannel chs[256]; MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel(); AutomationPattern * timeSigNumeratorPat = AutomationPattern::globalAutomationPattern( &timeSigMM.numeratorModel() ); AutomationPattern * timeSigDenominatorPat = AutomationPattern::globalAutomationPattern( &timeSigMM.denominatorModel() ); // TODO: adjust these to Time.Sig changes double beatsPerTact = 4; double ticksPerBeat = DefaultTicksPerTact / beatsPerTact; // Time-sig changes Alg_time_sigs * timeSigs = &seq->time_sig; for( int s = 0; s < timeSigs->length(); ++s ) { Alg_time_sig timeSig = (*timeSigs)[s]; // Initial timeSig, set song-default value if(/* timeSig.beat == 0*/ true ) { // TODO set song-global default value printf("Another timesig at %f\n", timeSig.beat); timeSigNumeratorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.num ); timeSigDenominatorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.den ); } else { } } pd.setValue( 2 ); // Tempo stuff AutomationPattern * tap = tc->tempoAutomationPattern(); if( tap ) { tap->clear(); Alg_time_map * timeMap = seq->get_time_map(); Alg_beats & beats = timeMap->beats; for( int i = 0; i < beats.len - 1; i++ ) { Alg_beat_ptr b = &(beats[i]); double tempo = ( beats[i + 1].beat - b->beat ) / ( beats[i + 1].time - beats[i].time ); tap->putValue( b->beat * ticksPerBeat, tempo * 60.0 ); } if( timeMap->last_tempo_flag ) { Alg_beat_ptr b = &( beats[beats.len - 1] ); tap->putValue( b->beat * ticksPerBeat, timeMap->last_tempo * 60.0 ); } } // Song events for( int e = 0; e < seq->length(); ++e ) { Alg_event_ptr evt = (*seq)[e]; if( evt->is_update() ) { printf("Unhandled SONG update: %d %f %s\n", evt->get_type_code(), evt->time, evt->get_attribute() ); } } // Tracks for( int t = 0; t < seq->tracks(); ++t ) { QString trackName = QString( tr( "Track" ) + " %1" ).arg( t ); Alg_track_ptr trk = seq->track( t ); pd.setValue( t + preTrackSteps ); for( int c = 0; c < 129; c++ ) { ccs[c].clear(); } // Now look at events for( int e = 0; e < trk->length(); ++e ) { Alg_event_ptr evt = (*trk)[e]; if( evt->chan == -1 ) { bool handled = false; if( evt->is_update() ) { QString attr = evt->get_attribute(); if( attr == "tracknames" && evt->get_update_type() == 's' ) { trackName = evt->get_string_value(); handled = true; } } if( !handled ) { // Write debug output printf("MISSING GLOBAL HANDLER\n"); printf(" Chn: %d, Type Code: %d, Time: %f", (int) evt->chan, evt->get_type_code(), evt->time ); if ( evt->is_update() ) { printf( ", Update Type: %s", evt->get_attribute() ); if ( evt->get_update_type() == 'a' ) { printf( ", Atom: %s", evt->get_atom_value() ); } } printf( "\n" ); } } else if( evt->is_note() && evt->chan < 256 ) { smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); Alg_note_ptr noteEvt = dynamic_cast<Alg_note_ptr>( evt ); int ticks = noteEvt->get_duration() * ticksPerBeat; Note n( (ticks < 1 ? 1 : ticks ), noteEvt->get_start_time() * ticksPerBeat, noteEvt->get_identifier() - 12, noteEvt->get_loud()); ch->addNote( n ); } else if( evt->is_update() ) { smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); double time = evt->time*ticksPerBeat; QString update( evt->get_attribute() ); if( update == "programi" ) { long prog = evt->get_integer_value(); if( ch->isSF2 ) { ch->it_inst->childModel( "bank" )->setValue( 0 ); ch->it_inst->childModel( "patch" )->setValue( prog ); } else { const QString num = QString::number( prog ); const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat"; const QString dir = "/usr/share/midi/" "freepats/Tone_000/"; const QStringList files = QDir( dir ). entryList( QStringList( filter ) ); if( ch->it_inst && !files.empty() ) { ch->it_inst->loadFile( dir+files.front() ); } } } else if( update.startsWith( "control" ) || update == "bendr" ) { int ccid = update.mid( 7, update.length()-8 ).toInt(); if( update == "bendr" ) { ccid = 128; } if( ccid <= 128 ) { double cc = evt->get_real_value(); AutomatableModel * objModel = NULL; switch( ccid ) { case 0: if( ch->isSF2 && ch->it_inst ) { objModel = ch->it_inst->childModel( "bank" ); printf("BANK SELECT %f %d\n", cc, (int)(cc*127.0)); cc *= 127.0f; } break; case 7: objModel = ch->it->volumeModel(); cc *= 100.0f; break; case 10: objModel = ch->it->panningModel(); cc = cc * 200.f - 100.0f; break; case 128: objModel = ch->it->pitchModel(); cc = cc * 100.0f; break; default: //TODO: something useful for other CCs break; } if( objModel ) { if( time == 0 && objModel ) { objModel->setInitValue( cc ); } else { if( ccs[ccid].at == NULL ) { ccs[ccid].create( tc, trackName + " > " + ( objModel != NULL ? objModel->displayName() : QString("CC %1").arg(ccid) ) ); } ccs[ccid].putValue( time, objModel, cc ); } } } } else { printf("Unhandled update: %d %d %f %s\n", (int) evt->chan, evt->get_type_code(), evt->time, evt->get_attribute() ); } } } } delete seq; for( int c=0; c < 256; ++c ) { if( !chs[c].hasNotes && chs[c].it ) { printf(" Should remove empty track\n"); // must delete trackView first - but where is it? //tc->removeTrack( chs[c].it ); //it->deleteLater(); } } // Set channel 10 to drums as per General MIDI's orders if( chs[9].hasNotes && chs[9].it_inst && chs[9].isSF2 ) { // AFAIK, 128 should be the standard bank for drums in SF2. // If not, this has to be made configurable. chs[9].it_inst->childModel( "bank" )->setValue( 128 ); chs[9].it_inst->childModel( "patch" )->setValue( 0 ); } return true; }
void AutomatableModelView::addDefaultActions( QMenu* menu ) { AutomatableModel* model = modelUntyped(); AutomatableModelViewSlots* amvSlots = new AutomatableModelViewSlots( this, menu ); menu->addAction( embed::getIconPixmap( "reload" ), AutomatableModel::tr( "&Reset (%1%2)" ). arg( model->displayValue( model->initValue<float>() ) ). arg( m_unit ), model, SLOT( reset() ) ); menu->addSeparator(); menu->addAction( embed::getIconPixmap( "edit_copy" ), AutomatableModel::tr( "&Copy value (%1%2)" ). arg( model->displayValue( model->value<float>() ) ). arg( m_unit ), model, SLOT( copyValue() ) ); menu->addAction( embed::getIconPixmap( "edit_paste" ), AutomatableModel::tr( "&Paste value (%1%2)"). arg( model->displayValue( AutomatableModel::copiedValue() ) ). arg( m_unit ), model, SLOT( pasteValue() ) ); menu->addSeparator(); menu->addAction( embed::getIconPixmap( "automation" ), AutomatableModel::tr( "Edit song-global automation" ), amvSlots, SLOT( editSongGlobalAutomation() ) ); menu->addAction( QPixmap(), AutomatableModel::tr( "Remove song-global automation" ), amvSlots, SLOT( removeSongGlobalAutomation() ) ); menu->addSeparator(); if( model->hasLinkedModels() ) { menu->addAction( embed::getIconPixmap( "edit-delete" ), AutomatableModel::tr( "Remove all linked controls" ), amvSlots, SLOT( unlinkAllModels() ) ); menu->addSeparator(); } QString controllerTxt; if( model->controllerConnection() ) { Controller* cont = model->controllerConnection()->getController(); if( cont ) { controllerTxt = AutomatableModel::tr( "Connected to %1" ).arg( cont->name() ); } else { controllerTxt = AutomatableModel::tr( "Connected to controller" ); } QMenu* contMenu = menu->addMenu( embed::getIconPixmap( "controller" ), controllerTxt ); contMenu->addAction( embed::getIconPixmap( "controller" ), AutomatableModel::tr("Edit connection..."), amvSlots, SLOT( execConnectionDialog() ) ); contMenu->addAction( embed::getIconPixmap( "cancel" ), AutomatableModel::tr("Remove connection"), amvSlots, SLOT( removeConnection() ) ); } else { menu->addAction( embed::getIconPixmap( "controller" ), AutomatableModel::tr("Connect to controller..."), amvSlots, SLOT( execConnectionDialog() ) ); } }
ValueBuffer * AutomatableModel::valueBuffer() { // if we've already calculated the valuebuffer this period, return the cached buffer if( m_lastUpdatedPeriod == s_periodCounter ) { return m_hasSampleExactData ? &m_valueBuffer : NULL; } QMutexLocker m( &m_valueBufferMutex ); if( m_lastUpdatedPeriod == s_periodCounter ) { return m_hasSampleExactData ? &m_valueBuffer : NULL; } float val = m_value; // make sure our m_value doesn't change midway ValueBuffer * vb; if( m_controllerConnection && m_controllerConnection->getController()->isSampleExact() ) { vb = m_controllerConnection->valueBuffer(); if( vb ) { float * values = vb->values(); float * nvalues = m_valueBuffer.values(); switch( m_scaleType ) { case Linear: for( int i = 0; i < m_valueBuffer.length(); i++ ) { nvalues[i] = minValue<float>() + ( range() * values[i] ); } break; case Logarithmic: for( int i = 0; i < m_valueBuffer.length(); i++ ) { nvalues[i] = logToLinearScale( values[i] ); } break; default: qFatal("AutomatableModel::valueBuffer() " "lacks implementation for a scale type"); break; } m_lastUpdatedPeriod = s_periodCounter; m_hasSampleExactData = true; return &m_valueBuffer; } } AutomatableModel* lm = NULL; if( m_hasLinkedModels ) { lm = m_linkedModels.first(); } if( lm && lm->controllerConnection() && lm->controllerConnection()->getController()->isSampleExact() ) { vb = lm->valueBuffer(); float * values = vb->values(); float * nvalues = m_valueBuffer.values(); for( int i = 0; i < vb->length(); i++ ) { nvalues[i] = fittedValue( values[i] ); } m_lastUpdatedPeriod = s_periodCounter; m_hasSampleExactData = true; return &m_valueBuffer; } if( m_oldValue != val ) { m_valueBuffer.interpolate( m_oldValue, val ); m_oldValue = val; m_lastUpdatedPeriod = s_periodCounter; m_hasSampleExactData = true; return &m_valueBuffer; } // if we have no sample-exact source for a ValueBuffer, return NULL to signify that no data is available at the moment // in which case the recipient knows to use the static value() instead m_lastUpdatedPeriod = s_periodCounter; m_hasSampleExactData = false; return NULL; }