Merge branch 'stable-1.2'
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* BufferManager.cpp - A buffer caching/memory management system
|
||||
*
|
||||
* Copyright (c) 2017 Lukas W <lukaswhl/at/gmail.com>
|
||||
* Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
|
||||
* Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
@@ -25,56 +26,28 @@
|
||||
|
||||
#include "BufferManager.h"
|
||||
|
||||
#include "Engine.h"
|
||||
#include "Mixer.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
sampleFrame ** BufferManager::s_available;
|
||||
AtomicInt BufferManager::s_availableIndex = 0;
|
||||
sampleFrame ** BufferManager::s_released;
|
||||
AtomicInt BufferManager::s_releasedIndex = 0;
|
||||
//QReadWriteLock BufferManager::s_mutex;
|
||||
int BufferManager::s_size;
|
||||
|
||||
static fpp_t framesPerPeriod;
|
||||
|
||||
void BufferManager::init( fpp_t framesPerPeriod )
|
||||
{
|
||||
s_available = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS );
|
||||
s_released = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS );
|
||||
|
||||
int c = framesPerPeriod * BM_INITIAL_BUFFERS;
|
||||
sampleFrame * b = MM_ALLOC( sampleFrame, c );
|
||||
|
||||
for( int i = 0; i < BM_INITIAL_BUFFERS; ++i )
|
||||
{
|
||||
s_available[ i ] = b;
|
||||
b += framesPerPeriod;
|
||||
}
|
||||
s_availableIndex = BM_INITIAL_BUFFERS - 1;
|
||||
s_size = BM_INITIAL_BUFFERS;
|
||||
::framesPerPeriod = framesPerPeriod;
|
||||
}
|
||||
|
||||
|
||||
sampleFrame * BufferManager::acquire()
|
||||
{
|
||||
if( s_availableIndex < 0 )
|
||||
{
|
||||
qFatal( "BufferManager: out of buffers" );
|
||||
}
|
||||
|
||||
int i = s_availableIndex.fetchAndAddOrdered( -1 );
|
||||
sampleFrame * b = s_available[ i ];
|
||||
|
||||
//qDebug( "acquired buffer: %p - index %d", b, i );
|
||||
return b;
|
||||
return MM_ALLOC( sampleFrame, ::framesPerPeriod );
|
||||
}
|
||||
|
||||
|
||||
void BufferManager::clear( sampleFrame * ab, const f_cnt_t frames,
|
||||
const f_cnt_t offset )
|
||||
void BufferManager::clear( sampleFrame *ab, const f_cnt_t frames, const f_cnt_t offset )
|
||||
{
|
||||
memset( ab + offset, 0, sizeof( *ab ) * frames );
|
||||
}
|
||||
|
||||
|
||||
#ifndef LMMS_DISABLE_SURROUND
|
||||
void BufferManager::clear( surroundSampleFrame * ab, const f_cnt_t frames,
|
||||
const f_cnt_t offset )
|
||||
@@ -86,43 +59,6 @@ void BufferManager::clear( surroundSampleFrame * ab, const f_cnt_t frames,
|
||||
|
||||
void BufferManager::release( sampleFrame * buf )
|
||||
{
|
||||
if (buf == nullptr) return;
|
||||
int i = s_releasedIndex.fetchAndAddOrdered( 1 );
|
||||
s_released[ i ] = buf;
|
||||
//qDebug( "released buffer: %p - index %d", buf, i );
|
||||
MM_FREE( buf );
|
||||
}
|
||||
|
||||
|
||||
void BufferManager::refresh() // non-threadsafe, hence it's called periodically from mixer at a time when no other threads can interfere
|
||||
{
|
||||
if( s_releasedIndex == 0 ) return;
|
||||
//qDebug( "refresh: %d buffers", int( s_releasedIndex ) );
|
||||
|
||||
int j = s_availableIndex;
|
||||
for( int i = 0; i < s_releasedIndex; ++i )
|
||||
{
|
||||
++j;
|
||||
s_available[ j ] = s_released[ i ];
|
||||
}
|
||||
s_availableIndex = j;
|
||||
s_releasedIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
/* // non-extensible for now
|
||||
void BufferManager::extend( int c )
|
||||
{
|
||||
s_size += c;
|
||||
sampleFrame ** tmp = MM_ALLOC( sampleFrame*, s_size );
|
||||
MM_FREE( s_available );
|
||||
s_available = tmp;
|
||||
|
||||
int cc = c * Engine::mixer()->framesPerPeriod();
|
||||
sampleFrame * b = MM_ALLOC( sampleFrame, cc );
|
||||
|
||||
for( int i = 0; i < c; ++i )
|
||||
{
|
||||
s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = b;
|
||||
b += Engine::mixer()->framesPerPeriod();
|
||||
}
|
||||
}*/
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Allocate a number of bytes and return them.
|
||||
* @param byteNum is the number of bytes
|
||||
*/
|
||||
void* MemoryHelper::alignedMalloc( int byteNum )
|
||||
void* MemoryHelper::alignedMalloc( size_t byteNum )
|
||||
{
|
||||
char *ptr, *ptr2, *aligned_ptr;
|
||||
int align_mask = ALIGN_SIZE - 1;
|
||||
|
||||
@@ -482,9 +482,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
Controller::triggerFrameCounter();
|
||||
AutomatableModel::incrementPeriodCounter();
|
||||
|
||||
// refresh buffer pool
|
||||
BufferManager::refresh();
|
||||
|
||||
s_renderingThread = false;
|
||||
|
||||
m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod );
|
||||
|
||||
@@ -252,17 +252,8 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
|
||||
if( m_released && (!instrumentTrack()->isSustainPedalPressed() ||
|
||||
m_releaseStarted) )
|
||||
{
|
||||
if (m_releaseStarted == false)
|
||||
{
|
||||
m_releaseStarted = true;
|
||||
|
||||
if( m_origin == OriginMidiInput )
|
||||
{
|
||||
setLength( MidiTime( static_cast<f_cnt_t>( totalFramesPlayed() / Engine::framesPerTick() ) ) );
|
||||
m_instrumentTrack->midiNoteOff( *this );
|
||||
}
|
||||
|
||||
m_releaseStarted = true;
|
||||
}
|
||||
f_cnt_t todo = framesThisPeriod;
|
||||
|
||||
// if this note is base-note for arpeggio, always set
|
||||
@@ -389,6 +380,16 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
|
||||
MidiTime::fromFrames( _s, Engine::framesPerTick() ),
|
||||
_s );
|
||||
}
|
||||
|
||||
// inform attached components about MIDI finished (used for recording in Piano Roll)
|
||||
if (!instrumentTrack()->isSustainPedalPressed())
|
||||
{
|
||||
if( m_origin == OriginMidiInput )
|
||||
{
|
||||
setLength( MidiTime( static_cast<f_cnt_t>( totalFramesPlayed() / Engine::framesPerTick() ) ) );
|
||||
m_instrumentTrack->midiNoteOff( *this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,16 +24,21 @@
|
||||
|
||||
#include "PlayHandle.h"
|
||||
#include "BufferManager.h"
|
||||
#include "Engine.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QDebug>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
PlayHandle::PlayHandle( const Type type, f_cnt_t offset ) :
|
||||
m_type( type ),
|
||||
m_offset( offset ),
|
||||
m_affinity( QThread::currentThread() ),
|
||||
m_playHandleBuffer( NULL ),
|
||||
m_usesBuffer( true )
|
||||
PlayHandle::PlayHandle(const Type type, f_cnt_t offset) :
|
||||
m_type(type),
|
||||
m_offset(offset),
|
||||
m_affinity(QThread::currentThread()),
|
||||
m_playHandleBuffer(BufferManager::acquire()),
|
||||
m_bufferReleased(true),
|
||||
m_usesBuffer(true)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -48,8 +53,8 @@ void PlayHandle::doProcessing()
|
||||
{
|
||||
if( m_usesBuffer )
|
||||
{
|
||||
if( ! m_playHandleBuffer ) m_playHandleBuffer = BufferManager::acquire();
|
||||
play( m_playHandleBuffer );
|
||||
m_bufferReleased = false;
|
||||
play( buffer() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -60,6 +65,10 @@ void PlayHandle::doProcessing()
|
||||
|
||||
void PlayHandle::releaseBuffer()
|
||||
{
|
||||
BufferManager::release( m_playHandleBuffer );
|
||||
m_playHandleBuffer = NULL;
|
||||
m_bufferReleased = true;
|
||||
}
|
||||
|
||||
sampleFrame* PlayHandle::buffer()
|
||||
{
|
||||
return m_bufferReleased ? nullptr : reinterpret_cast<sampleFrame*>(m_playHandleBuffer);
|
||||
};
|
||||
|
||||
@@ -1435,14 +1435,15 @@ void Song::exportProjectMidi()
|
||||
|
||||
// instantiate midi export plugin
|
||||
TrackContainer::TrackList tracks;
|
||||
tracks += Engine::getSong()->tracks();
|
||||
tracks += Engine::getBBTrackContainer()->tracks();
|
||||
TrackContainer::TrackList tracks_BB;
|
||||
tracks = Engine::getSong()->tracks();
|
||||
tracks_BB = Engine::getBBTrackContainer()->tracks();
|
||||
ExportFilter *exf = dynamic_cast<ExportFilter *> (Plugin::instantiate("midiexport", NULL, NULL));
|
||||
if (exf==NULL) {
|
||||
qDebug() << "failed to load midi export filter!";
|
||||
return;
|
||||
}
|
||||
exf->tryExport(tracks, Engine::getSong()->getTempo(), export_filename);
|
||||
exf->tryExport(tracks, tracks_BB, getTempo(), m_masterPitchModel.value(), export_filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ AudioPort::AudioPort( const QString & _name, bool _has_effect_chain,
|
||||
FloatModel * volumeModel, FloatModel * panningModel,
|
||||
BoolModel * mutedModel ) :
|
||||
m_bufferUsage( false ),
|
||||
m_portBuffer( NULL ),
|
||||
m_portBuffer( BufferManager::acquire() ),
|
||||
m_extOutputEnabled( false ),
|
||||
m_nextFxChannel( 0 ),
|
||||
m_name( "unnamed port" ),
|
||||
@@ -57,6 +57,7 @@ AudioPort::~AudioPort()
|
||||
setExtOutputEnabled( false );
|
||||
Engine::mixer()->removeAudioPort( this );
|
||||
delete m_effects;
|
||||
BufferManager::release( m_portBuffer );
|
||||
}
|
||||
|
||||
|
||||
@@ -110,8 +111,7 @@ void AudioPort::doProcessing()
|
||||
|
||||
const fpp_t fpp = Engine::mixer()->framesPerPeriod();
|
||||
|
||||
// get a buffer for processing and clear it
|
||||
m_portBuffer = BufferManager::acquire();
|
||||
// clear the buffer
|
||||
BufferManager::clear( m_portBuffer, fpp );
|
||||
|
||||
//qDebug( "Playhandles: %d", m_playHandles.size() );
|
||||
@@ -225,8 +225,6 @@ void AudioPort::doProcessing()
|
||||
// TODO: improve the flow here - convert to pull model
|
||||
m_bufferUsage = false;
|
||||
}
|
||||
|
||||
BufferManager::release( m_portBuffer ); // release buffer, we don't need it anymore
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QtGlobal>
|
||||
#include <QMessageBox>
|
||||
#include <QSplashScreen>
|
||||
|
||||
@@ -54,6 +55,11 @@ GuiApplication* GuiApplication::instance()
|
||||
|
||||
GuiApplication::GuiApplication()
|
||||
{
|
||||
// enable HiDPI scaling before showing anything (Qt 5.6+ only)
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||
#endif
|
||||
|
||||
// prompt the user to create the LMMS working directory (e.g. ~/lmms) if it doesn't exist
|
||||
if ( !ConfigManager::inst()->hasWorkingDir() &&
|
||||
QMessageBox::question( NULL,
|
||||
|
||||
@@ -62,3 +62,27 @@ bool MainApplication::event(QEvent* event)
|
||||
return QApplication::event(event);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
bool MainApplication::winEventFilter(MSG* msg, long* result)
|
||||
{
|
||||
switch(msg->message)
|
||||
{
|
||||
case WM_STYLECHANGING:
|
||||
if(msg->wParam == GWL_EXSTYLE)
|
||||
{
|
||||
// Prevent plugins making the main window transparent
|
||||
STYLESTRUCT * style = reinterpret_cast<STYLESTRUCT *>(msg->lParam);
|
||||
if(!(style->styleOld & WS_EX_LAYERED))
|
||||
{
|
||||
style->styleNew &= ~WS_EX_LAYERED;
|
||||
}
|
||||
*result = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "PluginView.h"
|
||||
#include "ProjectJournal.h"
|
||||
#include "ProjectNotes.h"
|
||||
#include "RemotePlugin.h"
|
||||
#include "SetupDialog.h"
|
||||
#include "SideBar.h"
|
||||
#include "SongEditor.h"
|
||||
@@ -300,12 +301,11 @@ void MainWindow::finalize()
|
||||
SLOT( exportProjectTracks() ),
|
||||
Qt::CTRL + Qt::SHIFT + Qt::Key_E );
|
||||
|
||||
// temporarily disabled broken MIDI export
|
||||
/*project_menu->addAction( embed::getIconPixmap( "midi_file" ),
|
||||
project_menu->addAction( embed::getIconPixmap( "midi_file" ),
|
||||
tr( "Export &MIDI..." ),
|
||||
Engine::getSong(),
|
||||
SLOT( exportProjectMidi() ),
|
||||
Qt::CTRL + Qt::Key_M );*/
|
||||
Qt::CTRL + Qt::Key_M );
|
||||
|
||||
// Prevent dangling separator at end of menu per https://bugreports.qt.io/browse/QTBUG-40071
|
||||
#if !(defined(LMMS_BUILD_APPLE) && (QT_VERSION >= 0x050000) && (QT_VERSION < 0x050600))
|
||||
@@ -1536,14 +1536,14 @@ void MainWindow::browseHelp()
|
||||
void MainWindow::autoSave()
|
||||
{
|
||||
if( !Engine::getSong()->isExporting() &&
|
||||
!Engine::getSong()->isLoadingProject() &&
|
||||
!RemotePluginBase::isMainThreadWaiting() &&
|
||||
!QApplication::mouseButtons() &&
|
||||
( ConfigManager::inst()->value( "ui",
|
||||
"enablerunningautosave" ).toInt() ||
|
||||
! Engine::getSong()->isPlaying() ) )
|
||||
( ConfigManager::inst()->value( "ui",
|
||||
"enablerunningautosave" ).toInt() ||
|
||||
! Engine::getSong()->isPlaying() ) )
|
||||
{
|
||||
AutoSaveThread * ast = new AutoSaveThread();
|
||||
connect( ast, SIGNAL( finished() ), ast, SLOT( deleteLater() ) );
|
||||
ast->start();
|
||||
Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile());
|
||||
autoSaveTimerReset(); // Reset timer
|
||||
}
|
||||
else
|
||||
@@ -1555,11 +1555,3 @@ void MainWindow::autoSave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AutoSaveThread::run()
|
||||
{
|
||||
Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile());
|
||||
}
|
||||
|
||||
@@ -157,7 +157,8 @@ InstrumentTrack::~InstrumentTrack()
|
||||
void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, NotePlayHandle* n )
|
||||
{
|
||||
// we must not play the sound if this InstrumentTrack is muted...
|
||||
if( isMuted() || ( n && n->isBbTrackMuted() ) || ! m_instrument )
|
||||
if( isMuted() || ( Engine::getSong()->playMode() != Song::Mode_PlayPattern &&
|
||||
n && n->isBbTrackMuted() ) || ! m_instrument )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -237,6 +238,11 @@ MidiEvent InstrumentTrack::applyMasterKey( const MidiEvent& event )
|
||||
|
||||
void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset )
|
||||
{
|
||||
if( Engine::getSong()->isExporting() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool eventHandled = false;
|
||||
|
||||
switch( event.type() )
|
||||
@@ -273,6 +279,12 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti
|
||||
// be deleted later automatically)
|
||||
Engine::mixer()->requestChangeInModel();
|
||||
m_notes[event.key()]->noteOff( offset );
|
||||
if (isSustainPedalPressed() &&
|
||||
m_notes[event.key()]->origin() ==
|
||||
m_notes[event.key()]->OriginMidiInput)
|
||||
{
|
||||
m_sustainedNotes << m_notes[event.key()];
|
||||
}
|
||||
m_notes[event.key()] = NULL;
|
||||
Engine::mixer()->doneChangeInModel();
|
||||
}
|
||||
@@ -302,8 +314,24 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti
|
||||
{
|
||||
m_sustainPedalPressed = true;
|
||||
}
|
||||
else
|
||||
else if (isSustainPedalPressed())
|
||||
{
|
||||
for (NotePlayHandle* nph : m_sustainedNotes)
|
||||
{
|
||||
if (nph && nph->isReleased())
|
||||
{
|
||||
if( nph->origin() ==
|
||||
nph->OriginMidiInput)
|
||||
{
|
||||
nph->setLength(
|
||||
MidiTime( static_cast<f_cnt_t>(
|
||||
nph->totalFramesPlayed() /
|
||||
Engine::framesPerTick() ) ) );
|
||||
midiNoteOff( *nph );
|
||||
}
|
||||
}
|
||||
}
|
||||
m_sustainedNotes.clear();
|
||||
m_sustainPedalPressed = false;
|
||||
}
|
||||
}
|
||||
@@ -586,7 +614,10 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames,
|
||||
{
|
||||
TrackContentObject * tco = getTCO( _tco_num );
|
||||
tcos.push_back( tco );
|
||||
bb_track = BBTrack::findBBTrack( _tco_num );
|
||||
if (trackContainer() == (TrackContainer*)Engine::getBBTrackContainer())
|
||||
{
|
||||
bb_track = BBTrack::findBBTrack( _tco_num );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -594,7 +594,10 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames,
|
||||
return false;
|
||||
}
|
||||
tcos.push_back( getTCO( _tco_num ) );
|
||||
bb_track = BBTrack::findBBTrack( _tco_num );
|
||||
if (trackContainer() == (TrackContainer*)Engine::getBBTrackContainer())
|
||||
{
|
||||
bb_track = BBTrack::findBBTrack( _tco_num );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user