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:
@@ -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