Merge branch 'stable-1.2'

# Conflicts:
#	data/locale/cs.ts
#	data/locale/en.ts
#	data/locale/ko.ts
#	data/locale/ru.ts
#	data/locale/uk.ts
#	data/locale/zh_TW.ts
#	include/Engine.h
#	plugins/nes/Nes.cpp
#	src/core/NotePlayHandle.cpp
#	src/core/SampleBuffer.cpp
#	src/tracks/SampleTrack.cpp
This commit is contained in:
Hyunjin Song
2019-06-12 17:41:12 +09:00
35 changed files with 14916 additions and 45037 deletions

View File

@@ -781,6 +781,16 @@ void AutomationPattern::resolveAllIDs()
{
a->addObject( dynamic_cast<AutomatableModel *>( o ), false );
}
else
{
// FIXME: Remove this block once the automation system gets fixed
// This is a temporary fix for https://github.com/LMMS/lmms/issues/4781
o = Engine::projectJournal()->journallingObject(ProjectJournal::idToSave(*k));
if( o && dynamic_cast<AutomatableModel *>( o ) )
{
a->addObject( dynamic_cast<AutomatableModel *>( o ), false );
}
}
}
}
a->m_idsToResolve.clear();

View File

@@ -205,13 +205,15 @@ void ControllerConnection::loadSettings( const QDomElement & _this )
else
{
m_controllerId = _this.attribute( "id", "-1" ).toInt();
if( m_controllerId < 0 || m_controllerId >= Engine::getSong()->controllers().size() )
if( m_controllerId < 0 )
{
qWarning( "controller index invalid\n" );
m_controllerId = -1;
}
if (!Engine::getSong()->isLoadingProject() && m_controllerId != -1)
if (!Engine::getSong()->isLoadingProject()
&& m_controllerId != -1
&& m_controllerId < Engine::getSong()->controllers().size())
{
setController( Engine::getSong()->
controllers().at( m_controllerId ) );

View File

@@ -105,6 +105,12 @@ void LmmsCore::destroy()
delete ConfigManager::inst();
}
float LmmsCore::framesPerTick(sample_rate_t sample_rate)
{
return sample_rate * 60.0f * 4 /
DefaultTicksPerTact / s_song->getTempo();
}

View File

@@ -577,21 +577,35 @@ void Mixer::changeQuality( const struct qualitySettings & _qs )
void Mixer::setAudioDevice( AudioDevice * _dev,
bool startNow )
void Mixer::doSetAudioDevice( AudioDevice * _dev )
{
stopProcessing();
// TODO: Use shared_ptr here in the future.
// Currently, this is safe, because this is only called by
// ProjectRenderer, and after ProjectRenderer calls this function,
// it does not access the old device anymore.
if( m_audioDev != m_oldAudioDev ) {delete m_audioDev;}
if( _dev == NULL )
if( _dev )
{
m_audioDev = _dev;
}
else
{
printf( "param _dev == NULL in Mixer::setAudioDevice(...). "
"Trying any working audio-device\n" );
m_audioDev = tryAudioDevices();
}
else
{
m_audioDev = _dev;
}
}
void Mixer::setAudioDevice( AudioDevice * _dev,
bool startNow )
{
stopProcessing();
doSetAudioDevice( _dev );
emit sampleRateChanged();
@@ -601,26 +615,16 @@ void Mixer::setAudioDevice( AudioDevice * _dev,
void Mixer::setAudioDevice( AudioDevice * _dev,
void Mixer::setAudioDevice(AudioDevice * _dev,
const struct qualitySettings & _qs,
bool _needs_fifo,
bool startNow )
bool startNow)
{
// don't delete the audio-device
stopProcessing();
m_qualitySettings = _qs;
if( _dev == NULL )
{
printf( "param _dev == NULL in Mixer::setAudioDevice(...). "
"Trying any working audio-device\n" );
m_audioDev = tryAudioDevices();
}
else
{
m_audioDev = _dev;
}
doSetAudioDevice( _dev );
emit qualitySettingsChanged();
emit sampleRateChanged();

View File

@@ -574,13 +574,9 @@ NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrac
int midiEventChannel,
NotePlayHandle::Origin origin )
{
if( s_availableIndex < 0 )
{
s_mutex.lockForWrite();
if( s_availableIndex < 0 ) extend( NPH_CACHE_INCREMENT );
s_mutex.unlock();
}
s_mutex.lockForRead();
// TODO: use some lockless data structures
s_mutex.lockForWrite();
if (s_availableIndex < 0) { extend(NPH_CACHE_INCREMENT); }
NotePlayHandle * nph = s_available[s_availableIndex--];
s_mutex.unlock();

View File

@@ -29,6 +29,8 @@
#include "JournallingObject.h"
#include "Song.h"
//! Avoid clashes between loaded IDs (have the bit cleared)
//! and newly created IDs (have the bit set)
static const int EO_ID_MSB = 1 << 23;
const int ProjectJournal::MAX_UNDO_STATES = 100; // TODO: make this configurable in settings

View File

@@ -73,7 +73,7 @@ SampleBuffer::SampleBuffer() :
m_amplification( 1.0f ),
m_reversed( false ),
m_frequency( BaseFreq ),
m_sampleRate( Engine::mixer()->baseSampleRate() )
m_sampleRate( mixerSampleRate () )
{
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) );
@@ -143,6 +143,11 @@ void SampleBuffer::sampleRateChanged()
update( true );
}
sample_rate_t SampleBuffer::mixerSampleRate()
{
return Engine::mixer()->processingSampleRate();
}
void SampleBuffer::update( bool _keep_settings )
{
@@ -178,7 +183,7 @@ void SampleBuffer::update( bool _keep_settings )
int_sample_t * buf = NULL;
sample_t * fbuf = NULL;
ch_cnt_t channels = DEFAULT_CHANNELS;
sample_rate_t samplerate = Engine::mixer()->baseSampleRate();
sample_rate_t samplerate = mixerSampleRate();
m_frames = 0;
const QFileInfo fileInfo( file );
@@ -366,10 +371,10 @@ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr,
bool _keep_settings )
{
// do samplerate-conversion to our default-samplerate
if( _src_sr != Engine::mixer()->baseSampleRate() )
if( _src_sr != mixerSampleRate() )
{
SampleBuffer * resampled = resample( _src_sr,
Engine::mixer()->baseSampleRate() );
mixerSampleRate() );
MM_FREE( m_data );
m_frames = resampled->frames();
m_data = MM_ALLOC( sampleFrame, m_frames );

View File

@@ -141,7 +141,7 @@ bool SamplePlayHandle::isFromTrack( const Track * _track ) const
f_cnt_t SamplePlayHandle::totalFrames() const
{
return ( m_sampleBuffer->endFrame() - m_sampleBuffer->startFrame() ) * ( Engine::mixer()->processingSampleRate() / Engine::mixer()->baseSampleRate() );
return ( m_sampleBuffer->endFrame() - m_sampleBuffer->startFrame() ) * ( Engine::mixer()->processingSampleRate() / m_sampleBuffer->sampleRate() );
}

View File

@@ -120,6 +120,7 @@ bool AudioFileMP3::initEncoder()
lame_set_brate(m_lame, bitRate);
// Add a comment
id3tag_init(m_lame);
id3tag_set_comment(m_lame, "Created with LMMS");
return lame_init_params(m_lame) != -1;

View File

@@ -84,6 +84,7 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt,
m_xOffset -= s_posMarkerPixmap->width() / 2;
setMouseTracking(true);
m_pos.m_timeLine = this;
QTimer * updateTimer = new QTimer( this );
@@ -351,6 +352,7 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event )
void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
{
parentWidget()->update(); // essential for widgets that this timeline had taken their mouse move event from.
const MidiTime t = m_begin + static_cast<int>( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * MidiTime::ticksPerTact() / m_ppt );
switch( m_action )

View File

@@ -431,8 +431,8 @@ void AutomationEditor::leaveEvent(QEvent * e )
{
QApplication::restoreOverrideCursor();
}
QWidget::leaveEvent( e );
update();
}
@@ -1511,8 +1511,11 @@ void AutomationEditor::paintEvent(QPaintEvent * pe )
case SELECT: cursor = s_toolSelect; break;
case MOVE: cursor = s_toolMove; break;
}
p.drawPixmap( mapFromGlobal( QCursor::pos() ) + QPoint( 8, 8 ),
*cursor );
QPoint mousePosition = mapFromGlobal( QCursor::pos() );
if( cursor != NULL && mousePosition.y() > TOP_MARGIN + SCROLLBAR_SIZE)
{
p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor );
}
}

View File

@@ -1435,6 +1435,7 @@ void PianoRoll::leaveEvent(QEvent * e )
QWidget::leaveEvent( e );
s_textFloat->hide();
update(); // cleaning inner mouse-related graphics
}
@@ -3433,10 +3434,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
case ModeSelect: cursor = s_toolSelect; break;
case ModeEditDetuning: cursor = s_toolOpen; break;
}
if( cursor != NULL )
QPoint mousePosition = mapFromGlobal( QCursor::pos() );
if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft())
{
p.drawPixmap( mapFromGlobal( QCursor::pos() ) + QPoint( 8, 8 ),
*cursor );
p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor );
}
}

View File

@@ -114,7 +114,9 @@ SampleTCO::~SampleTCO()
{
sampletrack->updateTcos();
}
Engine::mixer()->requestChangeInModel();
sharedObject::unref( m_sampleBuffer );
Engine::mixer()->doneChangeInModel();
}
@@ -137,7 +139,9 @@ const QString & SampleTCO::sampleFile() const
void SampleTCO::setSampleBuffer( SampleBuffer* sb )
{
Engine::mixer()->requestChangeInModel();
sharedObject::unref( m_sampleBuffer );
Engine::mixer()->doneChangeInModel();
m_sampleBuffer = sb;
updateLength();
@@ -269,6 +273,8 @@ void SampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _this )
QString s;
_this.setAttribute( "data", m_sampleBuffer->toBase64( s ) );
}
_this.setAttribute ("sample_rate", m_sampleBuffer->sampleRate());
// TODO: start- and end-frame
}
@@ -289,6 +295,10 @@ void SampleTCO::loadSettings( const QDomElement & _this )
changeLength( _this.attribute( "len" ).toInt() );
setMuted( _this.attribute( "muted" ).toInt() );
setStartTimeOffset( _this.attribute( "off" ).toInt() );
if (_this.hasAttribute("sample_rate")) {
m_sampleBuffer->setSampleRate(_this.attribute("sample_rate").toInt());
}
}
@@ -628,13 +638,14 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames,
{
TrackContentObject * tco = getTCO( i );
SampleTCO * sTco = dynamic_cast<SampleTCO*>( tco );
float framesPerTick = Engine::framesPerTick();
if( _start >= sTco->startPosition() && _start < sTco->endPosition() )
{
if( sTco->isPlaying() == false && _start > sTco->startPosition() + sTco->startTimeOffset() )
{
f_cnt_t sampleStart = framesPerTick * ( _start - sTco->startPosition() - sTco->startTimeOffset() );
f_cnt_t tcoFrameLength = framesPerTick * ( sTco->endPosition() - sTco->startPosition() - sTco->startTimeOffset() );
auto bufferFramesPerTick = Engine::framesPerTick (sTco->sampleBuffer ()->sampleRate ());
f_cnt_t sampleStart = bufferFramesPerTick * ( _start - sTco->startPosition() - sTco->startTimeOffset() );
f_cnt_t tcoFrameLength = bufferFramesPerTick * ( sTco->endPosition() - sTco->startPosition() - sTco->startTimeOffset() );
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