Exemple #1
0
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();
		}
	}
}
Exemple #3
0
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;
}
Exemple #4
0
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 );
	}
}
Exemple #6
0
void Fader::mouseReleaseEvent( QMouseEvent * mouseEvent )
{
	if( mouseEvent && mouseEvent->button() == Qt::LeftButton )
	{
		AutomatableModel *thisModel = model();
		if( thisModel )
		{
			thisModel->restoreJournallingState();
		}
	}

	s_textFloat->hide();
}
Exemple #7
0
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();
}
Exemple #8
0
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() );
		}
	}
}
Exemple #9
0
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 );
	}
}
Exemple #10
0
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() ) );
	}
}
Exemple #12
0
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;
}