Let sample tracks play from any song position (#3133)
* play sampletracks from any song position * take care of TCO length * TCOs shouldn't be updated when SE window is resized * take care of zooming level * takes care on all song position changes and mute/solo tracks now * playes the sample only within the buffer limits * takes care of time signature changes * some minor code improvements (zapashcanon) * loopback one tick earlier * minor code changes * get rid off clicks by resize and scrolling song editor * removes playhandle by remove TCO * minor bugs on manipulating TCOs in Song Editor * update on add sample by playing * white spaces 1
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -381,6 +381,7 @@ signals:
|
||||
void timeSignatureChanged( int oldTicksPerTact, int ticksPerTact );
|
||||
void controllerAdded( Controller * );
|
||||
void controllerRemoved( Controller * );
|
||||
void updateSampleTracks();
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -241,6 +241,7 @@ private:
|
||||
signals:
|
||||
void positionChanged( const MidiTime & _t );
|
||||
void loopPointStateLoaded( int _n );
|
||||
void positionMarkerMoved();
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -577,6 +577,8 @@ public:
|
||||
return m_processingLock.tryLock();
|
||||
}
|
||||
|
||||
BoolModel* getMutedModel();
|
||||
|
||||
public slots:
|
||||
virtual void setName( const QString & newName )
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -115,6 +115,11 @@ Editor::~Editor()
|
||||
|
||||
}
|
||||
|
||||
QAction *Editor::playAction() const
|
||||
{
|
||||
return m_playAction;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <QPushButton>
|
||||
|
||||
#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<SampleTrack*>( 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<int>( _length ), DefaultTicksPerTact ) );
|
||||
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
|
||||
float den = Engine::getSong()->getTimeSigModel().getDenominator();
|
||||
int ticksPerTact = DefaultTicksPerTact * ( nom / den );
|
||||
TrackContentObject::changeLength( qMax( static_cast<int>( _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<SampleTrack*>( 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<SampleTCO*>( 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<SampleTCO*>( 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<int>( m_tco->sampleLength() * ppt
|
||||
/ DefaultTicksPerTact ), 1 ),
|
||||
rect().bottom() - 2 * spacing );
|
||||
qMax( static_cast<int>( 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<SampleTCO*>( 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<SampleTCO*>( tco );
|
||||
sTco->playbackPositionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) :
|
||||
|
||||
Reference in New Issue
Block a user