Merge branch 'master' into groove

This commit is contained in:
Hyunjin Song
2022-06-18 15:47:07 +09:00
602 changed files with 70948 additions and 70235 deletions

View File

@@ -62,6 +62,46 @@ class LMMS_EXPORT AudioEngine : public QObject
{
Q_OBJECT
public:
/**
* @brief RAII helper for requestChangesInModel.
* Used by AudioEngine::requestChangesGuard.
*/
class RequestChangesGuard {
friend class AudioEngine;
private:
RequestChangesGuard(AudioEngine* audioEngine)
: m_audioEngine{audioEngine}
{
m_audioEngine->requestChangeInModel();
}
public:
RequestChangesGuard()
: m_audioEngine{nullptr}
{
}
RequestChangesGuard(RequestChangesGuard&& other)
: RequestChangesGuard()
{
std::swap(other.m_audioEngine, m_audioEngine);
}
// Disallow copy.
RequestChangesGuard(const RequestChangesGuard&) = delete;
RequestChangesGuard& operator=(const RequestChangesGuard&) = delete;
~RequestChangesGuard() {
if (m_audioEngine) {
m_audioEngine->doneChangeInModel();
}
}
private:
AudioEngine* m_audioEngine;
};
struct qualitySettings
{
enum Mode
@@ -309,6 +349,11 @@ public:
void requestChangeInModel();
void doneChangeInModel();
RequestChangesGuard requestChangesGuard()
{
return RequestChangesGuard{this};
}
static bool isAudioDevNameValid(QString name);
static bool isMidiDevNameValid(QString name);

View File

@@ -25,8 +25,10 @@
#ifndef AUDIO_SOUNDIO_H
#define AUDIO_SOUNDIO_H
#include <QObject>
#include "lmmsconfig.h"
#include "ComboBoxModel.h"
#ifdef LMMS_HAVE_SOUNDIO

View File

@@ -1,5 +1,5 @@
/*
* LadspaManager.h - declaration of class ladspaManager
* LadspaManager.h - declaration of class LadspaManager
* a class to manage loading and instantiation
* of ladspa plugins
*
@@ -47,7 +47,7 @@ typedef QPair<QString, ladspa_key_t> sortable_plugin_t;
typedef QList<sortable_plugin_t> l_sortable_plugin_t;
typedef QList<ladspa_key_t> l_ladspa_key_t;
/* ladspaManager provides a database of LADSPA plug-ins. Upon instantiation,
/* LadspaManager provides a database of LADSPA plug-ins. Upon instantiation,
it loads all of the plug-ins found in the LADSPA_PATH environmental variable
and stores their access descriptors according in a dictionary keyed on
the filename the plug-in was loaded from and the label of the plug-in.
@@ -60,7 +60,7 @@ calls using:
as the plug-in key. */
enum ladspaPluginType
enum LadspaPluginType
{
SOURCE,
TRANSFER,
@@ -70,14 +70,14 @@ enum ladspaPluginType
OTHER
};
typedef struct ladspaManagerStorage
typedef struct LadspaManagerStorage
{
LADSPA_Descriptor_Function descriptorFunction;
uint32_t index;
ladspaPluginType type;
LadspaPluginType type;
uint16_t inputChannels;
uint16_t outputChannels;
} ladspaManagerDescription;
} LadspaManagerDescription;
class LMMS_EXPORT LadspaManager
@@ -88,7 +88,7 @@ public:
virtual ~LadspaManager();
l_sortable_plugin_t getSortedPlugins();
ladspaManagerDescription * getDescription( const ladspa_key_t &
LadspaManagerDescription * getDescription( const ladspa_key_t &
_plugin );
/* This identifier can be used as a unique, case-sensitive
@@ -339,9 +339,9 @@ private:
const LADSPA_PortRangeHint* getPortRangeHint( const ladspa_key_t& _plugin,
uint32_t _port );
typedef QMap<ladspa_key_t, ladspaManagerDescription *>
ladspaManagerMapType;
ladspaManagerMapType m_ladspaManagerMap;
typedef QMap<ladspa_key_t, LadspaManagerDescription *>
LadspaManagerMapType;
LadspaManagerMapType m_ladspaManagerMap;
l_sortable_plugin_t m_sortedPlugins;
} ;

View File

@@ -1,5 +1,5 @@
/*
* LedCheckbox.h - class LedCheckBox, an improved QCheckBox
* LedCheckBox.h - class LedCheckBox, an improved QCheckBox
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*

View File

@@ -135,6 +135,8 @@ public:
static void saveWidgetState( QWidget * _w, QDomElement & _de );
static void restoreWidgetState( QWidget * _w, const QDomElement & _de );
bool eventFilter(QObject* watched, QEvent* event) override;
public slots:
void resetWindowTitle();

View File

@@ -34,7 +34,6 @@
#include "Engine.h"
#include "Fader.h"
#include "PixmapButton.h"
#include "ToolTip.h"
#include "embed.h"
#include "EffectRackView.h"

View File

@@ -1,5 +1,5 @@
/*
* PluginBrowser.h - include file for pluginBrowser
* PluginBrowser.h - include file for PluginBrowser
*
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*

60
include/RaiiHelpers.h Normal file
View File

@@ -0,0 +1,60 @@
/*
* RaiiHelpers.h
*
* Copyright (c) 2022 Dominic Clark <mrdomclark/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
#ifndef RAII_HELPERS_H
#define RAII_HELPERS_H
#include <cstddef>
#include <memory>
template<typename T, T Null>
class NullableResource
{
public:
NullableResource() = default;
NullableResource(std::nullptr_t) noexcept { }
NullableResource(T value) noexcept : m_value{value} { }
operator T() const noexcept { return m_value; }
explicit operator bool() const noexcept { return m_value != Null; }
friend bool operator==(NullableResource a, NullableResource b) noexcept { return a.m_value == b.m_value; }
friend bool operator==(NullableResource a, T b) noexcept { return a.m_value == b; }
friend bool operator==(T a, NullableResource b) noexcept { return a == b.m_value; }
friend bool operator!=(NullableResource a, NullableResource b) noexcept { return a.m_value != b.m_value; }
friend bool operator!=(NullableResource a, T b) noexcept { return a.m_value != b; }
friend bool operator!=(T a, NullableResource b) noexcept { return a != b.m_value; }
private:
T m_value = Null;
};
template<typename T, T Null, auto Deleter>
struct NullableResourceDeleter
{
using pointer = NullableResource<T, Null>;
void operator()(T value) const noexcept { Deleter(value); }
};
template<typename T, T Null, auto Deleter>
using UniqueNullableResource = std::unique_ptr<T, NullableResourceDeleter<T, Null, Deleter>>;
#endif // RAII_HELPERS_H

File diff suppressed because it is too large Load Diff

672
include/RemotePluginBase.h Normal file
View File

@@ -0,0 +1,672 @@
/*
* RemotePluginBase.h - base class providing RPC like mechanisms
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef REMOTE_PLUGIN_BASE_H
#define REMOTE_PLUGIN_BASE_H
#include "MidiEvent.h"
#include "VstSyncData.h"
#include <atomic>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cassert>
#if !(defined(LMMS_HAVE_SYS_IPC_H) && defined(LMMS_HAVE_SEMAPHORE_H))
#define SYNC_WITH_SHM_FIFO
#define USE_QT_SEMAPHORES
#ifdef LMMS_HAVE_PROCESS_H
#include <process.h>
#endif
#include <QtGlobal>
#include <QSystemSemaphore>
#include <QUuid>
#else
#ifdef LMMS_HAVE_UNISTD_H
#include <unistd.h>
#endif
#endif
#ifdef LMMS_HAVE_LOCALE_H
#include <clocale>
#endif
#ifdef LMMS_HAVE_PTHREAD_H
#include <pthread.h>
#endif
#ifdef BUILD_REMOTE_PLUGIN_CLIENT
#undef LMMS_EXPORT
#define LMMS_EXPORT
#ifndef SYNC_WITH_SHM_FIFO
#include <sys/socket.h>
#include <sys/un.h>
#endif
#else
#include "lmms_export.h"
#include <QMutex>
#include <QProcess>
#include <QThread>
#include <QString>
#ifndef SYNC_WITH_SHM_FIFO
#include <poll.h>
#include <unistd.h>
#endif
#endif
#ifdef SYNC_WITH_SHM_FIFO
#include "SharedMemory.h"
// sometimes we need to exchange bigger messages (e.g. for VST parameter dumps)
// so set a usable value here
const int SHM_FIFO_SIZE = 512*1024;
// implements a FIFO inside a shared memory segment
class shmFifo
{
// need this union to handle different sizes of sem_t on 32 bit
// and 64 bit platforms
union sem32_t
{
int semKey;
char fill[32];
} ;
struct shmData
{
sem32_t dataSem; // semaphore for locking this
// FIFO management data
sem32_t messageSem; // semaphore for incoming messages
int32_t startPtr; // current start of FIFO in memory
int32_t endPtr; // current end of FIFO in memory
char data[SHM_FIFO_SIZE]; // actual data
} ;
public:
// constructor for master-side
shmFifo() :
m_invalid( false ),
m_master( true ),
m_dataSem( QString() ),
m_messageSem( QString() ),
m_lockDepth( 0 )
{
m_data.create(QUuid::createUuid().toString().toStdString());
m_data->startPtr = m_data->endPtr = 0;
static int k = 0;
m_data->dataSem.semKey = ( getpid()<<10 ) + ++k;
m_data->messageSem.semKey = ( getpid()<<10 ) + ++k;
m_dataSem.setKey( QString::number( m_data->dataSem.semKey ),
1, QSystemSemaphore::Create );
m_messageSem.setKey( QString::number(
m_data->messageSem.semKey ),
0, QSystemSemaphore::Create );
}
// constructor for remote-/client-side - use _shm_key for making up
// the connection to master
shmFifo(const std::string& shmKey) :
m_invalid( false ),
m_master( false ),
m_dataSem( QString() ),
m_messageSem( QString() ),
m_lockDepth( 0 )
{
m_data.attach(shmKey);
m_dataSem.setKey( QString::number( m_data->dataSem.semKey ) );
m_messageSem.setKey( QString::number(
m_data->messageSem.semKey ) );
}
inline bool isInvalid() const
{
return m_invalid;
}
void invalidate()
{
m_invalid = true;
}
// do we act as master (i.e. not as remote-process?)
inline bool isMaster() const
{
return m_master;
}
// recursive lock
inline void lock()
{
if( !isInvalid() && m_lockDepth.fetch_add( 1 ) == 0 )
{
m_dataSem.acquire();
}
}
// recursive unlock
inline void unlock()
{
if( m_lockDepth.fetch_sub( 1 ) <= 1 )
{
m_dataSem.release();
}
}
// wait until message-semaphore is available
inline void waitForMessage()
{
if( !isInvalid() )
{
m_messageSem.acquire();
}
}
// increase message-semaphore
inline void messageSent()
{
m_messageSem.release();
}
inline int32_t readInt()
{
int32_t i;
read( &i, sizeof( i ) );
return i;
}
inline void writeInt( const int32_t & _i )
{
write( &_i, sizeof( _i ) );
}
inline std::string readString()
{
const int len = readInt();
if( len )
{
char * sc = new char[len + 1];
read( sc, len );
sc[len] = 0;
std::string s( sc );
delete[] sc;
return s;
}
return std::string();
}
inline void writeString( const std::string & _s )
{
const int len = _s.size();
writeInt( len );
write( _s.c_str(), len );
}
inline bool messagesLeft()
{
if( isInvalid() )
{
return false;
}
lock();
const bool empty = ( m_data->startPtr == m_data->endPtr );
unlock();
return !empty;
}
const std::string& shmKey() const
{
return m_data.key();
}
private:
static inline void fastMemCpy( void * _dest, const void * _src,
const int _len )
{
// calling memcpy() for just an integer is obsolete overhead
if( _len == 4 )
{
*( (int32_t *) _dest ) = *( (int32_t *) _src );
}
else
{
memcpy( _dest, _src, _len );
}
}
void read( void * _buf, int _len )
{
if( isInvalid() )
{
memset( _buf, 0, _len );
return;
}
lock();
while( isInvalid() == false &&
_len > m_data->endPtr - m_data->startPtr )
{
unlock();
#ifndef LMMS_BUILD_WIN32
usleep( 5 );
#endif
lock();
}
fastMemCpy( _buf, m_data->data + m_data->startPtr, _len );
m_data->startPtr += _len;
// nothing left?
if( m_data->startPtr == m_data->endPtr )
{
// then reset to 0
m_data->startPtr = m_data->endPtr = 0;
}
unlock();
}
void write( const void * _buf, int _len )
{
if( isInvalid() || _len > SHM_FIFO_SIZE )
{
return;
}
lock();
while( _len > SHM_FIFO_SIZE - m_data->endPtr )
{
// if no space is left, try to move data to front
if( m_data->startPtr > 0 )
{
memmove( m_data->data,
m_data->data + m_data->startPtr,
m_data->endPtr - m_data->startPtr );
m_data->endPtr = m_data->endPtr -
m_data->startPtr;
m_data->startPtr = 0;
}
unlock();
#ifndef LMMS_BUILD_WIN32
usleep( 5 );
#endif
lock();
}
fastMemCpy( m_data->data + m_data->endPtr, _buf, _len );
m_data->endPtr += _len;
unlock();
}
volatile bool m_invalid;
bool m_master;
SharedMemory<shmData> m_data;
QSystemSemaphore m_dataSem;
QSystemSemaphore m_messageSem;
std::atomic_int m_lockDepth;
} ;
#endif
enum RemoteMessageIDs
{
IdUndefined,
IdHostInfoGotten,
IdInitDone,
IdQuit,
IdSampleRateInformation,
IdBufferSizeInformation,
IdInformationUpdated,
IdMidiEvent,
IdStartProcessing,
IdProcessingDone,
IdChangeSharedMemoryKey,
IdChangeInputCount,
IdChangeOutputCount,
IdChangeInputOutputCount,
IdShowUI,
IdHideUI,
IdToggleUI,
IdIsUIVisible,
IdSaveSettingsToString,
IdSaveSettingsToFile,
IdLoadSettingsFromString,
IdLoadSettingsFromFile,
IdSavePresetFile,
IdLoadPresetFile,
IdDebugMessage,
IdIdle,
IdUserBase = 64
} ;
class LMMS_EXPORT RemotePluginBase
{
public:
struct message
{
message() :
id( IdUndefined ),
data()
{
}
message( const message & _m ) :
id( _m.id ),
data( _m.data )
{
}
message( int _id ) :
id( _id ),
data()
{
}
inline message & addString( const std::string & _s )
{
data.push_back( _s );
return *this;
}
message & addInt( int _i )
{
char buf[32];
sprintf( buf, "%d", _i );
data.push_back( std::string( buf ) );
return *this;
}
message & addFloat( float _f )
{
char buf[32];
sprintf( buf, "%f", _f );
data.push_back( std::string( buf ) );
return *this;
}
inline std::string getString( int _p = 0 ) const
{
return data[_p];
}
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
inline QString getQString( int _p = 0 ) const
{
return QString::fromStdString( getString( _p ) );
}
#endif
inline int getInt( int _p = 0 ) const
{
return atoi( data[_p].c_str() );
}
inline float getFloat( int _p ) const
{
return (float) atof( data[_p].c_str() );
}
inline bool operator==( const message & _m ) const
{
return( id == _m.id );
}
int id;
private:
std::vector<std::string> data;
friend class RemotePluginBase;
} ;
#ifdef SYNC_WITH_SHM_FIFO
RemotePluginBase( shmFifo * _in, shmFifo * _out );
#else
RemotePluginBase();
#endif
virtual ~RemotePluginBase();
#ifdef SYNC_WITH_SHM_FIFO
void reset( shmFifo *in, shmFifo *out )
{
delete m_in;
delete m_out;
m_in = in;
m_out = out;
}
#endif
int sendMessage( const message & _m );
message receiveMessage();
inline bool isInvalid() const
{
#ifdef SYNC_WITH_SHM_FIFO
return m_in->isInvalid() || m_out->isInvalid();
#else
return m_invalid;
#endif
}
message waitForMessage( const message & _m,
bool _busy_waiting = false );
inline message fetchAndProcessNextMessage()
{
message m = receiveMessage();
processMessage( m );
return m;
}
#ifndef SYNC_WITH_SHM_FIFO
inline int32_t readInt()
{
int32_t i;
read( &i, sizeof( i ) );
return i;
}
inline void writeInt( const int32_t & _i )
{
write( &_i, sizeof( _i ) );
}
inline std::string readString()
{
const int len = readInt();
if( len )
{
char * sc = new char[len + 1];
read( sc, len );
sc[len] = 0;
std::string s( sc );
delete[] sc;
return s;
}
return std::string();
}
inline void writeString( const std::string & _s )
{
const int len = _s.size();
writeInt( len );
write( _s.c_str(), len );
}
#endif
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
inline bool messagesLeft()
{
#ifdef SYNC_WITH_SHM_FIFO
return m_in->messagesLeft();
#else
struct pollfd pollin;
pollin.fd = m_socket;
pollin.events = POLLIN;
if ( poll( &pollin, 1, 0 ) == -1 )
{
qWarning( "Unexpected poll error." );
}
return pollin.revents & POLLIN;
#endif
}
inline void fetchAndProcessAllMessages()
{
while( messagesLeft() )
{
fetchAndProcessNextMessage();
}
}
static bool isMainThreadWaiting()
{
return waitDepthCounter() > 0;
}
#endif
virtual bool processMessage( const message & _m ) = 0;
protected:
#ifdef SYNC_WITH_SHM_FIFO
inline const shmFifo * in() const
{
return m_in;
}
inline const shmFifo * out() const
{
return m_out;
}
#endif
inline void invalidate()
{
#ifdef SYNC_WITH_SHM_FIFO
m_in->invalidate();
m_out->invalidate();
m_in->messageSent();
#else
m_invalid = true;
#endif
}
#ifndef SYNC_WITH_SHM_FIFO
int m_socket;
#endif
private:
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
static int & waitDepthCounter()
{
static int waitDepth = 0;
return waitDepth;
}
#endif
#ifdef SYNC_WITH_SHM_FIFO
shmFifo * m_in;
shmFifo * m_out;
#else
void read( void * _buf, int _len )
{
if( isInvalid() )
{
memset( _buf, 0, _len );
return;
}
char * buf = (char *) _buf;
int remaining = _len;
while ( remaining )
{
ssize_t nread = ::read( m_socket, buf, remaining );
switch ( nread )
{
case -1:
fprintf( stderr,
"Error while reading.\n" );
case 0:
invalidate();
memset( _buf, 0, _len );
return;
}
buf += nread;
remaining -= nread;
}
}
void write( const void * _buf, int _len )
{
if( isInvalid() )
{
return;
}
const char * buf = (const char *) _buf;
int remaining = _len;
while ( remaining )
{
ssize_t nwritten = ::write( m_socket, buf, remaining );
switch ( nwritten )
{
case -1:
fprintf( stderr,
"Error while writing.\n" );
case 0:
invalidate();
return;
}
buf += nwritten;
remaining -= nwritten;
}
}
bool m_invalid;
pthread_mutex_t m_receiveMutex;
pthread_mutex_t m_sendMutex;
#endif
} ;
#endif // REMOTE_PLUGIN_BASE_H

View File

@@ -0,0 +1,358 @@
/*
* RemotePluginClient.h
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef REMOTE_PLUGIN_CLIENT_H
#define REMOTE_PLUGIN_CLIENT_H
#include "RemotePluginBase.h"
#include <stdexcept>
#ifndef LMMS_BUILD_WIN32
# include <condition_variable>
# include <mutex>
# include <thread>
# include <signal.h>
# include <unistd.h>
#endif
#include "SharedMemory.h"
class RemotePluginClient : public RemotePluginBase
{
public:
#ifdef SYNC_WITH_SHM_FIFO
RemotePluginClient( const std::string& _shm_in, const std::string& _shm_out );
#else
RemotePluginClient( const char * socketPath );
#endif
virtual ~RemotePluginClient();
const VstSyncData* getVstSyncData();
virtual bool processMessage( const message & _m );
virtual void process( const sampleFrame * _in_buf,
sampleFrame * _out_buf ) = 0;
virtual void processMidiEvent( const MidiEvent&, const f_cnt_t /* _offset */ )
{
}
virtual void updateSampleRate()
{
}
virtual void updateBufferSize()
{
}
inline sample_rate_t sampleRate() const
{
return m_sampleRate;
}
inline fpp_t bufferSize() const
{
return m_bufferSize;
}
void setInputCount( int _i )
{
m_inputCount = _i;
sendMessage( message( IdChangeInputCount ).addInt( _i ) );
}
void setOutputCount( int _i )
{
m_outputCount = _i;
sendMessage( message( IdChangeOutputCount ).addInt( _i ) );
}
void setInputOutputCount( int i, int o )
{
m_inputCount = i;
m_outputCount = o;
sendMessage( message( IdChangeInputOutputCount )
.addInt( i )
.addInt( o ) );
}
virtual int inputCount() const
{
return m_inputCount;
}
virtual int outputCount() const
{
return m_outputCount;
}
void debugMessage( const std::string & _s )
{
sendMessage( message( IdDebugMessage ).addString( _s ) );
}
private:
void setShmKey(const std::string& key);
void doProcessing();
SharedMemory<float[]> m_audioBuffer;
SharedMemory<const VstSyncData> m_vstSyncShm;
const VstSyncData* m_vstSyncData;
int m_inputCount;
int m_outputCount;
sample_rate_t m_sampleRate;
fpp_t m_bufferSize;
} ;
#ifndef LMMS_BUILD_WIN32
class PollParentThread
{
public:
PollParentThread() :
m_stop{false},
m_thread{
[this]
{
using namespace std::literals::chrono_literals;
auto lock = std::unique_lock{m_mutex};
while (!m_cv.wait_for(lock, 500ms, [this] { return m_stop; }))
{
if (getppid() == 1)
{
kill(getpid(), SIGHUP);
break;
}
}
}
}
{ }
~PollParentThread()
{
{
const auto lock = std::unique_lock{m_mutex};
m_stop = true;
}
m_cv.notify_all();
m_thread.join();
}
private:
bool m_stop;
std::mutex m_mutex;
std::condition_variable m_cv;
std::thread m_thread;
};
#endif
#ifdef SYNC_WITH_SHM_FIFO
RemotePluginClient::RemotePluginClient( const std::string& _shm_in, const std::string& _shm_out ) :
RemotePluginBase( new shmFifo( _shm_in ), new shmFifo( _shm_out ) ),
#else
RemotePluginClient::RemotePluginClient( const char * socketPath ) :
RemotePluginBase(),
#endif
m_vstSyncData( nullptr ),
m_inputCount( 0 ),
m_outputCount( 0 ),
m_sampleRate( 44100 ),
m_bufferSize( 0 )
{
#ifndef SYNC_WITH_SHM_FIFO
struct sockaddr_un sa;
sa.sun_family = AF_LOCAL;
size_t length = strlen( socketPath );
if ( length >= sizeof sa.sun_path )
{
length = sizeof sa.sun_path - 1;
fprintf( stderr, "Socket path too long.\n" );
}
memcpy( sa.sun_path, socketPath, length );
sa.sun_path[length] = '\0';
m_socket = socket( PF_LOCAL, SOCK_STREAM, 0 );
if ( m_socket == -1 )
{
fprintf( stderr, "Could not connect to local server.\n" );
}
if ( ::connect( m_socket, (struct sockaddr *) &sa, sizeof sa ) == -1 )
{
fprintf( stderr, "Could not connect to local server.\n" );
}
#endif
try
{
m_vstSyncShm.attach("usr_bin_lmms");
m_vstSyncData = m_vstSyncShm.get();
m_bufferSize = m_vstSyncData->m_bufferSize;
m_sampleRate = m_vstSyncData->m_sampleRate;
}
catch (const std::runtime_error&)
{
// if attaching shared memory fails
sendMessage( IdSampleRateInformation );
sendMessage( IdBufferSizeInformation );
if( waitForMessage( IdBufferSizeInformation ).id
!= IdBufferSizeInformation )
{
fprintf( stderr, "Could not get buffer size information\n" );
}
}
sendMessage( IdHostInfoGotten );
}
RemotePluginClient::~RemotePluginClient()
{
sendMessage( IdQuit );
#ifndef SYNC_WITH_SHM_FIFO
if ( close( m_socket ) == -1)
{
fprintf( stderr, "Error freeing resources.\n" );
}
#endif
}
const VstSyncData* RemotePluginClient::getVstSyncData()
{
return m_vstSyncData;
}
bool RemotePluginClient::processMessage( const message & _m )
{
message reply_message( _m.id );
bool reply = false;
switch( _m.id )
{
case IdUndefined:
return false;
case IdSampleRateInformation:
m_sampleRate = _m.getInt();
updateSampleRate();
reply_message.id = IdInformationUpdated;
reply = true;
break;
case IdBufferSizeInformation:
// Should LMMS gain the ability to change buffer size
// without a restart, it must wait for this message to
// complete processing or else risk VST crashes
m_bufferSize = _m.getInt();
updateBufferSize();
break;
case IdQuit:
return false;
case IdMidiEvent:
processMidiEvent(
MidiEvent( static_cast<MidiEventTypes>(
_m.getInt( 0 ) ),
_m.getInt( 1 ),
_m.getInt( 2 ),
_m.getInt( 3 ) ),
_m.getInt( 4 ) );
break;
case IdStartProcessing:
doProcessing();
reply_message.id = IdProcessingDone;
reply = true;
break;
case IdChangeSharedMemoryKey:
setShmKey(_m.getString(0));
break;
case IdInitDone:
break;
default:
{
char buf[64];
sprintf( buf, "undefined message: %d\n", (int) _m.id );
debugMessage( buf );
break;
}
}
if( reply )
{
sendMessage( reply_message );
}
return true;
}
void RemotePluginClient::setShmKey(const std::string& key)
{
try
{
m_audioBuffer.attach(key);
}
catch (const std::runtime_error& error)
{
debugMessage(std::string{"failed getting shared memory: "} + error.what() + '\n');
}
}
void RemotePluginClient::doProcessing()
{
if (m_audioBuffer)
{
process( (sampleFrame *)( m_inputCount > 0 ? m_audioBuffer.get() : nullptr ),
(sampleFrame *)( m_audioBuffer.get() +
( m_inputCount*m_bufferSize ) ) );
}
else
{
debugMessage( "doProcessing(): have no shared memory!\n" );
}
}
#endif // REMOTE_PLUGIN_CLIENT_H

View File

@@ -31,7 +31,7 @@
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
#include "LedCheckbox.h"
#include "LedCheckBox.h"
#include "lmmsconfig.h"
#include "MidiClient.h"
#include "MidiSetupWidget.h"

143
include/SharedMemory.h Normal file
View File

@@ -0,0 +1,143 @@
/*
* SharedMemory.h
*
* Copyright (c) 2022 Dominic Clark <mrdomclark/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
#ifndef SHARED_MEMORY_H
#define SHARED_MEMORY_H
#include <memory>
#include <string>
#include <type_traits>
namespace detail {
class SharedMemoryImpl;
class SharedMemoryData
{
public:
SharedMemoryData() noexcept;
SharedMemoryData(std::string&& key, bool readOnly);
SharedMemoryData(std::string&& key, std::size_t size, bool readOnly);
~SharedMemoryData();
SharedMemoryData(SharedMemoryData&& other) noexcept;
SharedMemoryData& operator=(SharedMemoryData&& other) noexcept
{
auto temp = std::move(other);
swap(*this, temp);
return *this;
}
friend void swap(SharedMemoryData& a, SharedMemoryData& b) noexcept
{
using std::swap;
swap(a.m_key, b.m_key);
swap(a.m_impl, b.m_impl);
swap(a.m_ptr, b.m_ptr);
}
const std::string& key() const noexcept { return m_key; }
void* get() const noexcept { return m_ptr; }
private:
std::string m_key;
std::unique_ptr<SharedMemoryImpl> m_impl;
void* m_ptr = nullptr;
};
} // namespace detail
template<typename T>
class SharedMemory
{
// This is stricter than necessary, but keeps things easy for now
static_assert(std::is_trivial_v<T>, "objects held in shared memory must be trivial");
public:
SharedMemory() = default;
SharedMemory(SharedMemory&&) = default;
SharedMemory& operator=(SharedMemory&&) = default;
void attach(std::string key)
{
m_data = detail::SharedMemoryData{std::move(key), std::is_const_v<T>};
}
void create(std::string key)
{
m_data = detail::SharedMemoryData{std::move(key), sizeof(T), std::is_const_v<T>};
}
void detach() noexcept
{
m_data = detail::SharedMemoryData{};
}
const std::string& key() const noexcept { return m_data.key(); }
T* get() const noexcept { return static_cast<T*>(m_data.get()); }
T* operator->() const noexcept { return get(); }
T& operator*() const noexcept { return *get(); }
explicit operator bool() const noexcept { return get() != nullptr; }
private:
detail::SharedMemoryData m_data;
};
template<typename T>
class SharedMemory<T[]>
{
// This is stricter than necessary, but keeps things easy for now
static_assert(std::is_trivial_v<T>, "objects held in shared memory must be trivial");
public:
SharedMemory() = default;
SharedMemory(SharedMemory&&) = default;
SharedMemory& operator=(SharedMemory&&) = default;
void attach(std::string key)
{
m_data = detail::SharedMemoryData{std::move(key), std::is_const_v<T>};
}
void create(std::string key, std::size_t size)
{
m_data = detail::SharedMemoryData{std::move(key), size * sizeof(T), std::is_const_v<T>};
}
void detach() noexcept
{
m_data = detail::SharedMemoryData{};
}
const std::string& key() const noexcept { return m_data.key(); }
T* get() const noexcept { return static_cast<T*>(m_data.get()); }
T& operator[](std::size_t index) const noexcept { return get()[index]; }
explicit operator bool() const noexcept { return get() != nullptr; }
private:
detail::SharedMemoryData m_data;
};
#endif // SHARED_MEMORY_H

View File

@@ -180,6 +180,10 @@ public slots:
{
updatePosition( TimePos() );
}
void setSnapSize( const float snapSize )
{
m_snapSize = snapSize;
}
void toggleAutoScroll( int _n );
void toggleLoopPoints( int _n );
void toggleBehaviourAtStop( int _n );
@@ -217,6 +221,7 @@ private:
int m_xOffset;
int m_posMarkerX;
float m_ppb;
float m_snapSize;
Song::PlayPos & m_pos;
const TimePos & m_begin;
const Song::PlayModes m_mode;

View File

@@ -1,42 +0,0 @@
/*
* ToolTip.h - namespace toolTip, a tooltip-wrapper for LMMS
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef TOOLTIP_H
#define TOOLTIP_H
#include <qstring.h>
#include "lmms_export.h"
class QWidget;
struct ToolTip
{
static void LMMS_EXPORT add( QWidget * _w, const QString & _txt );
} ;
#endif

View File

@@ -27,8 +27,8 @@
#define VST_SYNC_CONTROLLER_H
#include <QObject>
#include <QSharedMemory>
#include "SharedMemory.h"
#include "VstSyncData.h"
@@ -75,11 +75,7 @@ private slots:
private:
VstSyncData* m_syncData;
int m_shmID;
QSharedMemory m_shm;
SharedMemory<VstSyncData> m_shm;
};
#endif

View File

@@ -33,10 +33,6 @@
// When defined, latency should be subtracted from song PPQ position
//#define VST_SNC_LATENCY
// define file for ftok as shared memory shmget key
constexpr const char* VST_SNC_SHM_KEY_FILE = "/dev/null";
//constexpr int64_t VST_SNC_SHM_RND_KEY = 3561653564469;
struct VstSyncData

View File

@@ -31,10 +31,10 @@
#include "Midi.h"
#include "volume.h"
inline stereoVolumeVector panningToVolumeVector( panning_t _p,
inline StereoVolumeVector panningToVolumeVector( panning_t _p,
float _scale = 1.0f )
{
stereoVolumeVector v = { { _scale, _scale } };
StereoVolumeVector v = { { _scale, _scale } };
const float pf = _p / 100.0f;
v.vol[_p >= PanningCenter ? 0 : 1] *= 1.0f - qAbs<float>( pf );
return v;

View File

@@ -35,6 +35,6 @@ constexpr volume_t DefaultVolume = 100;
typedef struct
{
float vol[2];
} stereoVolumeVector;
} StereoVolumeVector;
#endif