Merge branch 'master' into fix/msvc

# Conflicts:
#	plugins/LadspaEffect/CMakeLists.txt
#	plugins/Xpressive/Xpressive.cpp
#	plugins/opl2/CMakeLists.txt
#	plugins/papu/CMakeLists.txt
#	plugins/xpressive/CMakeLists.txt
#	src/CMakeLists.txt
This commit is contained in:
Lukas W
2017-11-22 17:26:49 +01:00
735 changed files with 4298 additions and 213826 deletions

5
src/3rdparty/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,5 @@
set(CMAKE_C_FLAGS "")
set(CMAKE_CXX_FLAGS "")
ADD_SUBDIRECTORY(rpmalloc)
ADD_SUBDIRECTORY(weakjack)

30
src/3rdparty/rpmalloc/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,30 @@
set(CMAKE_C_FLAGS "-std=c11")
add_library(rpmalloc STATIC
rpmalloc/rpmalloc/rpmalloc.c
rpmalloc/rpmalloc/rpmalloc.h
)
target_include_directories(rpmalloc PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/rpmalloc/rpmalloc
)
if (NOT LMMS_BUILD_WIN32)
target_compile_definitions(rpmalloc
PRIVATE -D_GNU_SOURCE
)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(rpmalloc
PRIVATE -DENABLE_ASSERTS=1 -DENABLE_VALIDATE_ARGS=1
)
endif()
option(LMMS_ENABLE_MALLOC_STATS "Enables statistics for rpmalloc" OFF)
if (LMMS_ENABLE_MALLOC_STATS)
target_compile_definitions(rpmalloc
PRIVATE -DENABLE_STATISTICS=1
)
endif()

12
src/3rdparty/weakjack/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,12 @@
# Use weak jack library linking
IF(LMMS_HAVE_WEAKJACK)
SET(CMAKE_C_FLAGS "-std=c11")
# Enable weakjack, disable metadata support
ADD_DEFINITIONS(-DUSE_WEAK_JACK=1 -DNO_JACK_METADATA=1)
# Library stub for AppImages running on systems without jack
ADD_LIBRARY(weakjack MODULE weakjack/weak_libjack.c weakjack/weak_libjack.h)
TARGET_INCLUDE_DIRECTORIES(weakjack PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/weakjack)
INSTALL(TARGETS weakjack LIBRARY DESTINATION "${PLUGIN_DIR}/optional")
ENDIF()

View File

@@ -1,3 +1,5 @@
ADD_SUBDIRECTORY(3rdparty)
CONFIGURE_FILE("lmmsconfig.h.in" "${CMAKE_BINARY_DIR}/lmmsconfig.h")
CONFIGURE_FILE("lmmsversion.h.in" "${CMAKE_BINARY_DIR}/lmmsversion.h")
@@ -7,7 +9,7 @@ SET(LMMS_UIS "")
SET(CMAKE_AUTOMOC ON)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# Enable C11 & C++11
# Enable C++11
SET(CMAKE_CXX_STANDARD 11)
IF(LMMS_BUILD_APPLE)
@@ -67,6 +69,13 @@ ELSEIF(NOT ("${SDL_INCLUDE_DIR}" STREQUAL ""))
INCLUDE_DIRECTORIES("${SDL_INCLUDE_DIR}")
ENDIF()
IF(LMMS_HAVE_WEAKJACK)
LIST(APPEND LMMS_SRCS "${WEAKJACK_INCLUDE_DIRS}/weak_libjack.c")
LIST(APPEND LMMS_INCLUDES "${WEAKJACK_INCLUDE_DIRS}/weak_libjack.h")
INCLUDE_DIRECTORIES("${WEAKJACK_INCLUDE_DIRS}")
ADD_DEFINITIONS(-DUSE_WEAK_JACK=1 -DNO_JACK_METADATA=1)
ENDIF()
IF(NOT ("${PORTAUDIO_INCLUDE_DIR}" STREQUAL ""))
INCLUDE_DIRECTORIES("${PORTAUDIO_INCLUDE_DIR}")
ENDIF()
@@ -149,7 +158,9 @@ SET(LMMS_REQUIRED_LIBS
${SAMPLERATE_LIBRARIES}
${SNDFILE_LIBRARIES}
${EXTRA_LIBRARIES}
rpmalloc
)
# Expose required libs for tests binary
SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} PARENT_SCOPE)
@@ -157,6 +168,16 @@ TARGET_LINK_LIBRARIES(lmms
${LMMS_REQUIRED_LIBS}
)
FOREACH(LIB ${LMMS_REQUIRED_LIBS})
IF(TARGET ${LIB})
GET_TARGET_PROPERTY(INCLUDE_DIRS ${LIB} INTERFACE_INCLUDE_DIRECTORIES)
IF(INCLUDE_DIRS)
TARGET_INCLUDE_DIRECTORIES(lmmsobjs PRIVATE ${INCLUDE_DIRS})
ENDIF()
ENDIF()
ENDFOREACH()
# Required libs for debug msys builds
IF(LMMS_BUILD_MSYS AND CMAKE_BUILD_TYPE STREQUAL "Debug")
TARGET_LINK_LIBRARIES(lmms QtCore4 QtGui4 QtXml4)

View File

@@ -503,7 +503,16 @@ void ConfigManager::loadConfigFile( const QString & configFile )
#elif defined(LMMS_BUILD_APPLE)
m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/";
#else
m_stkDir = "/usr/share/stk/rawwaves/";
if ( qApp->applicationDirPath().startsWith("/tmp/") )
{
// Assume AppImage bundle
m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/";
}
else
{
// Fallback to system provided location
m_stkDir = "/usr/share/stk/rawwaves/";
}
#endif
}
#endif

View File

@@ -194,7 +194,7 @@ bool DataFile::validate( QString extension )
if (! ( extension == "mmp" || extension == "mpt" || extension == "mmpz" ||
extension == "xpf" || extension == "xml" ||
( extension == "xiz" && ! pluginFactory->pluginSupportingExtension(extension).isNull()) ||
extension == "sf2" || extension == "pat" || extension == "mid" ||
extension == "sf2" || extension == "sf3" || extension == "pat" || extension == "mid" ||
extension == "dll"
) )
{
@@ -968,6 +968,61 @@ void DataFile::upgrade_1_2_0_rc2_42()
}
void DataFile::upgrade_1_3_0()
{
QDomNodeList list = elementsByTagName( "instrument" );
for( int i = 0; !list.item( i ).isNull(); ++i )
{
QDomElement el = list.item( i ).toElement();
if( el.attribute( "name" ) == "papu" )
{
el.setAttribute( "name", "freeboy" );
QDomNodeList children = el.elementsByTagName( "papu" );
for( int j = 0; !children.item( j ).isNull(); ++j )
{
QDomElement child = children.item( j ).toElement();
child.setTagName( "freeboy" );
}
}
else if( el.attribute( "name" ) == "OPL2" )
{
el.setAttribute( "name", "opulenz" );
QDomNodeList children = el.elementsByTagName( "OPL2" );
for( int j = 0; !children.item( j ).isNull(); ++j )
{
QDomElement child = children.item( j ).toElement();
child.setTagName( "opulenz" );
}
}
}
list = elementsByTagName( "effect" );
for( int i = 0; !list.item( i ).isNull(); ++i )
{
QDomElement effect = list.item( i ).toElement();
if( effect.attribute( "name" ) == "ladspaeffect" )
{
QDomNodeList keys = effect.elementsByTagName( "key" );
for( int j = 0; !keys.item( j ).isNull(); ++j )
{
QDomElement key = keys.item( j ).toElement();
QDomNodeList attributes = key.elementsByTagName( "attribute" );
for( int k = 0; !attributes.item( k ).isNull(); ++k )
{
QDomElement attribute = attributes.item( k ).toElement();
if( attribute.attribute( "name" ) == "file" &&
( attribute.attribute( "value" ) == "calf" ||
attribute.attribute( "value" ) == "calf.so" ) )
{
attribute.setAttribute( "value", "veal" );
}
}
}
}
}
}
void DataFile::upgrade()
{
ProjectVersion version =
@@ -1049,6 +1104,10 @@ void DataFile::upgrade()
upgrade_1_2_0_rc3();
upgrade_1_2_0_rc2_42();
}
if( version < "1.3.0" )
{
upgrade_1_3_0();
}
// update document meta data
documentElement().setAttribute( "version", LDF_VERSION_STRING );

View File

@@ -305,7 +305,7 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
m_arpCycleModel( 0.0f, 0.0f, 6.0f, 1.0f, this, tr( "Cycle steps" ) ),
m_arpSkipModel( 0.0f, 0.0f, 100.0f, 1.0f, this, tr( "Skip rate" ) ),
m_arpMissModel( 0.0f, 0.0f, 100.0f, 1.0f, this, tr( "Miss rate" ) ),
m_arpTimeModel( 100.0f, 25.0f, 2000.0f, 1.0f, 2000, this, tr( "Arpeggio time" ) ),
m_arpTimeModel( 200.0f, 25.0f, 2000.0f, 1.0f, 2000, this, tr( "Arpeggio time" ) ),
m_arpGateModel( 100.0f, 1.0f, 200.0f, 1.0f, this, tr( "Arpeggio gate" ) ),
m_arpDirectionModel( this, tr( "Arpeggio direction" ) ),
m_arpModeModel( this, tr( "Arpeggio mode" ) )
@@ -396,14 +396,13 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
frames_processed += remaining_frames_for_cur_arp;
// init with zero
int cur_arp_idx = 0;
// in sorted mode: is it our turn or do we have to be quiet for
// now?
if( m_arpModeModel.value() == SortMode &&
( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() )
{
// Set master note if not playing arp note or it will play as an ordinary note
_n->setMasterNote();
// update counters
frames_processed += arp_frames;
cur_frame += arp_frames;
@@ -416,10 +415,9 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpSkipModel.value() )
{
if( cur_arp_idx == 0 )
{
_n->setMasterNote();
}
// Set master note to prevent the note to extend over skipped notes
// This may only be needed for lb302
_n->setMasterNote();
// update counters
frames_processed += arp_frames;
cur_frame += arp_frames;
@@ -440,6 +438,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
}
}
int cur_arp_idx = 0;
// process according to arpeggio-direction...
if( dir == ArpDirUp )
{
@@ -525,6 +524,13 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
frames_processed += arp_frames;
cur_frame += arp_frames;
}
// make sure note is handled as arp-base-note, even
// if we didn't add a sub-note so far
if( m_arpModeModel.value() != FreeMode )
{
_n->setMasterNote();
}
}

View File

@@ -300,13 +300,22 @@ f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const
f_cnt_t InstrumentSoundShaping::releaseFrames() const
{
if( !m_instrumentTrack->instrument() )
{
return 0;
}
f_cnt_t ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames();
if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::IsSingleStreamed ) )
{
return ret_val;
}
if( m_envLfoParameters[Volume]->isUsed() )
{
return m_envLfoParameters[Volume]->releaseFrames();
}
f_cnt_t ret_val = m_instrumentTrack->instrument()
? m_instrumentTrack->instrument()->desiredReleaseFrames()
: 0;
for( int i = Volume+1; i < NumTargets; ++i )
{

View File

@@ -1,8 +1,7 @@
/*
* MemoryManager.cpp - A lightweight, generic memory manager for LMMS
* MemoryManager.cpp
*
* Copyright (c) 2014 Vesa Kivimäki
* Copyright (c) 2007-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2017 Lukas W <lukaswhl/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
@@ -25,197 +24,54 @@
#include "MemoryManager.h"
#include <QReadWriteLock>
#include <QtCore/QtGlobal>
#include "rpmalloc.h"
/// Global static object handling rpmalloc intializing and finalizing
struct MemoryManagerGlobalGuard {
MemoryManagerGlobalGuard() {
rpmalloc_initialize();
}
~MemoryManagerGlobalGuard() {
rpmalloc_finalize();
}
} static mm_global_guard;
MemoryPoolVector MemoryManager::s_memoryPools;
QReadWriteLock MemoryManager::s_poolMutex;
PointerInfoMap MemoryManager::s_pointerInfo;
QMutex MemoryManager::s_pointerMutex;
bool MemoryManager::init()
{
s_memoryPools.reserve( 64 );
s_pointerInfo.reserve( 4096 );
// construct first MemoryPool and allocate memory
MemoryPool m ( MM_INITIAL_CHUNKS );
m.m_pool = MemoryHelper::alignedMalloc( MM_INITIAL_CHUNKS * MM_CHUNK_SIZE );
s_memoryPools.append( m );
return true;
namespace {
static thread_local size_t thread_guard_depth;
}
void * MemoryManager::alloc( size_t size )
MemoryManager::ThreadGuard::ThreadGuard()
{
if( !size )
{
return NULL;
}
int requiredChunks = size / MM_CHUNK_SIZE + ( size % MM_CHUNK_SIZE > 0 ? 1 : 0 );
MemoryPool * mp = NULL;
void * ptr = NULL;
MemoryPoolVector::iterator it = s_memoryPools.begin();
s_poolMutex.lockForRead();
while( it != s_memoryPools.end() && !ptr )
{
ptr = ( *it ).getChunks( requiredChunks );
if( ptr )
{
mp = &( *it );
}
++it;
}
s_poolMutex.unlock();
if( ptr )
{
s_pointerMutex.lock();
PtrInfo p;
p.chunks = requiredChunks;
p.memPool = mp;
s_pointerInfo[ptr] = p;
s_pointerMutex.unlock();
return ptr;
}
// can't find enough chunks in existing pools, so
// create a new pool that is guaranteed to have enough chunks
int moreChunks = qMax( requiredChunks, MM_INCREMENT_CHUNKS );
int i = MemoryManager::extend( moreChunks );
mp = &s_memoryPools[i];
ptr = s_memoryPools[i].getChunks( requiredChunks );
if( ptr )
{
s_pointerMutex.lock();
PtrInfo p;
p.chunks = requiredChunks;
p.memPool = mp;
s_pointerInfo[ptr] = p;
s_pointerMutex.unlock();
return ptr;
}
// still no luck? something is horribly wrong
qFatal( "MemoryManager.cpp: Couldn't allocate memory: %d chunks asked", requiredChunks );
return NULL;
}
void MemoryManager::free( void * ptr )
{
if( !ptr )
{
return; // Null pointer deallocations are OK but do not need to be handled
}
// fetch info on the ptr and remove
s_pointerMutex.lock();
if( ! s_pointerInfo.contains( ptr ) ) // if we have no info on ptr, fail loudly
{
qFatal( "MemoryManager: Couldn't find pointer info for pointer: %p", ptr );
}
PtrInfo p = s_pointerInfo[ptr];
s_pointerInfo.remove( ptr );
s_pointerMutex.unlock();
p.memPool->releaseChunks( ptr, p.chunks );
}
int MemoryManager::extend( int chunks )
{
MemoryPool m ( chunks );
m.m_pool = MemoryHelper::alignedMalloc( chunks * MM_CHUNK_SIZE );
s_poolMutex.lockForWrite();
s_memoryPools.append( m );
int i = s_memoryPools.size() - 1;
s_poolMutex.unlock();
return i;
}
void MemoryManager::cleanup()
{
for( MemoryPoolVector::iterator it = s_memoryPools.begin(); it != s_memoryPools.end(); ++it )
{
MemoryHelper::alignedFree( ( *it ).m_pool );
MemoryHelper::alignedFree( ( *it ).m_free );
if (thread_guard_depth++ == 0) {
rpmalloc_thread_initialize();
}
}
void * MemoryPool::getChunks( int chunksNeeded )
MemoryManager::ThreadGuard::~ThreadGuard()
{
if( chunksNeeded > m_chunks ) // not enough chunks in this pool?
{
return NULL;
if (--thread_guard_depth == 0) {
rpmalloc_thread_finalize();
}
}
m_mutex.lock();
static thread_local MemoryManager::ThreadGuard local_mm_thread_guard{};
// now find out if we have a long enough sequence of chunks in this pool
char last = 0;
intptr_t n = 0;
intptr_t index = -1;
bool found = false;
for( int i = 0; i < m_chunks; ++i )
{
if( m_free[i] )
{
if( !last )
{
index = i;
}
++n;
if( n >= chunksNeeded )
{
found = true;
break;
}
}
else
{
n = 0;
}
last = m_free[i];
}
if( found ) // if enough chunks found, return pointer to chunks
{
// set chunk flags to false so we know the chunks are in use
for( intptr_t i = 0; i < chunksNeeded; ++i )
{
m_free[ index + i ] = 0;
}
m_mutex.unlock();
return (char*)m_pool + ( index * MM_CHUNK_SIZE );
}
m_mutex.unlock();
return NULL; // out of stock, come again tomorrow!
void* MemoryManager::alloc(size_t size)
{
// Reference local thread guard to ensure it is initialized.
// Compilers may optimize the instance away otherwise.
Q_UNUSED(&local_mm_thread_guard);
Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::alloc", "Thread not initialized");
return rpmalloc(size);
}
void MemoryPool::releaseChunks( void * ptr, int chunks )
void MemoryManager::free(void * ptr)
{
m_mutex.lock();
intptr_t start = ( (intptr_t)ptr - (intptr_t)m_pool ) / MM_CHUNK_SIZE;
if( start < 0 )
{
qFatal( "MemoryManager: error at releaseChunks() - corrupt pointer info?" );
}
memset( &m_free[ start ], 1, chunks );
m_mutex.unlock();
Q_UNUSED(&local_mm_thread_guard);
Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::free", "Thread not initialized");
return rpfree(ptr);
}

View File

@@ -36,6 +36,7 @@
#include "NotePlayHandle.h"
#include "ConfigManager.h"
#include "SamplePlayHandle.h"
#include "MemoryHelper.h"
// platform-specific audio-interface-classes
#include "AudioAlsa.h"

View File

@@ -151,6 +151,7 @@ void MixerWorkerThread::startAndWaitForJobs()
void MixerWorkerThread::run()
{
MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard);
disable_denormals();
QMutex m;

View File

@@ -128,7 +128,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
}
void NotePlayHandle::done()
NotePlayHandle::~NotePlayHandle()
{
lock();
noteOff( 0 );
@@ -599,7 +599,7 @@ NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrac
void NotePlayHandleManager::release( NotePlayHandle * nph )
{
nph->done();
nph->NotePlayHandle::~NotePlayHandle();
s_mutex.lockForRead();
s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = nph;
s_mutex.unlock();

View File

@@ -54,6 +54,7 @@ void PlayHandle::doProcessing()
if( m_usesBuffer )
{
m_bufferReleased = false;
BufferManager::clear(m_playHandleBuffer, Engine::mixer()->framesPerPeriod());
play( buffer() );
}
else

View File

@@ -22,6 +22,7 @@
*
*/
#include <QAtomicPointer>
#include <QFileInfo>
#include "PresetPreviewPlayHandle.h"
@@ -66,12 +67,25 @@ public:
NotePlayHandle* previewNote()
{
#if QT_VERSION >= 0x050000
return m_previewNote.loadAcquire();
#else
return m_previewNote;
#endif
}
void setPreviewNote( NotePlayHandle * _note )
{
#if QT_VERSION >= 0x050000
m_previewNote.storeRelease( _note );
#else
m_previewNote = _note;
#endif
}
bool testAndSetPreviewNote( NotePlayHandle * expectedVal, NotePlayHandle * newVal )
{
return m_previewNote.testAndSetOrdered( expectedVal, newVal );
}
void lockData()
@@ -97,7 +111,7 @@ public:
private:
InstrumentTrack* m_previewInstrumentTrack;
NotePlayHandle* m_previewNote;
QAtomicPointer<NotePlayHandle> m_previewNote;
QMutex m_dataMutex;
friend class PresetPreviewPlayHandle;
@@ -113,15 +127,14 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
PlayHandle( TypePresetPreviewHandle ),
m_previewNote( NULL )
{
s_previewTC->lockData();
setUsesBuffer( false );
if( s_previewTC->previewNote() != NULL )
{
s_previewTC->previewNote()->mute();
}
s_previewTC->lockData();
Engine::mixer()->requestChangeInModel();
s_previewTC->setPreviewNote( nullptr );
s_previewTC->previewInstrumentTrack()->silenceAllNotes();
Engine::mixer()->doneChangeInModel();
const bool j = Engine::projectJournal()->isJournalling();
Engine::projectJournal()->setJournalling( false );
@@ -174,6 +187,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
s_previewTC->previewInstrumentTrack()->
midiPort()->setMode( MidiPort::Disabled );
Engine::mixer()->requestChangeInModel();
// create note-play-handle for it
m_previewNote = NotePlayHandleManager::acquire(
s_previewTC->previewInstrumentTrack(), 0,
@@ -186,6 +200,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
Engine::mixer()->addPlayHandle( m_previewNote );
Engine::mixer()->doneChangeInModel();
s_previewTC->unlockData();
Engine::projectJournal()->setJournalling( j );
}
@@ -195,15 +210,13 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
PresetPreviewPlayHandle::~PresetPreviewPlayHandle()
{
s_previewTC->lockData();
Engine::mixer()->requestChangeInModel();
// not muted by other preset-preview-handle?
if( !m_previewNote->isMuted() )
if (s_previewTC->testAndSetPreviewNote(m_previewNote, nullptr))
{
// then set according state
s_previewTC->setPreviewNote( NULL );
m_previewNote->noteOff();
}
m_previewNote->noteOff();
s_previewTC->unlockData();
Engine::mixer()->doneChangeInModel();
}
@@ -228,7 +241,7 @@ bool PresetPreviewPlayHandle::isFinished() const
bool PresetPreviewPlayHandle::isFromTrack( const Track * _track ) const
{
return s_previewTC->previewInstrumentTrack() == _track;
return s_previewTC && s_previewTC->previewInstrumentTrack() == _track;
}
@@ -258,13 +271,11 @@ ConstNotePlayHandleList PresetPreviewPlayHandle::nphsOfInstrumentTrack(
const InstrumentTrack * _it )
{
ConstNotePlayHandleList cnphv;
s_previewTC->lockData();
if( s_previewTC->previewNote() != NULL &&
s_previewTC->previewNote()->instrumentTrack() == _it )
{
cnphv.push_back( s_previewTC->previewNote() );
}
s_previewTC->unlockData();
return cnphv;
}

View File

@@ -41,15 +41,15 @@ const ProjectRenderer::FileEncodeDevice ProjectRenderer::fileEncodeDevices[] =
{
{ ProjectRenderer::WaveFile,
QT_TRANSLATE_NOOP( "ProjectRenderer", "WAV-File (*.wav)" ),
QT_TRANSLATE_NOOP( "ProjectRenderer", "WAV (*.wav)" ),
".wav", &AudioFileWave::getInst },
{ ProjectRenderer::FlacFile,
QT_TRANSLATE_NOOP("ProjectRenderer", "FLAC-File (*.flac)"),
QT_TRANSLATE_NOOP("ProjectRenderer", "FLAC (*.flac)"),
".flac",
&AudioFileFlac::getInst
},
{ ProjectRenderer::OggFile,
QT_TRANSLATE_NOOP( "ProjectRenderer", "Compressed OGG-File (*.ogg)" ),
QT_TRANSLATE_NOOP( "ProjectRenderer", "OGG (*.ogg)" ),
".ogg",
#ifdef LMMS_HAVE_OGGVORBIS
&AudioFileOgg::getInst
@@ -58,7 +58,7 @@ const ProjectRenderer::FileEncodeDevice ProjectRenderer::fileEncodeDevices[] =
#endif
},
{ ProjectRenderer::MP3File,
QT_TRANSLATE_NOOP( "ProjectRenderer", "Compressed MP3-File (*.mp3)" ),
QT_TRANSLATE_NOOP( "ProjectRenderer", "MP3 (*.mp3)" ),
".mp3",
#ifdef LMMS_HAVE_MP3LAME
&AudioFileMP3::getInst
@@ -66,8 +66,8 @@ const ProjectRenderer::FileEncodeDevice ProjectRenderer::fileEncodeDevices[] =
NULL
#endif
},
// ... insert your own file-encoder-infos here... may be one day the
// user can add own encoders inside the program...
// Insert your own file-encoder infos here.
// Maybe one day the user can add own encoders inside the program.
{ ProjectRenderer::NumFileFormats, NULL, NULL, NULL }
@@ -109,15 +109,15 @@ ProjectRenderer::ProjectRenderer( const Mixer::qualitySettings & qualitySettings
ProjectRenderer::~ProjectRenderer()
{
Engine::mixer()->restoreAudioDevice(); // also deletes audio-dev
Engine::mixer()->restoreAudioDevice(); // Also deletes audio dev.
Engine::mixer()->changeQuality( m_oldQualitySettings );
}
// little help-function for getting file-format from a file-extension (only for
// registered file-encoders)
// Little help function for getting file format from a file extension
// (only for registered file-encoders).
ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension(
const QString & _ext )
{
@@ -131,7 +131,7 @@ ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension(
++idx;
}
return( WaveFile ); // default
return( WaveFile ); // Default.
}
@@ -151,9 +151,8 @@ void ProjectRenderer::startProcessing()
if( isReady() )
{
// have to do mixer stuff with GUI-thread-affinity in order to
// make slots connected to sampleRateChanged()-signals being
// called immediately
// Have to do mixer stuff with GUI-thread affinity in order to
// make slots connected to sampleRateChanged()-signals being called immediately.
Engine::mixer()->setAudioDevice( m_fileDev,
m_qualitySettings, false );
@@ -169,6 +168,7 @@ void ProjectRenderer::startProcessing()
void ProjectRenderer::run()
{
MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard);
#if 0
#ifdef LMMS_BUILD_LINUX
#ifdef LMMS_HAVE_SCHED_H
@@ -182,7 +182,7 @@ void ProjectRenderer::run()
Engine::getSong()->startExport();
Engine::getSong()->updateLength();
//skip first empty buffer
// Skip first empty buffer.
Engine::mixer()->nextBuffer();
const Song::PlayPos & exportPos = Engine::getSong()->getPlayPos(
@@ -193,7 +193,7 @@ void ProjectRenderer::run()
tick_t endTick = exportEndpoints.second.getTicks();
tick_t lengthTicks = endTick - startTick;
// Continually track and emit progress percentage to listeners
// Continually track and emit progress percentage to listeners.
while( exportPos.getTicks() < endTick &&
Engine::getSong()->isExporting() == true
&& !m_abort )
@@ -207,12 +207,12 @@ void ProjectRenderer::run()
}
}
// notify mixer of the end of processing
// Notify mixer of the end of processing.
Engine::mixer()->stopProcessing();
Engine::getSong()->stopExport();
// if the user aborted export-process, the file has to be deleted
// If the user aborted export-process, the file has to be deleted.
const QString f = m_fileDev->outputFile();
if( m_abort )
{
@@ -255,4 +255,3 @@ void ProjectRenderer::updateConsoleProgress()
}

View File

@@ -90,10 +90,10 @@ Song::Song() :
m_length( 0 ),
m_patternToPlay( NULL ),
m_loopPattern( false ),
m_elapsedMilliSeconds( 0 ),
m_elapsedTicks( 0 ),
m_elapsedTacts( 0 )
{
for(int i = 0; i < Mode_Count; ++i) m_elapsedMilliSeconds[i] = 0;
connect( &m_tempoModel, SIGNAL( dataChanged() ),
this, SLOT( setTempo() ) );
connect( &m_tempoModel, SIGNAL( dataUnchanged() ),
@@ -387,7 +387,7 @@ void Song::processNextBuffer()
framesPlayed += framesToPlay;
m_playPos[m_playMode].setCurrentFrame( framesToPlay +
currentFrame );
m_elapsedMilliSeconds += MidiTime::ticksToMilliseconds( framesToPlay / framesPerTick, getTempo());
m_elapsedMilliSeconds[m_playMode] += MidiTime::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo());
m_elapsedTacts = m_playPos[Mode_PlaySong].getTact();
m_elapsedTicks = ( m_playPos[Mode_PlaySong].getTicks() % ticksPerTact() ) / 48;
}
@@ -597,7 +597,7 @@ void Song::setPlayPos( tick_t ticks, PlayModes playMode )
{
tick_t ticksFromPlayMode = m_playPos[playMode].getTicks();
m_elapsedTicks += ticksFromPlayMode - ticks;
m_elapsedMilliSeconds += MidiTime::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() );
m_elapsedMilliSeconds[m_playMode] += MidiTime::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() );
m_playPos[playMode].setTicks( ticks );
m_playPos[playMode].setCurrentFrame( 0.0f );
@@ -651,8 +651,8 @@ void Song::stop()
switch( tl->behaviourAtStop() )
{
case TimeLineWidget::BackToZero:
m_playPos[m_playMode].setTicks( 0 );
m_elapsedMilliSeconds = 0;
m_playPos[m_playMode].setTicks(0);
m_elapsedMilliSeconds[m_playMode] = 0;
if( gui && gui->songEditor() &&
( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) )
{
@@ -663,7 +663,7 @@ void Song::stop()
case TimeLineWidget::BackToStart:
if( tl->savedPos() >= 0 )
{
m_playPos[m_playMode].setTicks( tl->savedPos().getTicks() );
m_playPos[m_playMode].setTicks(tl->savedPos().getTicks());
setToTime(tl->savedPos());
if( gui && gui->songEditor() &&
@@ -683,10 +683,13 @@ void Song::stop()
else
{
m_playPos[m_playMode].setTicks( 0 );
m_elapsedMilliSeconds = 0;
m_elapsedMilliSeconds[m_playMode] = 0;
}
m_playing = false;
m_elapsedMilliSeconds[Mode_None] = m_elapsedMilliSeconds[m_playMode];
m_playPos[Mode_None].setTicks(m_playPos[m_playMode].getTicks());
m_playPos[m_playMode].setCurrentFrame( 0 );
m_vstSyncController.setPlaybackState( m_exporting );

View File

@@ -166,6 +166,9 @@ void TrackContentObject::changeLength( const MidiTime & length )
emit lengthChanged();
}
bool TrackContentObject::comparePosition(const TrackContentObject *a, const TrackContentObject *b)
{
return a->startPosition() < b->startPosition();
@@ -224,6 +227,22 @@ void TrackContentObject::toggleMute()
MidiTime TrackContentObject::startTimeOffset() const
{
return m_startTimeOffset;
}
void TrackContentObject::setStartTimeOffset( const MidiTime &startTimeOffset )
{
m_startTimeOffset = startTimeOffset;
}
@@ -698,20 +717,10 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
}
else
{
gui->songEditor()->m_editor->selectAllTcos( false );
QVector<TrackContentObjectView *> tcoViews;
tcoViews.push_back( this );
DataFile dataFile = createTCODataFiles( tcoViews );
QPixmap thumbnail = QPixmap::grabWidget( this ).scaled(
128, 128,
Qt::KeepAspectRatio,
Qt::SmoothTransformation );
new StringPairDrag( QString( "tco_%1" ).arg(
m_tco->getTrack()->type() ),
dataFile.toString(), thumbnail, this );
m_action = ToggleSelected;
}
}
else
else if( !me->modifiers() )
{
if( isSelected() )
{
@@ -719,31 +728,22 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
}
else
{
gui->songEditor()->m_editor->selectAllTcos( false );
m_tco->addJournalCheckPoint();
// move or resize
m_tco->setJournalling( false );
setInitialMousePos( me->pos() );
if( me->x() < width() - RESIZE_GRIP_WIDTH )
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
if( me->x() < RESIZE_GRIP_WIDTH && sTco )
{
m_action = ResizeLeft;
m_oldTime = m_tco->startPosition();
QCursor c( Qt::SizeHorCursor );
QApplication::setOverrideCursor( c );
s_textFloat->setTitle( tr( "Current length" ) );
}
else if( me->x() < width() - RESIZE_GRIP_WIDTH )
{
m_action = Move;
m_oldTime = m_tco->startPosition();
QCursor c( Qt::SizeAllCursor );
QApplication::setOverrideCursor( c );
s_textFloat->setTitle( tr( "Current position" ) );
delete m_hint;
m_hint = TextFloat::displayMessage( tr( "Hint" ),
tr( "Press <%1> and drag to make "
"a copy." ).arg(
#ifdef LMMS_BUILD_APPLE
""),
#else
"Ctrl"),
#endif
embed::getIconPixmap( "hint" ), 0 );
}
else if( !m_tco->getAutoResize() )
{
@@ -752,23 +752,26 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
QCursor c( Qt::SizeHorCursor );
QApplication::setOverrideCursor( c );
s_textFloat->setTitle( tr( "Current length" ) );
delete m_hint;
m_hint = TextFloat::displayMessage( tr( "Hint" ),
tr( "Press <%1> for free "
"resizing." ).arg(
#ifdef LMMS_BUILD_APPLE
""),
#else
"Ctrl"),
#endif
embed::getIconPixmap( "hint" ), 0 );
}
// s_textFloat->reparent( this );
// setup text-float as if TCO was already moved/resized
mouseMoveEvent( me );
s_textFloat->show();
}
}
delete m_hint;
QString hint = m_action == Move ? tr( "Press <%1> and drag to make "
"a copy." )
: tr( "Press <%1> for free "
"resizing." );
m_hint = TextFloat::displayMessage( tr( "Hint" ),
hint.arg(
#ifdef LMMS_BUILD_APPLE
""),
#else
"Ctrl"),
#endif
embed::getIconPixmap( "hint" ), 0 );
// s_textFloat->reparent( this );
// setup text-float as if TCO was already moved/resized
mouseMoveEvent( me );
s_textFloat->show();
}
else if( me->button() == Qt::RightButton )
{
@@ -916,14 +919,43 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
( *it )->movePosition( t );
}
}
else if( m_action == Resize )
else if( m_action == Resize || m_action == ResizeLeft )
{
MidiTime t = qMax( MidiTime::ticksPerTact() / 16, static_cast<int>( me->x() * MidiTime::ticksPerTact() / ppt ) );
if( ! ( me->modifiers() & Qt::ControlModifier ) && me->button() == Qt::NoButton )
if( m_action == Resize )
{
t = qMax<int>( MidiTime::ticksPerTact(), t.toNearestTact() );
MidiTime t = qMax( MidiTime::ticksPerTact() / 16, static_cast<int>( me->x() * MidiTime::ticksPerTact() / ppt ) );
if( ! ( me->modifiers() & Qt::ControlModifier ) && me->button() == Qt::NoButton )
{
t = qMax<int>( MidiTime::ticksPerTact(), t.toNearestTact() );
}
m_tco->changeLength( t );
}
else
{
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
if( sTco )
{
const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x();
MidiTime t = qMax( 0, (int)
m_trackView->trackContainerView()->currentPosition()+
static_cast<int>( x * MidiTime::ticksPerTact() /
ppt ) );
if( ! ( me->modifiers() & Qt::ControlModifier )
&& me->button() == Qt::NoButton )
{
t = t.toNearestTact();
}
MidiTime oldPos = m_tco->startPosition();
if( m_tco->length() + ( oldPos - t ) >= MidiTime::ticksPerTact() )
{
m_tco->movePosition( t );
m_trackView->getTrackContentWidget()->changePosition();
m_tco->changeLength( m_tco->length() + ( oldPos - t ) );
sTco->setStartTimeOffset( sTco->startTimeOffset() + ( oldPos - t ) );
}
}
}
m_tco->changeLength( t );
s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ).
arg( m_tco->length().getTact() ).
arg( m_tco->length().getTicks() %
@@ -939,7 +971,9 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
}
else
{
if( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() )
SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
if( ( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() )
|| ( me->x() < RESIZE_GRIP_WIDTH && !me->buttons() && sTco ) )
{
if( QApplication::overrideCursor() != NULL &&
QApplication::overrideCursor()->shape() !=
@@ -982,8 +1016,9 @@ void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me )
setSelected( !isSelected() );
}
if( m_action == Move || m_action == Resize )
if( m_action == Move || m_action == Resize || m_action == ResizeLeft )
{
// TODO: Fix m_tco->setJournalling() consistency
m_tco->setJournalling( true );
}
m_action = NoAction;
@@ -1667,12 +1702,6 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) :
QWidget( parent ), /*!< The parent widget */
m_trackView( parent ) /*!< The parent track view */
{
if( s_grip == NULL )
{
s_grip = new QPixmap( embed::getIconPixmap(
"track_op_grip" ) );
}
ToolTip::add( this, tr( "Press <%1> while clicking on move-grip "
"to begin a new drag'n'drop-action." ).arg(
#ifdef LMMS_BUILD_APPLE
@@ -1795,14 +1824,17 @@ void TrackOperationsWidget::paintEvent( QPaintEvent * pe )
if( m_trackView->isMovingTrack() == false )
{
s_grip = new QPixmap( embed::getIconPixmap(
"track_op_grip" ) );
p.drawPixmap( 2, 2, *s_grip );
m_trackOps->show();
m_muteBtn->show();
}
else
{
m_trackOps->hide();
m_muteBtn->hide();
s_grip = new QPixmap( embed::getIconPixmap(
"track_op_grip_c" ) );
p.drawPixmap( 2, 2, *s_grip );
}
}
@@ -2713,6 +2745,7 @@ void TrackView::dropEvent( QDropEvent * de )
*/
void TrackView::mousePressEvent( QMouseEvent * me )
{
// If previously dragged too small, restore on shift-leftclick
if( height() < DEFAULT_TRACK_HEIGHT &&
me->modifiers() & Qt::ShiftModifier &&
@@ -2743,9 +2776,15 @@ void TrackView::mousePressEvent( QMouseEvent * me )
}
else
{
if( me->x()>10 ) // 10 = The width of the grip + 2 pixels to the left and right.
{
QWidget::mousePressEvent( me );
return;
}
m_action = MoveTrack;
QCursor c( Qt::SizeAllCursor );
QCursor c( Qt::SizeVerCursor );
QApplication::setOverrideCursor( c );
// update because in move-mode, all elements in
// track-op-widgets are hidden as a visual feedback

View File

@@ -57,7 +57,6 @@
#include <signal.h>
#include "MainApplication.h"
#include "MemoryManager.h"
#include "ConfigManager.h"
#include "NotePlayHandle.h"
#include "embed.h"
@@ -242,7 +241,6 @@ int main( int argc, char * * argv )
#endif
// initialize memory managers
MemoryManager::init();
NotePlayHandleManager::init();
// intialize RNG
@@ -973,9 +971,6 @@ int main( int argc, char * * argv )
Engine::destroy();
}
// cleanup memory managers
MemoryManager::cleanup();
// ProjectRenderer::updateConsoleProgress() doesn't return line after render
if( coreOnly )
{

View File

@@ -47,7 +47,7 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name,
setWindowTitle( tr( "Export project to %1" ).arg(
QFileInfo( _file_name ).fileName() ) );
// get the extension of the chosen file
// Get the extension of the chosen file.
QStringList parts = _file_name.split( '.' );
QString fileExt;
if( parts.size() > 0 )
@@ -60,16 +60,16 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name,
{
if( ProjectRenderer::fileEncodeDevices[i].isAvailable() )
{
// get the extension of this format
// Get the extension of this format.
QString renderExt = ProjectRenderer::fileEncodeDevices[i].m_extension;
// add to combo box
// Add to combo box.
fileFormatCB->addItem( ProjectRenderer::tr(
ProjectRenderer::fileEncodeDevices[i].m_description ),
QVariant(ProjectRenderer::fileEncodeDevices[i].m_fileFormat) // format tag; later used for identification
QVariant( ProjectRenderer::fileEncodeDevices[i].m_fileFormat ) // Format tag; later used for identification.
);
// if this is our extension, select it
// If this is our extension, select it.
if( QString::compare( renderExt, fileExt,
Qt::CaseInsensitive ) == 0 )
{
@@ -84,9 +84,8 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name,
for(int i=0; i<=MAX_LEVEL; ++i)
{
QString info="";
if (i==0){ info = tr("(fastest)"); }
else if (i==4){ info = tr("(default)"); }
else if (i==MAX_LEVEL){ info = tr("(smallest)"); }
if ( i==0 ){ info = tr( "( Fastest - biggest )" ); }
else if ( i==MAX_LEVEL ){ info = tr( "( Slowest - smallest )" ); }
compLevelCB->addItem(
QString::number(i)+" "+info,
@@ -95,7 +94,7 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name,
}
compLevelCB->setCurrentIndex(MAX_LEVEL/2);
#ifndef LMMS_HAVE_SF_COMPLEVEL
//Disable this widget; the setting would be ignored by the renderer.
// Disable this widget; the setting would be ignored by the renderer.
compressionWidget->setVisible(false);
#endif
@@ -175,8 +174,8 @@ void ExportProjectDialog::startExport()
os.setCompressionLevel(level);
}
//Make sure we have the the correct file extension
//so there's no confusion about the codec in use.
// Make sure we have the the correct file extension
// so there's no confusion about the codec in use.
auto output_name = m_fileName;
if (!(m_multiExport || output_name.endsWith(m_fileExtension,Qt::CaseInsensitive)))
{
@@ -190,9 +189,9 @@ void ExportProjectDialog::startExport()
connect( m_renderManager.get(), SIGNAL( progressChanged( int ) ),
progressBar, SLOT( setValue( int ) ) );
connect( m_renderManager.get(), SIGNAL( progressChanged( int ) ),
this, SLOT( updateTitleBar( int ) )) ;
this, SLOT( updateTitleBar( int ) ));
connect( m_renderManager.get(), SIGNAL( finished() ),
this, SLOT( accept() ) );
this, SLOT( accept() ) ) ;
connect( m_renderManager.get(), SIGNAL( finished() ),
gui->mainWindow(), SLOT( resetWindowTitle() ) );
@@ -250,7 +249,7 @@ void ExportProjectDialog::startBtnClicked()
{
m_ft = ProjectRenderer::NumFileFormats;
//Get file format from current menu selection.
// Get file format from current menu selection.
bool successful_conversion = false;
QVariant tag = fileFormatCB->itemData(fileFormatCB->currentIndex());
m_ft = static_cast<ProjectRenderer::ExportFileFormats>(

View File

@@ -427,7 +427,7 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me )
m_previewPlayHandle = s;
delete tf;
}
else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "gig" ) &&
else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" ) &&
! pluginFactory->pluginSupportingExtension(f->extension()).isNull() )
{
m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin );
@@ -983,7 +983,7 @@ void FileItem::determineFileType( void )
m_type = PresetFile;
m_handling = LoadByPlugin;
}
else if( ext == "sf2" )
else if( ext == "sf2" || ext == "sf3" )
{
m_type = SoundFontFile;
}

View File

@@ -37,14 +37,6 @@
#include "PluginFactory.h"
static bool pluginBefore( const Plugin::Descriptor* d1, const Plugin::Descriptor* d2 )
{
return qstricmp( d1->displayName, d2->displayName ) < 0 ? true : false;
}
PluginBrowser::PluginBrowser( QWidget * _parent ) :
SideBarWidget( tr( "Instrument Plugins" ),
embed::getIconPixmap( "plugins" ).transformed( QTransform().rotate( 90 ) ), _parent )
@@ -60,7 +52,7 @@ PluginBrowser::PluginBrowser( QWidget * _parent ) :
view_layout->setSpacing( 5 );
QLabel * hint = new QLabel( tr( "Drag an instrument "
auto hint = new QLabel( tr( "Drag an instrument "
"into either the Song-Editor, the "
"Beat+Bassline Editor or into an "
"existing instrument track." ),
@@ -79,20 +71,20 @@ PluginBrowser::PluginBrowser( QWidget * _parent ) :
PluginBrowser::~PluginBrowser()
{
}
PluginDescList::PluginDescList(QWidget *parent) :
QWidget(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
QList<Plugin::Descriptor*> descs = pluginFactory->descriptors(Plugin::Instrument);
std::sort(descs.begin(), descs.end(), pluginBefore);
std::sort(
descs.begin(),
descs.end(),
[]( const Plugin::Descriptor* d1, const Plugin::Descriptor* d2 ) -> bool
{
return qstricmp( d1->displayName, d2->displayName ) < 0 ? true : false;
}
);
for (const Plugin::Descriptor* desc : descs)
{
PluginDescWidget* p = new PluginDescWidget( *desc, this );
@@ -110,23 +102,14 @@ PluginDescList::PluginDescList(QWidget *parent) :
PluginDescWidget::PluginDescWidget( const Plugin::Descriptor & _pd,
QWidget * _parent ) :
QWidget( _parent ),
m_updateTimer( this ),
m_pluginDescriptor( _pd ),
m_logo( _pd.logo->pixmap() ),
m_mouseOver( false ),
m_targetHeight( 24 )
m_mouseOver( false )
{
connect( &m_updateTimer, SIGNAL( timeout() ), SLOT( updateHeight() ) );
setFixedHeight( m_targetHeight );
setFixedHeight( DEFAULT_HEIGHT );
setMouseTracking( true );
setCursor( Qt::PointingHandCursor );
}
PluginDescWidget::~PluginDescWidget()
{
setToolTip(_pd.description);
}
@@ -159,21 +142,6 @@ void PluginDescWidget::paintEvent( QPaintEvent * e )
p.setFont( f );
p.drawText( 10 + logo_size.width(), 15,
m_pluginDescriptor.displayName );
if( height() > 24 || m_mouseOver )
{
f.setBold( false );
p.setFont( f );
QRect br;
p.drawText( 10 + logo_size.width(), 20, width() - 58 - 5, 999,
Qt::TextWordWrap,
qApp->translate( "pluginBrowser", m_pluginDescriptor.description ),
&br );
if( m_mouseOver )
{
m_targetHeight = qMax( 60, 25 + br.height() );
}
}
}
@@ -182,8 +150,7 @@ void PluginDescWidget::paintEvent( QPaintEvent * e )
void PluginDescWidget::enterEvent( QEvent * _e )
{
m_mouseOver = true;
m_targetHeight = height() + 1;
updateHeight();
QWidget::enterEvent( _e );
}
@@ -193,8 +160,7 @@ void PluginDescWidget::enterEvent( QEvent * _e )
void PluginDescWidget::leaveEvent( QEvent * _e )
{
m_mouseOver = false;
m_targetHeight = 24;
updateHeight();
QWidget::leaveEvent( _e );
}
@@ -214,30 +180,6 @@ void PluginDescWidget::mousePressEvent( QMouseEvent * _me )
void PluginDescWidget::updateHeight()
{
if( m_targetHeight > height() )
{
setFixedHeight( height() + 1 );
}
else if( m_targetHeight < height() )
{
setFixedHeight( height() - 1 );
}
else
{
m_updateTimer.stop();
return;
}
if( !m_updateTimer.isActive() )
{
m_updateTimer.start( 10 );
}
}

View File

@@ -2,9 +2,11 @@
* SubWindow.cpp - Implementation of QMdiSubWindow that correctly tracks
* the geometry that windows should be restored to.
* Workaround for https://bugreports.qt.io/browse/QTBUG-256
* This implementation adds a custom themed title bar to
* the subwindow.
*
* Copyright (c) 2015 Colin Wallace <wallace.colin.a@gmail.com>
*
* Copyright (c) 2016 Steffen Baranowsky <baramgb@freenet.de>
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
@@ -95,6 +97,12 @@ SubWindow::SubWindow( QWidget *parent, Qt::WindowFlags windowFlags ) :
/**
* @brief SubWindow::paintEvent
*
* This draws our new title bar with custom colors
* and draws a window icon on the left upper corner.
*/
void SubWindow::paintEvent( QPaintEvent * )
{
QPainter p( this );
@@ -119,6 +127,12 @@ void SubWindow::paintEvent( QPaintEvent * )
/**
* @brief SubWindow::changeEvent
*
* Triggers if the window title changes and calls adjustTitleBar().
* @param event
*/
void SubWindow::changeEvent( QEvent *event )
{
QMdiSubWindow::changeEvent( event );
@@ -133,6 +147,16 @@ void SubWindow::changeEvent( QEvent *event )
/**
* @brief SubWindow::elideText
*
* Stores the given text into the given label.
* Shorts the text if it's too big for the labels width
* ans adds three dots (...)
*
* @param label - holds a pointer to the QLabel
* @param text - the text which will be stored (and if needed breaked down) into the QLabel.
*/
void SubWindow::elideText( QLabel *label, QString text )
{
QFontMetrics metrix( label->font() );
@@ -144,6 +168,15 @@ void SubWindow::elideText( QLabel *label, QString text )
/**
* @brief SubWindow::isMaximized
*
* This function checks if the subwindow is maximized.
* QMdiSubWindow::isMaximized() doesn't work on MacOS.
* Therefore we need our own implementation for checking this
* @return true if the subwindow is maximized at the moment.
* false if it's not.
*/
bool SubWindow::isMaximized()
{
#ifdef LMMS_BUILD_APPLE
@@ -161,6 +194,12 @@ bool SubWindow::isMaximized()
/**
* @brief SubWindow::getTrueNormalGeometry
*
* same as QWidet::normalGeometry, but works properly under X11
* see https://bugreports.qt.io/browse/QTBUG-256
*/
QRect SubWindow::getTrueNormalGeometry() const
{
return m_trackedNormalGeom;
@@ -216,7 +255,15 @@ void SubWindow::setBorderColor( const QColor &c )
/**
* @brief SubWindow::moveEvent
*
* overides the QMdiSubWindow::moveEvent() for saving the position
* of the subwindow into m_trackedNormalGeom. This position
* will be saved with the project because of an Qt bug wich doesn't
* save the right position. look at: https://bugreports.qt.io/browse/QTBUG-256
* @param event
*/
void SubWindow::moveEvent( QMoveEvent * event )
{
QMdiSubWindow::moveEvent( event );
@@ -231,6 +278,14 @@ void SubWindow::moveEvent( QMoveEvent * event )
/**
* @brief SubWindow::adjustTitleBar
*
* Our title bar needs buttons for maximize/restore and close in the right upper corner.
* We check if the subwindow is maximizable and put the buttons on the right positions.
* At next we calculate the width of the title label and call elideText() for adding
* the window title to m_windowTitle (which is a QLabel)
*/
void SubWindow::adjustTitleBar()
{
// button adjustments
@@ -289,7 +344,6 @@ void SubWindow::adjustTitleBar()
void SubWindow::focusChanged( QMdiSubWindow *subWindow )
{
if( m_hasFocus && subWindow != this )
@@ -306,6 +360,20 @@ void SubWindow::focusChanged( QMdiSubWindow *subWindow )
/**
* @brief SubWindow::resizeEvent
*
* On every rezise event we have to adjust our title label.
*
* At next we give the event to QMdiSubWindow::resizeEvent() which handles
* the event on its behavior.
*
* At last we store the current size into m_trackedNormalGeom. This size
* will be saved with the project because of an Qt bug wich doesn't
* save the right size. look at: https://bugreports.qt.io/browse/QTBUG-256
*
* @param event
*/
void SubWindow::resizeEvent( QResizeEvent * event )
{
adjustTitleBar();

View File

@@ -48,7 +48,7 @@
QPixmap * TimeLineWidget::s_posMarkerPixmap = NULL;
TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt,
Song::PlayPos & pos, const MidiTime & begin,
Song::PlayPos & pos, const MidiTime & begin, Song::PlayModes mode,
QWidget * parent ) :
QWidget( parent ),
m_inactiveLoopColor( 52, 63, 53, 64 ),
@@ -69,6 +69,7 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt,
m_ppt( ppt ),
m_pos( pos ),
m_begin( begin ),
m_mode( mode ),
m_savedPos( -1 ),
m_hint( NULL ),
m_action( NoAction ),
@@ -370,8 +371,13 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
switch( m_action )
{
case MovePositionMarker:
m_pos.setTicks( t.getTicks() );
Engine::getSong()->setToTime(t);
m_pos.setTicks(t.getTicks());
Engine::getSong()->setToTime(t, m_mode);
if (!( Engine::getSong()->isPlaying()))
{
//Song::Mode_None is used when nothing is being played.
Engine::getSong()->setToTime(t, Song::Mode_None);
}
m_pos.setCurrentFrame( 0 );
updatePosition();
positionMarkerMoved();

View File

@@ -6,35 +6,20 @@
<rect>
<x>0</x>
<y>0</y>
<width>558</width>
<height>357</height>
<width>504</width>
<height>286</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>558</width>
<height>357</height>
<width>504</width>
<height>286</height>
</size>
</property>
<property name="windowTitle">
<string>About LMMS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>8</number>
</property>
<property name="leftMargin">
<number>8</number>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
@@ -45,9 +30,6 @@
<height>64</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
@@ -65,7 +47,7 @@
<item>
<widget class="QLabel" name="versionLabel">
<property name="text">
<string>Version %1 (%2/%3, Qt %4, %5)</string>
<string>Version %1 (%2/%3, Qt %4, %5).</string>
</property>
</widget>
</item>
@@ -78,8 +60,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -106,8 +88,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -115,7 +97,7 @@
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>LMMS - easy music production for everyone</string>
<string>LMMS - easy music production for everyone.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -132,8 +114,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -141,7 +123,7 @@
<item>
<widget class="QLabel" name="copyrightLabel">
<property name="text">
<string>Copyright © %1</string>
<string>Copyright © %1.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -158,8 +140,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -167,7 +149,7 @@
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://lmms.io&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://lmms.io&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://lmms.io&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#33cc33;&quot;&gt;https://lmms.io&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -181,8 +163,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -236,7 +218,6 @@
</property>
<property name="plainText">
<string>Current language not translated (or native English).
If you're interested in translating LMMS in another language or want to improve existing translations, you're welcome to help us! Simply contact the maintainer!</string>
</property>
</widget>
@@ -278,32 +259,12 @@ If you're interested in translating LMMS in another language or want to improve
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -6,34 +6,48 @@
<rect>
<x>0</x>
<y>0</y>
<width>715</width>
<height>491</height>
<width>379</width>
<height>374</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>519</width>
<height>412</height>
<width>379</width>
<height>374</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>379</width>
<height>374</height>
</size>
</property>
<property name="windowTitle">
<string>Export project</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QCheckBox" name="exportLoopCB">
<property name="text">
<string>Export as loop (remove extra bar)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="renderMarkersCB">
<property name="text">
<string>Export between loop markers</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QGroupBox" name="outputGroupBox">
<property name="title">
<string>Output</string>
<string>File format settings</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
@@ -62,7 +76,7 @@
<item>
<widget class="QLabel" name="labelSampleRate">
<property name="text">
<string>Samplerate:</string>
<string>Sampling rate:</string>
</property>
</widget>
</item>
@@ -100,12 +114,6 @@
</item>
<item>
<widget class="QWidget" name="depthWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<layout class="QVBoxLayout">
<property name="leftMargin">
<number>0</number>
@@ -122,7 +130,7 @@
<item>
<widget class="QLabel" name="labelBitDepth">
<property name="text">
<string>Depth:</string>
<string>Bit depth:</string>
</property>
</widget>
</item>
@@ -133,17 +141,17 @@
</property>
<item>
<property name="text">
<string>16 Bit Integer</string>
<string>16 Bit integer</string>
</property>
</item>
<item>
<property name="text">
<string>24 Bit Integer</string>
<string>24 Bit integer</string>
</property>
</item>
<item>
<property name="text">
<string>32 Bit Float</string>
<string>32 Bit float</string>
</property>
</item>
</widget>
@@ -178,6 +186,11 @@
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Mono</string>
</property>
</item>
<item>
<property name="text">
<string>Stereo</string>
@@ -185,12 +198,7 @@
</item>
<item>
<property name="text">
<string>Joint Stereo</string>
</property>
</item>
<item>
<property name="text">
<string>Mono</string>
<string>Joint stereo</string>
</property>
</item>
</widget>
@@ -225,9 +233,6 @@
<property name="currentIndex">
<number>-1</number>
</property>
<property name="maxVisibleItems">
<number>9</number>
</property>
</widget>
</item>
</layout>
@@ -236,9 +241,6 @@
<item>
<widget class="QWidget" name="bitrateWidget" native="true">
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
@@ -312,8 +314,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>163</width>
<height>20</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -341,22 +343,22 @@
</property>
<item>
<property name="text">
<string>Zero Order Hold</string>
<string>Zero order hold</string>
</property>
</item>
<item>
<property name="text">
<string>Sinc Fastest</string>
<string>Sinc worst (fastest)</string>
</property>
</item>
<item>
<property name="text">
<string>Sinc Medium (recommended)</string>
<string>Sinc medium (recommended)</string>
</property>
</item>
<item>
<property name="text">
<string>Sinc Best (very slow!)</string>
<string>Sinc best (slowest)</string>
</property>
</item>
</widget>
@@ -364,7 +366,7 @@
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Oversampling (use with care!):</string>
<string>Oversampling:</string>
</property>
</widget>
</item>
@@ -392,20 +394,6 @@
</item>
</widget>
</item>
<item>
<widget class="QCheckBox" name="exportLoopCB">
<property name="text">
<string>Export as loop (remove end silence)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="renderMarkersCB">
<property name="text">
<string>Export between loop markers</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
@@ -413,8 +401,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
@@ -433,8 +421,8 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>

View File

@@ -152,7 +152,8 @@ AutomationEditor::AutomationEditor() :
m_timeLine = new TimeLineWidget( VALUES_WIDTH, 0, m_ppt,
Engine::getSong()->getPlayPos(
Song::Mode_PlayAutomationPattern ),
m_currentPosition, this );
m_currentPosition,
Song::Mode_PlayAutomationPattern, this );
connect( this, SIGNAL( positionChanged( const MidiTime & ) ),
m_timeLine, SLOT( updatePosition( const MidiTime & ) ) );
connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ),
@@ -589,6 +590,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent )
m_editMode == DRAW ) ||
m_editMode == ERASE )
{
m_drawLastTick = pos_ticks;
m_pattern->addJournalCheckPoint();
// erase single value
if( it != time_map.end() )
@@ -680,6 +682,39 @@ void AutomationEditor::mouseReleaseEvent(QMouseEvent * mouseEvent )
void AutomationEditor::removePoints( int x0, int x1 )
{
int deltax = qAbs( x1 - x0 );
int x = x0;
int xstep;
if( deltax < 1 )
{
return;
}
if( x0 < x1 )
{
xstep = 1;
}
else
{
xstep = -1;
}
int i = 0;
while( i <= deltax )
{
m_pattern->removeValue( MidiTime( x ) );
x += xstep;
i += 1;
}
}
void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent )
{
QMutexLocker m( &m_patternMutex );
@@ -735,14 +770,13 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent )
( mouseEvent->buttons() & Qt::LeftButton &&
m_editMode == ERASE ) )
{
// int resolution needed to improve the sensitivity of
// the erase manoeuvre with zoom levels < 100%
int zoom = m_zoomingXModel.value();
int resolution = 1 + zoom * zoom;
for( int i = -resolution; i < resolution; ++i )
// removing automation point
if( pos_ticks < 0 )
{
m_pattern->removeValue( MidiTime( pos_ticks + i ) );
pos_ticks = 0;
}
removePoints( m_drawLastTick, pos_ticks );
Engine::getSong()->setModified();
}
else if( mouseEvent->buttons() & Qt::NoButton && m_editMode == DRAW )
{
@@ -1067,7 +1101,7 @@ inline void AutomationEditor::drawAutomationPoint( QPainter & p, timeMap::iterat
{
int x = xCoordOfTick( it.key() );
int y = yCoordOfLevel( it.value() );
const int outerRadius = qBound( 2, ( m_ppt * AutomationPattern::quantization() ) / 576, 5 ); // man, getting this calculation right took forever
const int outerRadius = qBound( 3, ( m_ppt * AutomationPattern::quantization() ) / 576, 5 ); // man, getting this calculation right took forever
p.setPen( QPen( vertexColor().lighter( 200 ) ) );
p.setBrush( QBrush( vertexColor() ) );
p.drawEllipse( x - outerRadius, y - outerRadius, outerRadius * 2, outerRadius * 2 );

View File

@@ -311,7 +311,8 @@ PianoRoll::PianoRoll() :
m_timeLine = new TimeLineWidget( WHITE_KEY_WIDTH, 0, m_ppt,
Engine::getSong()->getPlayPos(
Song::Mode_PlayPattern ),
m_currentPosition, this );
m_currentPosition,
Song::Mode_PlayPattern, this );
connect( this, SIGNAL( positionChanged( const MidiTime & ) ),
m_timeLine, SLOT( updatePosition( const MidiTime & ) ) );
connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ),
@@ -961,6 +962,9 @@ void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones
}
}
m_pattern->rearrangeAllNotes();
m_pattern->dataChanged();
// we modified the song
update();
gui->songEditor()->update();
@@ -1668,10 +1672,10 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
// clicked on keyboard on the left
if( me->buttons() == Qt::RightButton )
{
// right click, tone marker contextual menu
// right click - tone marker contextual menu
m_semiToneMarkerMenu->popup( mapToGlobal( QPoint( me->x(), me->y() ) ) );
}
else
else if( me->buttons() == Qt::LeftButton )
{
// left click - play the note
int v = ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity;

View File

@@ -89,7 +89,8 @@ SongEditor::SongEditor( Song * song ) :
m_timeLine = new TimeLineWidget( widgetTotal, 32,
pixelsPerTact(),
m_song->m_playPos[Song::Mode_PlaySong],
m_currentPosition, this );
m_currentPosition,
Song::Mode_PlaySong, this );
connect( this, SIGNAL( positionChanged( const MidiTime & ) ),
m_song->m_playPos[Song::Mode_PlaySong].m_timeLine,
SLOT( updatePosition( const MidiTime & ) ) );

View File

@@ -101,19 +101,21 @@ void TimeDisplayWidget::updateTime()
switch( m_displayMode )
{
case MinutesSeconds:
m_majorLCD.setValue( s->getMilliseconds() / 60000 );
m_minorLCD.setValue( ( s->getMilliseconds() / 1000 ) % 60 );
m_milliSecondsLCD.setValue( s->getMilliseconds() % 1000 );
int msec;
msec = s->getMilliseconds();
m_majorLCD.setValue(msec / 60000);
m_minorLCD.setValue((msec / 1000) % 60);
m_milliSecondsLCD.setValue(msec % 1000);
break;
case BarsTicks:
int tick;
tick = ( s->getMilliseconds() * s->getTempo() * (DefaultTicksPerTact / 4 ) ) / 60000 ;
m_majorLCD.setValue( (int)(tick / s->ticksPerTact() ) + 1);
m_minorLCD.setValue( ( tick % s->ticksPerTact() ) /
( s->ticksPerTact() / s->getTimeSigModel().getNumerator() ) +1 );
m_milliSecondsLCD.setValue( ( tick % s->ticksPerTact() ) %
( s->ticksPerTact() / s->getTimeSigModel().getNumerator() ) );
tick = s->getPlayPos().getTicks();
m_majorLCD.setValue((int)(tick / s->ticksPerTact()) + 1);
m_minorLCD.setValue((tick % s->ticksPerTact()) /
(s->ticksPerTact() / s->getTimeSigModel().getNumerator() ) +1);
m_milliSecondsLCD.setValue((tick % s->ticksPerTact()) %
(s->ticksPerTact() / s->getTimeSigModel().getNumerator()));
break;
default: break;

View File

@@ -11,6 +11,7 @@
#cmakedefine LMMS_HAVE_ALSA
#cmakedefine LMMS_HAVE_FLUIDSYNTH
#cmakedefine LMMS_HAVE_JACK
#cmakedefine LMMS_HAVE_WEAKJACK
#cmakedefine LMMS_HAVE_MP3LAME
#cmakedefine LMMS_HAVE_OGGVORBIS
#cmakedefine LMMS_HAVE_OSS

View File

@@ -442,12 +442,15 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH )
m_midiNotesMutex.unlock();
lock();
// invalidate all NotePlayHandles linked to this track
// invalidate all NotePlayHandles and PresetPreviewHandles linked to this track
m_processHandles.clear();
Engine::mixer()->removePlayHandlesOfTypes( this, removeIPH
? PlayHandle::TypeNotePlayHandle
| PlayHandle::TypeInstrumentPlayHandle
: PlayHandle::TypeNotePlayHandle );
quint8 flags = PlayHandle::TypeNotePlayHandle | PlayHandle::TypePresetPreviewHandle;
if( removeIPH )
{
flags |= PlayHandle::TypeInstrumentPlayHandle;
}
Engine::mixer()->removePlayHandlesOfTypes( this, flags );
unlock();
}

View File

@@ -215,28 +215,7 @@ Note * Pattern::addNote( const Note & _new_note, const bool _quant_pos )
}
instrumentTrack()->lock();
if( m_notes.size() == 0 || m_notes.back()->pos() <= new_note->pos() )
{
m_notes.push_back( new_note );
}
else
{
// simple algorithm for inserting the note between two
// notes with smaller and greater position
// maybe it could be optimized by starting in the middle and
// going forward or backward but note-inserting isn't that
// time-critical since it is usually not done while playing...
long new_note_abs_time = new_note->pos();
NoteVector::Iterator it = m_notes.begin();
while( it != m_notes.end() &&
( *it )->pos() < new_note_abs_time )
{
++it;
}
m_notes.insert( it, new_note );
}
m_notes.insert(std::upper_bound(m_notes.begin(), m_notes.end(), new_note, Note::lessThan), new_note);
instrumentTrack()->unlock();
checkType();
@@ -294,7 +273,7 @@ Note * Pattern::noteAtStep( int _step )
void Pattern::rearrangeAllNotes()
{
// sort notes by start time
qSort(m_notes.begin(), m_notes.end(), Note::lessThan );
std::sort(m_notes.begin(), m_notes.end(), Note::lessThan);
}

View File

@@ -146,6 +146,7 @@ void SampleTCO::setSampleBuffer( SampleBuffer* sb )
void SampleTCO::setSampleFile( const QString & _sf )
{
m_sampleBuffer->setAudioFile( _sf );
setStartTimeOffset( 0 );
changeLength( (int) ( m_sampleBuffer->frames() / Engine::framesPerTick() ) );
emit sampleChanged();
@@ -183,11 +184,17 @@ void SampleTCO::updateTrackTcos()
}
}
bool SampleTCO::isPlaying() const
{
return m_isPlaying;
}
void SampleTCO::setIsPlaying(bool isPlaying)
{
m_isPlaying = isPlaying;
@@ -241,6 +248,7 @@ void SampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _this )
_this.setAttribute( "len", length() );
_this.setAttribute( "muted", isMuted() );
_this.setAttribute( "src", sampleFile() );
_this.setAttribute( "off", startTimeOffset() );
if( sampleFile() == "" )
{
QString s;
@@ -265,6 +273,7 @@ void SampleTCO::loadSettings( const QDomElement & _this )
}
changeLength( _this.attribute( "len" ).toInt() );
setMuted( _this.attribute( "muted" ).toInt() );
setStartTimeOffset( _this.attribute( "off" ).toInt() );
}
@@ -369,6 +378,8 @@ void SampleTCOView::dragEnterEvent( QDragEnterEvent * _dee )
void SampleTCOView::dropEvent( QDropEvent * _de )
{
if( StringPairDrag::decodeKey( _de ) == "samplefile" )
@@ -501,8 +512,9 @@ void SampleTCOView::paintEvent( QPaintEvent * pe )
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
float den = Engine::getSong()->getTimeSigModel().getDenominator();
float ticksPerTact = DefaultTicksPerTact * nom / den;
QRect r = QRect( TCO_BORDER_WIDTH, spacing,
float offset = m_tco->startTimeOffset() / ticksPerTact * pixelsPerTact();
QRect r = QRect( TCO_BORDER_WIDTH + offset, spacing,
qMax( static_cast<int>( m_tco->sampleLength() * ppt / ticksPerTact ), 1 ), rect().bottom() - 2 * spacing );
m_tco->m_sampleBuffer->visualize( p, r, pe->rect() );
@@ -608,10 +620,10 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames,
float framesPerTick = Engine::framesPerTick();
if( _start >= sTco->startPosition() && _start < sTco->endPosition() )
{
if( sTco->isPlaying() == false )
if( sTco->isPlaying() == false && _start > sTco->startPosition() + sTco->startTimeOffset() )
{
f_cnt_t sampleStart = framesPerTick * ( _start - sTco->startPosition() );
f_cnt_t tcoFrameLength = framesPerTick * ( sTco->endPosition() - sTco->startPosition() );
f_cnt_t sampleStart = framesPerTick * ( _start - sTco->startPosition() - sTco->startTimeOffset() );
f_cnt_t tcoFrameLength = framesPerTick * ( 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