diff --git a/include/Editor.h b/include/Editor.h index 54e41b028..c8c69a514 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -43,7 +43,7 @@ class Editor : public QMainWindow Q_OBJECT public: void setPauseIcon(bool displayPauseIcon=true); - + QAction *playAction() const; protected: DropToolBar * addDropToolBarToTop(QString const & windowTitle); DropToolBar * addDropToolBar(Qt::ToolBarArea whereToAdd, QString const & windowTitle); diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 51fd43a1c..1ea61d876 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -59,20 +59,27 @@ public: } MidiTime sampleLength() const; - + void setSampleStartFrame( f_cnt_t startFrame ); + void setSamplePlayLength( f_cnt_t length ); virtual TrackContentObjectView * createView( TrackView * _tv ); + bool isPlaying() const; + void setIsPlaying(bool isPlaying); + public slots: void setSampleBuffer( SampleBuffer* sb ); void setSampleFile( const QString & _sf ); - void updateLength( bpm_t = 0 ); + void updateLength(); void toggleRecord(); + void playbackPositionChanged(); + void updateTrackTcos(); private: SampleBuffer* m_sampleBuffer; BoolModel m_recordModel; + bool m_isPlaying; friend class SampleTCOView; @@ -102,6 +109,7 @@ public slots: protected: virtual void contextMenuEvent( QContextMenuEvent * _cme ); virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); virtual void dragEnterEvent( QDragEnterEvent * _dee ); virtual void dropEvent( QDropEvent * _de ); virtual void mouseDoubleClickEvent( QMouseEvent * ); @@ -143,6 +151,8 @@ public: return "sampletrack"; } +public slots: + void updateTcos(); private: FloatModel m_volumeModel; diff --git a/include/Song.h b/include/Song.h index c8c5656e9..63fb6eca5 100644 --- a/include/Song.h +++ b/include/Song.h @@ -381,6 +381,7 @@ signals: void timeSignatureChanged( int oldTicksPerTact, int ticksPerTact ); void controllerAdded( Controller * ); void controllerRemoved( Controller * ); + void updateSampleTracks(); } ; diff --git a/include/SongEditor.h b/include/SongEditor.h index d5bddc8ae..433bfb76d 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -71,6 +71,8 @@ public: void saveSettings( QDomDocument& doc, QDomElement& element ); void loadSettings( const QDomElement& element ); + ComboBoxModel *zoomingModel() const; + public slots: void scrolled( int new_pos ); @@ -158,6 +160,9 @@ protected slots: void adjustUiAfterProjectLoad(); +signals: + void playTriggered(); + private: QAction* m_addBBTrackAction; QAction* m_addSampleTrackAction; diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 0f8ac8350..e48d8aac1 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -241,6 +241,7 @@ private: signals: void positionChanged( const MidiTime & _t ); void loopPointStateLoaded( int _n ); + void positionMarkerMoved(); } ; diff --git a/include/Track.h b/include/Track.h index 8905e02ad..a43fd8528 100644 --- a/include/Track.h +++ b/include/Track.h @@ -577,6 +577,8 @@ public: return m_processingLock.tryLock(); } + BoolModel* getMutedModel(); + public slots: virtual void setName( const QString & newName ) { diff --git a/src/core/Song.cpp b/src/core/Song.cpp index ad34c73ed..ebcdedb78 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -270,6 +270,7 @@ void Song::processNextBuffer() ( tl->loopBegin().getTicks() * 60 * 1000 / 48 ) / getTempo(); m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); + emit updateSampleTracks(); } } @@ -343,10 +344,14 @@ void Song::processNextBuffer() { m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); - m_elapsedMilliSeconds = - ( ( tl->loopBegin().getTicks() ) * 60 * 1000 / 48 ) / + m_elapsedMilliSeconds = + ( ( tl->loopBegin().getTicks() ) * 60 * 1000 / 48 ) / getTempo(); } + else if( m_playPos[m_playMode] == tl->loopEnd() - 1 ) + { + emit updateSampleTracks(); + } } else { @@ -577,6 +582,7 @@ void Song::setPlayPos( tick_t ticks, PlayModes playMode ) if( isPlaying() ) { emit playbackPositionChanged(); + emit updateSampleTracks(); } } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 2f96006b3..3d6dff0fb 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -68,6 +68,7 @@ #include "ProjectJournal.h" #include "SampleTrack.h" #include "Song.h" +#include "SongEditor.h" #include "StringPairDrag.h" #include "templates.h" #include "TextFloat.h" @@ -151,8 +152,8 @@ void TrackContentObject::movePosition( const MidiTime & pos ) { m_startPosition = pos; Engine::getSong()->updateLength(); + emit positionChanged(); } - emit positionChanged(); } @@ -167,11 +168,8 @@ void TrackContentObject::movePosition( const MidiTime & pos ) */ void TrackContentObject::changeLength( const MidiTime & length ) { - if( m_length != length ) - { - m_length = length; - Engine::getSong()->updateLength(); - } + m_length = length; + Engine::getSong()->updateLength(); emit lengthChanged(); } @@ -280,12 +278,15 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, connect( m_tco, SIGNAL( lengthChanged() ), this, SLOT( updateLength() ) ); + connect( gui->songEditor()->m_editor->zoomingModel(), SIGNAL( dataChanged() ), this, SLOT( updateLength() ) ); connect( m_tco, SIGNAL( positionChanged() ), this, SLOT( updatePosition() ) ); connect( m_tco, SIGNAL( destroyedTCO() ), this, SLOT( close() ) ); setModel( m_tco ); m_trackView->getTrackContentWidget()->addTCOView( this ); + updateLength(); + updatePosition(); } @@ -2483,6 +2484,14 @@ void Track::toggleSolo() +BoolModel *Track::getMutedModel() +{ + return &m_mutedModel; +} + + + + // =========================================================================== diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index 682fb56fd..4eeae5111 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -374,6 +374,7 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) Engine::getSong()->setMilliSeconds(((((t.getTicks()))*60*1000/48)/Engine::getSong()->getTempo())); m_pos.setCurrentFrame( 0 ); updatePosition(); + positionMarkerMoved(); break; case MoveLoopBegin: diff --git a/src/gui/editors/Editor.cpp b/src/gui/editors/Editor.cpp index 7496f99c3..7962142a7 100644 --- a/src/gui/editors/Editor.cpp +++ b/src/gui/editors/Editor.cpp @@ -115,6 +115,11 @@ Editor::~Editor() } +QAction *Editor::playAction() const +{ + return m_playAction; +} + diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 46253c808..9f51e8f42 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -610,6 +610,14 @@ bool SongEditor::allowRubberband() const +ComboBoxModel *SongEditor::zoomingModel() const +{ + return m_zoomingModel; +} + + + + SongEditorWindow::SongEditorWindow(Song* song) : Editor(Engine::mixer()->audioDev()->supportsCapture()), m_editor(new SongEditor(song)) @@ -703,6 +711,7 @@ QSize SongEditorWindow::sizeHint() const void SongEditorWindow::play() { + emit playTriggered(); if( Engine::getSong()->playMode() != Song::Mode_PlaySong ) { Engine::getSong()->playSong(); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 3416c6521..24437600a 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -34,6 +34,7 @@ #include #include "gui_templates.h" +#include "GuiApplication.h" #include "Song.h" #include "embed.h" #include "Engine.h" @@ -42,7 +43,9 @@ #include "BBTrack.h" #include "SamplePlayHandle.h" #include "SampleRecordHandle.h" +#include "SongEditor.h" #include "StringPairDrag.h" +#include "TimeLineWidget.h" #include "Knob.h" #include "MainWindow.h" #include "Mixer.h" @@ -53,10 +56,10 @@ #include "panning_constants.h" #include "volume.h" - SampleTCO::SampleTCO( Track * _track ) : TrackContentObject( _track ), - m_sampleBuffer( new SampleBuffer ) + m_sampleBuffer( new SampleBuffer ), + m_isPlaying( false ) { saveJournallingState( false ); setSampleFile( "" ); @@ -65,7 +68,24 @@ SampleTCO::SampleTCO( Track * _track ) : // we need to receive bpm-change-events, because then we have to // change length of this TCO connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), - this, SLOT( updateLength( bpm_t ) ) ); + this, SLOT( updateLength() ) ); + connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ), + this, SLOT( updateLength() ) ); + + //care about positionmarker + TimeLineWidget * timeLine = Engine::getSong()->getPlayPos( Engine::getSong()->Mode_PlaySong ).m_timeLine; + connect( timeLine, SIGNAL( positionMarkerMoved() ), this, SLOT( playbackPositionChanged() ) ); + //care about loops + connect( Engine::getSong(), SIGNAL( updateSampleTracks() ), this, SLOT( playbackPositionChanged() ) ); + //care about mute TCOs + connect( this, SIGNAL( dataChanged() ), this, SLOT( playbackPositionChanged() ) ); + //care about mute track + connect( getTrack()->getMutedModel(), SIGNAL( dataChanged() ),this, SLOT( playbackPositionChanged() ) ); + //care about TCO position + connect( this, SIGNAL( positionChanged() ), this, SLOT( updateTrackTcos() ) ); + //playbutton clicked or space key + connect( gui->songEditor(), SIGNAL( playTriggered() ), this, SLOT( playbackPositionChanged() ) ); + switch( getTrack()->trackContainer()->type() ) { case TrackContainer::BBContainer: @@ -78,6 +98,7 @@ SampleTCO::SampleTCO( Track * _track ) : setAutoResize( false ); break; } + updateTrackTcos(); } @@ -85,6 +106,11 @@ SampleTCO::SampleTCO( Track * _track ) : SampleTCO::~SampleTCO() { + SampleTrack * sampletrack = dynamic_cast( getTrack() ); + if( sampletrack) + { + sampletrack->updateTcos(); + } sharedObject::unref( m_sampleBuffer ); } @@ -93,7 +119,10 @@ SampleTCO::~SampleTCO() void SampleTCO::changeLength( const MidiTime & _length ) { - TrackContentObject::changeLength( qMax( static_cast( _length ), DefaultTicksPerTact ) ); + float nom = Engine::getSong()->getTimeSigModel().getNumerator(); + float den = Engine::getSong()->getTimeSigModel().getDenominator(); + int ticksPerTact = DefaultTicksPerTact * ( nom / den ); + TrackContentObject::changeLength( qMax( static_cast( _length ), ticksPerTact ) ); } @@ -123,6 +152,7 @@ void SampleTCO::setSampleFile( const QString & _sf ) updateLength(); emit sampleChanged(); + emit playbackPositionChanged(); } @@ -137,7 +167,38 @@ void SampleTCO::toggleRecord() -void SampleTCO::updateLength( bpm_t ) +void SampleTCO::playbackPositionChanged() +{ + Engine::mixer()->removePlayHandlesOfTypes( getTrack(), PlayHandle::TypeSamplePlayHandle ); + m_isPlaying = false; +} + + + + +void SampleTCO::updateTrackTcos() +{ + SampleTrack * sampletrack = dynamic_cast( getTrack() ); + if( sampletrack) + { + sampletrack->updateTcos(); + } +} + +bool SampleTCO::isPlaying() const +{ + return m_isPlaying; +} + +void SampleTCO::setIsPlaying(bool isPlaying) +{ + m_isPlaying = isPlaying; +} + + + + +void SampleTCO::updateLength() { changeLength( sampleLength() ); } @@ -153,6 +214,22 @@ MidiTime SampleTCO::sampleLength() const +void SampleTCO::setSampleStartFrame(f_cnt_t startFrame) +{ + m_sampleBuffer->setStartFrame( startFrame ); +} + + + + +void SampleTCO::setSamplePlayLength(f_cnt_t length) +{ + m_sampleBuffer->setEndFrame( length ); +} + + + + void SampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _this ) { if( _this.parentNode().nodeName() == "clipboard" ) @@ -329,6 +406,14 @@ void SampleTCOView::mousePressEvent( QMouseEvent * _me ) } else { + if( _me->button() == Qt::MiddleButton && _me->modifiers() == Qt::ControlModifier ) + { + SampleTCO * sTco = dynamic_cast( getTrackContentObject() ); + if( sTco ) + { + sTco->updateTrackTcos(); + } + } TrackContentObjectView::mousePressEvent( _me ); } } @@ -336,6 +421,22 @@ void SampleTCOView::mousePressEvent( QMouseEvent * _me ) +void SampleTCOView::mouseReleaseEvent(QMouseEvent *_me) +{ + if( _me->button() == Qt::MiddleButton && !_me->modifiers() ) + { + SampleTCO * sTco = dynamic_cast( getTrackContentObject() ); + if( sTco ) + { + sTco->playbackPositionChanged(); + } + } + TrackContentObjectView::mouseReleaseEvent( _me ); +} + + + + void SampleTCOView::mouseDoubleClickEvent( QMouseEvent * ) { QString af = m_tco->m_sampleBuffer->openAudioFile(); @@ -394,10 +495,12 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) / (float) m_tco->length().getTact() : pixelsPerTact(); + float nom = Engine::getSong()->getTimeSigModel().getNumerator(); + float den = Engine::getSong()->getTimeSigModel().getDenominator(); + float ticksPerTact = DefaultTicksPerTact * nom / den; + QRect r = QRect( TCO_BORDER_WIDTH, spacing, - qMax( static_cast( m_tco->sampleLength() * ppt - / DefaultTicksPerTact ), 1 ), - rect().bottom() - 2 * spacing ); + qMax( static_cast( m_tco->sampleLength() * ppt / ticksPerTact ), 1 ), rect().bottom() - 2 * spacing ); m_tco->m_sampleBuffer->visualize( p, r, pe->rect() ); // disable antialiasing for borders, since its not needed @@ -497,9 +600,31 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, for( int i = 0; i < numOfTCOs(); ++i ) { TrackContentObject * tco = getTCO( i ); - if( tco->startPosition() == _start ) + SampleTCO * sTco = dynamic_cast( tco ); + float framesPerTick = Engine::framesPerTick(); + if( _start >= sTco->startPosition() && _start < sTco->endPosition() ) { - tcos.push_back( tco ); + if( sTco->isPlaying() == false ) + { + f_cnt_t sampleStart = framesPerTick * ( _start - sTco->startPosition() ); + f_cnt_t tcoFrameLength = framesPerTick * ( sTco->endPosition() - sTco->startPosition() ); + f_cnt_t sampleBufferLength = sTco->sampleBuffer()->frames(); + //if the Tco smaller than the sample length we play only until Tco end + //else we play the sample to the end but nothing more + f_cnt_t samplePlayLength = tcoFrameLength > sampleBufferLength ? sampleBufferLength : tcoFrameLength; + //we only play within the sampleBuffer limits + if( sampleStart < sampleBufferLength ) + { + sTco->setSampleStartFrame( sampleStart ); + sTco->setSamplePlayLength( samplePlayLength ); + tcos.push_back( sTco ); + sTco->setIsPlaying( true ); + } + } + } + else + { + sTco->setIsPlaying( false ); } } } @@ -591,6 +716,19 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) +void SampleTrack::updateTcos() +{ + for( int i = 0; i < numOfTCOs(); ++i ) + { + TrackContentObject * tco = getTCO( i ); + SampleTCO * sTco = dynamic_cast( tco ); + sTco->playbackPositionChanged(); + } +} + + + + SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) :