mirror of
https://github.com/LMMS/lmms.git
synced 2026-05-14 09:46:57 -04:00
Refactor shared memory (#6404)
This commit is contained in:
@@ -133,7 +133,6 @@ CHECK_INCLUDE_FILES(semaphore.h LMMS_HAVE_SEMAPHORE_H)
|
||||
CHECK_INCLUDE_FILES(unistd.h LMMS_HAVE_UNISTD_H)
|
||||
CHECK_INCLUDE_FILES(sys/types.h LMMS_HAVE_SYS_TYPES_H)
|
||||
CHECK_INCLUDE_FILES(sys/ipc.h LMMS_HAVE_SYS_IPC_H)
|
||||
CHECK_INCLUDE_FILES(sys/shm.h LMMS_HAVE_SYS_SHM_H)
|
||||
CHECK_INCLUDE_FILES(sys/time.h LMMS_HAVE_SYS_TIME_H)
|
||||
CHECK_INCLUDE_FILES(sys/times.h LMMS_HAVE_SYS_TIMES_H)
|
||||
CHECK_INCLUDE_FILES(sched.h LMMS_HAVE_SCHED_H)
|
||||
@@ -146,6 +145,9 @@ CHECK_INCLUDE_FILES(string.h LMMS_HAVE_STRING_H)
|
||||
CHECK_INCLUDE_FILES(process.h LMMS_HAVE_PROCESS_H)
|
||||
CHECK_INCLUDE_FILES(locale.h LMMS_HAVE_LOCALE_H)
|
||||
|
||||
include(CheckLibraryExists)
|
||||
check_library_exists(rt shm_open "" LMMS_HAVE_LIBRT)
|
||||
|
||||
LIST(APPEND CMAKE_PREFIX_PATH "${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
FIND_PACKAGE(Qt5 5.6.0 COMPONENTS Core Gui Widgets Xml REQUIRED)
|
||||
|
||||
60
include/RaiiHelpers.h
Normal file
60
include/RaiiHelpers.h
Normal 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
|
||||
@@ -26,6 +26,7 @@
|
||||
#define REMOTE_PLUGIN_H
|
||||
|
||||
#include "RemotePluginBase.h"
|
||||
#include "SharedMemory.h"
|
||||
|
||||
class RemotePlugin;
|
||||
|
||||
@@ -155,13 +156,9 @@ private:
|
||||
|
||||
QMutex m_commMutex;
|
||||
bool m_splitChannels;
|
||||
#ifdef USE_QT_SHMEM
|
||||
QSharedMemory m_shmObj;
|
||||
#else
|
||||
int m_shmID;
|
||||
#endif
|
||||
size_t m_shmSize;
|
||||
float * m_shm;
|
||||
|
||||
SharedMemory<float[]> m_audioBuffer;
|
||||
std::size_t m_audioBufferSize;
|
||||
|
||||
int m_inputCount;
|
||||
int m_outputCount;
|
||||
|
||||
@@ -46,26 +46,12 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QSystemSemaphore>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LMMS_HAVE_SYS_SHM_H
|
||||
#include <sys/shm.h>
|
||||
|
||||
#include <QUuid>
|
||||
#else
|
||||
#ifdef LMMS_HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#else
|
||||
#define USE_QT_SHMEM
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QSharedMemory>
|
||||
|
||||
#if !defined(LMMS_HAVE_SYS_TYPES_H) || defined(LMMS_BUILD_WIN32)
|
||||
typedef int32_t key_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LMMS_HAVE_LOCALE_H
|
||||
#include <clocale>
|
||||
@@ -100,6 +86,9 @@ typedef int32_t key_t;
|
||||
#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;
|
||||
@@ -120,8 +109,8 @@ class shmFifo
|
||||
sem32_t dataSem; // semaphore for locking this
|
||||
// FIFO management data
|
||||
sem32_t messageSem; // semaphore for incoming messages
|
||||
volatile int32_t startPtr; // current start of FIFO in memory
|
||||
volatile int32_t endPtr; // current end of FIFO in memory
|
||||
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
|
||||
} ;
|
||||
|
||||
@@ -130,33 +119,11 @@ public:
|
||||
shmFifo() :
|
||||
m_invalid( false ),
|
||||
m_master( true ),
|
||||
m_shmKey( 0 ),
|
||||
#ifdef USE_QT_SHMEM
|
||||
m_shmObj(),
|
||||
#else
|
||||
m_shmID( -1 ),
|
||||
#endif
|
||||
m_data( nullptr ),
|
||||
m_dataSem( QString() ),
|
||||
m_messageSem( QString() ),
|
||||
m_lockDepth( 0 )
|
||||
{
|
||||
#ifdef USE_QT_SHMEM
|
||||
do
|
||||
{
|
||||
m_shmObj.setKey( QString( "%1" ).arg( ++m_shmKey ) );
|
||||
m_shmObj.create( sizeof( shmData ) );
|
||||
} while( m_shmObj.error() != QSharedMemory::NoError );
|
||||
|
||||
m_data = (shmData *) m_shmObj.data();
|
||||
#else
|
||||
while( ( m_shmID = shmget( ++m_shmKey, sizeof( shmData ),
|
||||
IPC_CREAT | IPC_EXCL | 0600 ) ) == -1 )
|
||||
{
|
||||
}
|
||||
m_data = (shmData *) shmat( m_shmID, 0, 0 );
|
||||
#endif
|
||||
assert( m_data != nullptr );
|
||||
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;
|
||||
@@ -170,51 +137,19 @@ public:
|
||||
|
||||
// constructor for remote-/client-side - use _shm_key for making up
|
||||
// the connection to master
|
||||
shmFifo( key_t _shm_key ) :
|
||||
shmFifo(const std::string& shmKey) :
|
||||
m_invalid( false ),
|
||||
m_master( false ),
|
||||
m_shmKey( 0 ),
|
||||
#ifdef USE_QT_SHMEM
|
||||
m_shmObj( QString::number( _shm_key ) ),
|
||||
#else
|
||||
m_shmID( shmget( _shm_key, 0, 0 ) ),
|
||||
#endif
|
||||
m_data( nullptr ),
|
||||
m_dataSem( QString() ),
|
||||
m_messageSem( QString() ),
|
||||
m_lockDepth( 0 )
|
||||
{
|
||||
#ifdef USE_QT_SHMEM
|
||||
if( m_shmObj.attach() )
|
||||
{
|
||||
m_data = (shmData *) m_shmObj.data();
|
||||
}
|
||||
#else
|
||||
if( m_shmID != -1 )
|
||||
{
|
||||
m_data = (shmData *) shmat( m_shmID, 0, 0 );
|
||||
}
|
||||
#endif
|
||||
assert( m_data != nullptr );
|
||||
m_data.attach(shmKey);
|
||||
m_dataSem.setKey( QString::number( m_data->dataSem.semKey ) );
|
||||
m_messageSem.setKey( QString::number(
|
||||
m_data->messageSem.semKey ) );
|
||||
}
|
||||
|
||||
~shmFifo()
|
||||
{
|
||||
// master?
|
||||
if( m_master )
|
||||
{
|
||||
#ifndef USE_QT_SHMEM
|
||||
shmctl( m_shmID, IPC_RMID, nullptr );
|
||||
#endif
|
||||
}
|
||||
#ifndef USE_QT_SHMEM
|
||||
shmdt( m_data );
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool isInvalid() const
|
||||
{
|
||||
return m_invalid;
|
||||
@@ -314,9 +249,9 @@ public:
|
||||
}
|
||||
|
||||
|
||||
inline int shmKey() const
|
||||
const std::string& shmKey() const
|
||||
{
|
||||
return m_shmKey;
|
||||
return m_data.key();
|
||||
}
|
||||
|
||||
|
||||
@@ -395,13 +330,7 @@ private:
|
||||
|
||||
volatile bool m_invalid;
|
||||
bool m_master;
|
||||
key_t m_shmKey;
|
||||
#ifdef USE_QT_SHMEM
|
||||
QSharedMemory m_shmObj;
|
||||
#else
|
||||
int m_shmID;
|
||||
#endif
|
||||
shmData * m_data;
|
||||
SharedMemory<shmData> m_data;
|
||||
QSystemSemaphore m_dataSem;
|
||||
QSystemSemaphore m_messageSem;
|
||||
std::atomic_int m_lockDepth;
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "RemotePluginBase.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef LMMS_BUILD_WIN32
|
||||
# include <condition_variable>
|
||||
# include <mutex>
|
||||
@@ -36,18 +38,20 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "SharedMemory.h"
|
||||
|
||||
class RemotePluginClient : public RemotePluginBase
|
||||
{
|
||||
public:
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
RemotePluginClient( key_t _shm_in, key_t _shm_out );
|
||||
RemotePluginClient( const std::string& _shm_in, const std::string& _shm_out );
|
||||
#else
|
||||
RemotePluginClient( const char * socketPath );
|
||||
#endif
|
||||
virtual ~RemotePluginClient();
|
||||
#ifdef USE_QT_SHMEM
|
||||
VstSyncData * getQtVSTshm();
|
||||
#endif
|
||||
|
||||
const VstSyncData* getVstSyncData();
|
||||
|
||||
virtual bool processMessage( const message & _m );
|
||||
|
||||
virtual void process( const sampleFrame * _in_buf,
|
||||
@@ -57,11 +61,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
inline float * sharedMemory()
|
||||
{
|
||||
return m_shm;
|
||||
}
|
||||
|
||||
virtual void updateSampleRate()
|
||||
{
|
||||
}
|
||||
@@ -118,15 +117,12 @@ public:
|
||||
|
||||
|
||||
private:
|
||||
void setShmKey( key_t _key, int _size );
|
||||
void setShmKey(const std::string& key);
|
||||
void doProcessing();
|
||||
|
||||
#ifdef USE_QT_SHMEM
|
||||
QSharedMemory m_shmObj;
|
||||
QSharedMemory m_shmQtID;
|
||||
#endif
|
||||
VstSyncData * m_vstSyncData;
|
||||
float * m_shm;
|
||||
SharedMemory<float[]> m_audioBuffer;
|
||||
SharedMemory<const VstSyncData> m_vstSyncShm;
|
||||
const VstSyncData* m_vstSyncData;
|
||||
|
||||
int m_inputCount;
|
||||
int m_outputCount;
|
||||
@@ -177,18 +173,13 @@ private:
|
||||
#endif
|
||||
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) :
|
||||
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
|
||||
#ifdef USE_QT_SHMEM
|
||||
m_shmObj(),
|
||||
m_shmQtID( "/usr/bin/lmms" ),
|
||||
#endif
|
||||
m_vstSyncData( nullptr ),
|
||||
m_shm( nullptr ),
|
||||
m_inputCount( 0 ),
|
||||
m_outputCount( 0 ),
|
||||
m_sampleRate( 44100 ),
|
||||
@@ -218,60 +209,23 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) :
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_QT_SHMEM
|
||||
if( m_shmQtID.attach( QSharedMemory::ReadOnly ) )
|
||||
try
|
||||
{
|
||||
m_vstSyncData = (VstSyncData *) m_shmQtID.data();
|
||||
m_vstSyncShm.attach("usr_bin_lmms");
|
||||
m_vstSyncData = m_vstSyncShm.get();
|
||||
m_bufferSize = m_vstSyncData->m_bufferSize;
|
||||
m_sampleRate = m_vstSyncData->m_sampleRate;
|
||||
sendMessage( IdHostInfoGotten );
|
||||
return;
|
||||
}
|
||||
#else
|
||||
key_t key;
|
||||
int m_shmID;
|
||||
|
||||
if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
|
||||
catch (const std::runtime_error&)
|
||||
{
|
||||
perror( "RemotePluginClient::ftok" );
|
||||
}
|
||||
else
|
||||
{ // connect to shared memory segment
|
||||
if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 )
|
||||
// if attaching shared memory fails
|
||||
sendMessage( IdSampleRateInformation );
|
||||
sendMessage( IdBufferSizeInformation );
|
||||
if( waitForMessage( IdBufferSizeInformation ).id
|
||||
!= IdBufferSizeInformation )
|
||||
{
|
||||
perror( "RemotePluginClient::shmget" );
|
||||
fprintf( stderr, "Could not get buffer size information\n" );
|
||||
}
|
||||
else
|
||||
{ // attach segment
|
||||
m_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0);
|
||||
if( m_vstSyncData == (VstSyncData *)( -1 ) )
|
||||
{
|
||||
perror( "RemotePluginClient::shmat" );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bufferSize = m_vstSyncData->m_bufferSize;
|
||||
m_sampleRate = m_vstSyncData->m_sampleRate;
|
||||
sendMessage( IdHostInfoGotten );
|
||||
|
||||
// detach segment
|
||||
if( shmdt(m_vstSyncData) == -1 )
|
||||
{
|
||||
perror("RemotePluginClient::shmdt");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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 );
|
||||
}
|
||||
@@ -281,15 +235,8 @@ RemotePluginClient::RemotePluginClient( const char * socketPath ) :
|
||||
|
||||
RemotePluginClient::~RemotePluginClient()
|
||||
{
|
||||
#ifdef USE_QT_SHMEM
|
||||
m_shmQtID.detach();
|
||||
#endif
|
||||
sendMessage( IdQuit );
|
||||
|
||||
#ifndef USE_QT_SHMEM
|
||||
shmdt( m_shm );
|
||||
#endif
|
||||
|
||||
#ifndef SYNC_WITH_SHM_FIFO
|
||||
if ( close( m_socket ) == -1)
|
||||
{
|
||||
@@ -300,12 +247,12 @@ RemotePluginClient::~RemotePluginClient()
|
||||
|
||||
|
||||
|
||||
#ifdef USE_QT_SHMEM
|
||||
VstSyncData * RemotePluginClient::getQtVSTshm()
|
||||
|
||||
const VstSyncData* RemotePluginClient::getVstSyncData()
|
||||
{
|
||||
return m_vstSyncData;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -353,7 +300,7 @@ bool RemotePluginClient::processMessage( const message & _m )
|
||||
break;
|
||||
|
||||
case IdChangeSharedMemoryKey:
|
||||
setShmKey( _m.getInt( 0 ), _m.getInt( 1 ) );
|
||||
setShmKey(_m.getString(0));
|
||||
break;
|
||||
|
||||
case IdInitDone:
|
||||
@@ -378,43 +325,16 @@ bool RemotePluginClient::processMessage( const message & _m )
|
||||
|
||||
|
||||
|
||||
void RemotePluginClient::setShmKey( key_t _key, int _size )
|
||||
void RemotePluginClient::setShmKey(const std::string& key)
|
||||
{
|
||||
#ifdef USE_QT_SHMEM
|
||||
m_shmObj.setKey( QString::number( _key ) );
|
||||
if( m_shmObj.attach() || m_shmObj.error() == QSharedMemory::NoError )
|
||||
try
|
||||
{
|
||||
m_shm = (float *) m_shmObj.data();
|
||||
m_audioBuffer.attach(key);
|
||||
}
|
||||
else
|
||||
catch (const std::runtime_error& error)
|
||||
{
|
||||
char buf[64];
|
||||
sprintf( buf, "failed getting shared memory: %d\n", m_shmObj.error() );
|
||||
debugMessage( buf );
|
||||
debugMessage(std::string{"failed getting shared memory: "} + error.what() + '\n');
|
||||
}
|
||||
#else
|
||||
if( m_shm != nullptr )
|
||||
{
|
||||
shmdt( m_shm );
|
||||
m_shm = nullptr;
|
||||
}
|
||||
|
||||
// only called for detaching SHM?
|
||||
if( _key == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int shm_id = shmget( _key, _size, 0 );
|
||||
if( shm_id == -1 )
|
||||
{
|
||||
debugMessage( "failed getting shared memory\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_shm = (float *) shmat( shm_id, 0, 0 );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -422,10 +342,10 @@ void RemotePluginClient::setShmKey( key_t _key, int _size )
|
||||
|
||||
void RemotePluginClient::doProcessing()
|
||||
{
|
||||
if( m_shm != nullptr )
|
||||
if (m_audioBuffer)
|
||||
{
|
||||
process( (sampleFrame *)( m_inputCount > 0 ? m_shm : nullptr ),
|
||||
(sampleFrame *)( m_shm +
|
||||
process( (sampleFrame *)( m_inputCount > 0 ? m_audioBuffer.get() : nullptr ),
|
||||
(sampleFrame *)( m_audioBuffer.get() +
|
||||
( m_inputCount*m_bufferSize ) ) );
|
||||
}
|
||||
else
|
||||
|
||||
143
include/SharedMemory.h
Normal file
143
include/SharedMemory.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,11 +2,6 @@ if(LMMS_HAVE_GIG)
|
||||
INCLUDE(BuildPlugin)
|
||||
INCLUDE_DIRECTORIES(${GIG_INCLUDE_DIRS})
|
||||
|
||||
# Disable C++11 on Clang until gig.h is patched
|
||||
IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
SET(CMAKE_CXX_STANDARD 98)
|
||||
ENDIF()
|
||||
|
||||
# Required for not crashing loading files with libgig
|
||||
SET(GCC_COVERAGE_COMPILE_FLAGS "-fexceptions")
|
||||
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
|
||||
|
||||
@@ -31,7 +31,9 @@ set(export_variables
|
||||
"CMAKE_BUILD_TYPE"
|
||||
"LMMS_BUILD_LINUX"
|
||||
"LMMS_BUILD_WIN32"
|
||||
"PLUGIN_DIR")
|
||||
"PLUGIN_DIR"
|
||||
"LMMS_HAVE_LIBRT"
|
||||
)
|
||||
|
||||
SET(EXTERNALPROJECT_CMAKE_ARGS
|
||||
"-DBUILD_WITH_EXTERNALPROJECT=ON"
|
||||
|
||||
@@ -125,18 +125,6 @@ struct ERect
|
||||
|
||||
#include "VstSyncData.h"
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
#define USE_QT_SHMEM
|
||||
#endif
|
||||
|
||||
#ifndef USE_QT_SHMEM
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
static VstHostLanguages hlang = LanguageEnglish;
|
||||
@@ -182,7 +170,7 @@ class RemoteVstPlugin : public RemotePluginClient
|
||||
{
|
||||
public:
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
RemoteVstPlugin( key_t _shm_in, key_t _shm_out );
|
||||
RemoteVstPlugin( const std::string& _shm_in, const std::string& _shm_out );
|
||||
#else
|
||||
RemoteVstPlugin( const char * socketPath );
|
||||
#endif
|
||||
@@ -497,8 +485,7 @@ private:
|
||||
|
||||
in * m_in;
|
||||
|
||||
int m_shmID;
|
||||
VstSyncData* m_vstSyncData;
|
||||
const VstSyncData* m_vstSyncData;
|
||||
|
||||
} ;
|
||||
|
||||
@@ -506,7 +493,7 @@ private:
|
||||
|
||||
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) :
|
||||
RemoteVstPlugin::RemoteVstPlugin( const std::string& _shm_in, const std::string& _shm_out ) :
|
||||
RemotePluginClient( _shm_in, _shm_out ),
|
||||
#else
|
||||
RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) :
|
||||
@@ -530,49 +517,26 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) :
|
||||
m_currentSamplePos( 0 ),
|
||||
m_currentProgram( -1 ),
|
||||
m_in( nullptr ),
|
||||
m_shmID( -1 ),
|
||||
m_vstSyncData( nullptr )
|
||||
{
|
||||
__plugin = this;
|
||||
|
||||
#ifndef USE_QT_SHMEM
|
||||
key_t key;
|
||||
if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
|
||||
{
|
||||
perror( "RemoteVstPlugin.cpp::ftok" );
|
||||
}
|
||||
else
|
||||
{ // connect to shared memory segment
|
||||
if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 )
|
||||
{
|
||||
perror( "RemoteVstPlugin.cpp::shmget" );
|
||||
}
|
||||
else
|
||||
{ // attach segment
|
||||
m_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0);
|
||||
if( m_vstSyncData == (VstSyncData *)( -1 ) )
|
||||
{
|
||||
perror( "RemoteVstPlugin.cpp::shmat" );
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
m_vstSyncData = RemotePluginClient::getQtVSTshm();
|
||||
#endif
|
||||
m_vstSyncData = RemotePluginClient::getVstSyncData();
|
||||
if( m_vstSyncData == nullptr )
|
||||
{
|
||||
fprintf(stderr, "RemoteVstPlugin.cpp: "
|
||||
"Failed to initialize shared memory for VST synchronization.\n"
|
||||
" (VST-host synchronization will be disabled)\n");
|
||||
m_vstSyncData = (VstSyncData*) malloc( sizeof( VstSyncData ) );
|
||||
m_vstSyncData->isPlaying = true;
|
||||
m_vstSyncData->timeSigNumer = 4;
|
||||
m_vstSyncData->timeSigDenom = 4;
|
||||
m_vstSyncData->ppqPos = 0;
|
||||
m_vstSyncData->isCycle = false;
|
||||
m_vstSyncData->hasSHM = false;
|
||||
m_vstSyncData->m_playbackJumped = false;
|
||||
m_vstSyncData->m_sampleRate = sampleRate();
|
||||
const auto vstSyncData = (VstSyncData*) malloc( sizeof( VstSyncData ) );
|
||||
vstSyncData->isPlaying = true;
|
||||
vstSyncData->timeSigNumer = 4;
|
||||
vstSyncData->timeSigDenom = 4;
|
||||
vstSyncData->ppqPos = 0;
|
||||
vstSyncData->isCycle = false;
|
||||
vstSyncData->hasSHM = false;
|
||||
vstSyncData->m_playbackJumped = false;
|
||||
vstSyncData->m_sampleRate = sampleRate();
|
||||
m_vstSyncData = vstSyncData;
|
||||
}
|
||||
|
||||
m_in = ( in* ) new char[ sizeof( in ) ];
|
||||
@@ -600,21 +564,12 @@ RemoteVstPlugin::~RemoteVstPlugin()
|
||||
destroyEditor();
|
||||
setResumed( false );
|
||||
pluginDispatch( effClose );
|
||||
#ifndef USE_QT_SHMEM
|
||||
// detach shared memory segment
|
||||
if( shmdt( m_vstSyncData ) == -1)
|
||||
|
||||
if (!m_vstSyncData->hasSHM)
|
||||
{
|
||||
if( __plugin->m_vstSyncData->hasSHM )
|
||||
{
|
||||
perror( "~RemoteVstPlugin::shmdt" );
|
||||
}
|
||||
if( m_vstSyncData != nullptr )
|
||||
{
|
||||
delete m_vstSyncData;
|
||||
m_vstSyncData = nullptr;
|
||||
}
|
||||
delete m_vstSyncData;
|
||||
m_vstSyncData = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( m_libInst != nullptr )
|
||||
{
|
||||
@@ -2575,7 +2530,7 @@ int main( int _argc, char * * _argv )
|
||||
// constructor automatically will process messages until it receives
|
||||
// a IdVstLoadPlugin message and processes it
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
__plugin = new RemoteVstPlugin( atoi( _argv[1] ), atoi( _argv[2] ) );
|
||||
__plugin = new RemoteVstPlugin( _argv[1], _argv[2] );
|
||||
#else
|
||||
__plugin = new RemoteVstPlugin( _argv[1] );
|
||||
#endif
|
||||
|
||||
@@ -43,6 +43,10 @@ if(NOT WIN32)
|
||||
target_link_libraries(${EXE_NAME} pthread)
|
||||
endif()
|
||||
|
||||
if(LMMS_HAVE_LIBRT)
|
||||
target_link_libraries(${EXE_NAME} rt)
|
||||
endif()
|
||||
|
||||
target_include_directories(${EXE_NAME}
|
||||
PRIVATE
|
||||
"${LMMS_SOURCE_DIR}/plugins/vst_base/common"
|
||||
|
||||
@@ -185,6 +185,10 @@ ENDIF()
|
||||
TARGET_LINK_LIBRARIES(RemoteZynAddSubFx zynaddsubfx_gui -lZynAddSubFxCore ${FLTK_FILTERED_LDFLAGS} -lpthread )
|
||||
ADD_DEPENDENCIES(RemoteZynAddSubFx ZynAddSubFxCore)
|
||||
|
||||
if(LMMS_HAVE_LIBRT)
|
||||
target_link_libraries(RemoteZynAddSubFx rt)
|
||||
endif()
|
||||
|
||||
# Support qt_version_tag in Qt 5.6
|
||||
TARGET_LINK_LIBRARIES(RemoteZynAddSubFx Qt5::Core)
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class RemoteZynAddSubFx : public RemotePluginClient, public LocalZynAddSubFx
|
||||
{
|
||||
public:
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
RemoteZynAddSubFx( int _shm_in, int _shm_out ) :
|
||||
RemoteZynAddSubFx( const std::string& _shm_in, const std::string& _shm_out ) :
|
||||
RemotePluginClient( _shm_in, _shm_out ),
|
||||
#else
|
||||
RemoteZynAddSubFx( const char * socketPath ) :
|
||||
@@ -282,7 +282,7 @@ int main( int _argc, char * * _argv )
|
||||
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
RemoteZynAddSubFx * remoteZASF =
|
||||
new RemoteZynAddSubFx( atoi( _argv[1] ), atoi( _argv[2] ) );
|
||||
new RemoteZynAddSubFx( _argv[1], _argv[2] );
|
||||
#else
|
||||
RemoteZynAddSubFx * remoteZASF = new RemoteZynAddSubFx( _argv[1] );
|
||||
#endif
|
||||
|
||||
@@ -166,6 +166,10 @@ IF(LMMS_BUILD_HAIKU)
|
||||
SET(EXTRA_LIBRARIES "-lnetwork")
|
||||
ENDIF()
|
||||
|
||||
if(LMMS_HAVE_LIBRT)
|
||||
list(APPEND EXTRA_LIBRARIES "rt")
|
||||
endif()
|
||||
|
||||
SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${QT_LIBRARIES}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
set(COMMON_SRCS
|
||||
RemotePluginBase.cpp
|
||||
SharedMemory.cpp
|
||||
)
|
||||
|
||||
foreach(SRC ${COMMON_SRCS})
|
||||
|
||||
201
src/common/SharedMemory.cpp
Normal file
201
src/common/SharedMemory.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* SharedMemory.cpp
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "SharedMemory.h"
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
#ifdef LMMS_HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if _POSIX_SHARED_MEMORY_OBJECTS > 0
|
||||
# include <system_error>
|
||||
#
|
||||
# include <sys/mman.h>
|
||||
# include <sys/stat.h>
|
||||
# include <fcntl.h>
|
||||
#
|
||||
# include "RaiiHelpers.h"
|
||||
#else
|
||||
# include <stdexcept>
|
||||
#
|
||||
# include <QtGlobal>
|
||||
# include <QSharedMemory>
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
|
||||
#if _POSIX_SHARED_MEMORY_OBJECTS > 0
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename F>
|
||||
int retryWhileInterrupted(F&& function) noexcept(std::is_nothrow_invocable_v<F>)
|
||||
{
|
||||
int result;
|
||||
do
|
||||
{
|
||||
result = function();
|
||||
}
|
||||
while (result == -1 && errno == EINTR);
|
||||
return result;
|
||||
}
|
||||
|
||||
void deleteFileDescriptor(int fd) noexcept { retryWhileInterrupted([fd]() noexcept { return close(fd); }); }
|
||||
void deleteShmObject(const char* name) noexcept { shm_unlink(name); }
|
||||
|
||||
using FileDescriptor = UniqueNullableResource<int, -1, deleteFileDescriptor>;
|
||||
using ShmObject = UniqueNullableResource<const char*, nullptr, deleteShmObject>;
|
||||
|
||||
} // namespace
|
||||
|
||||
class SharedMemoryImpl
|
||||
{
|
||||
public:
|
||||
SharedMemoryImpl(const std::string& key, bool readOnly) :
|
||||
m_key{"/" + key}
|
||||
{
|
||||
const auto openFlags = readOnly ? O_RDONLY : O_RDWR;
|
||||
const auto fd = FileDescriptor{
|
||||
retryWhileInterrupted([&]() noexcept { return shm_open(m_key.c_str(), openFlags, 0); })
|
||||
};
|
||||
if (!fd)
|
||||
{
|
||||
throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: shm_open() failed"};
|
||||
}
|
||||
auto stat = (struct stat){};
|
||||
if (fstat(fd.get(), &stat) == -1)
|
||||
{
|
||||
throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: fstat() failed"};
|
||||
}
|
||||
m_size = stat.st_size;
|
||||
const auto mappingProtection = readOnly ? PROT_READ : PROT_READ | PROT_WRITE;
|
||||
m_mapping = mmap(nullptr, m_size, mappingProtection, MAP_SHARED, fd.get(), 0);
|
||||
if (m_mapping == MAP_FAILED)
|
||||
{
|
||||
throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: mmap() failed"};
|
||||
}
|
||||
}
|
||||
|
||||
SharedMemoryImpl(const std::string& key, std::size_t size, bool readOnly) :
|
||||
m_key{"/" + key},
|
||||
m_size{size}
|
||||
{
|
||||
const auto fd = FileDescriptor{
|
||||
retryWhileInterrupted([&]() noexcept { return shm_open(m_key.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); })
|
||||
};
|
||||
if (fd.get() == -1)
|
||||
{
|
||||
throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: shm_open() failed"};
|
||||
}
|
||||
m_object.reset(m_key.c_str());
|
||||
if (retryWhileInterrupted([&]() noexcept { return ftruncate(fd.get(), m_size); }) == -1)
|
||||
{
|
||||
throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: ftruncate() failed"};
|
||||
}
|
||||
const auto mappingProtection = readOnly ? PROT_READ : PROT_READ | PROT_WRITE;
|
||||
m_mapping = mmap(nullptr, m_size, mappingProtection, MAP_SHARED, fd.get(), 0);
|
||||
if (m_mapping == MAP_FAILED)
|
||||
{
|
||||
throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: mmap() failed"};
|
||||
}
|
||||
}
|
||||
|
||||
SharedMemoryImpl(const SharedMemoryImpl&) = delete;
|
||||
SharedMemoryImpl& operator=(const SharedMemoryImpl&) = delete;
|
||||
|
||||
~SharedMemoryImpl()
|
||||
{
|
||||
munmap(m_mapping, m_size);
|
||||
}
|
||||
|
||||
void* get() { return m_mapping; }
|
||||
|
||||
private:
|
||||
std::string m_key;
|
||||
std::size_t m_size;
|
||||
void* m_mapping;
|
||||
ShmObject m_object;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class SharedMemoryImpl
|
||||
{
|
||||
public:
|
||||
SharedMemoryImpl(const std::string& key, bool readOnly) :
|
||||
m_shm{QString::fromStdString(key)}
|
||||
{
|
||||
const auto mode = readOnly ? QSharedMemory::ReadOnly : QSharedMemory::ReadWrite;
|
||||
if (!m_shm.attach(mode))
|
||||
{
|
||||
throw std::runtime_error{"SharedMemoryImpl: QSharedMemory::attach() failed"};
|
||||
}
|
||||
}
|
||||
|
||||
SharedMemoryImpl(const std::string& key, std::size_t size, bool readOnly) :
|
||||
m_shm{QString::fromStdString(key)}
|
||||
{
|
||||
const auto mode = readOnly ? QSharedMemory::ReadOnly : QSharedMemory::ReadWrite;
|
||||
if (!m_shm.create(size, mode))
|
||||
{
|
||||
throw std::runtime_error{"SharedMemoryImpl: QSharedMemory::create() failed"};
|
||||
}
|
||||
}
|
||||
|
||||
SharedMemoryImpl(const SharedMemoryImpl&) = delete;
|
||||
SharedMemoryImpl& operator=(const SharedMemoryImpl&) = delete;
|
||||
|
||||
void* get() { return m_shm.data(); }
|
||||
|
||||
private:
|
||||
QSharedMemory m_shm;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
SharedMemoryData::SharedMemoryData() noexcept
|
||||
{ }
|
||||
|
||||
SharedMemoryData::SharedMemoryData(std::string&& key, bool readOnly) :
|
||||
m_key{std::move(key)},
|
||||
m_impl{std::make_unique<SharedMemoryImpl>(m_key, readOnly)},
|
||||
m_ptr{m_impl->get()}
|
||||
{ }
|
||||
|
||||
SharedMemoryData::SharedMemoryData(std::string&& key, std::size_t size, bool readOnly) :
|
||||
m_key{std::move(key)},
|
||||
m_impl{std::make_unique<SharedMemoryImpl>(m_key, std::max(size, std::size_t{1}), readOnly)},
|
||||
m_ptr{m_impl->get()}
|
||||
{ }
|
||||
|
||||
SharedMemoryData::~SharedMemoryData() { }
|
||||
|
||||
SharedMemoryData::SharedMemoryData(SharedMemoryData&& other) noexcept :
|
||||
m_key{std::move(other.m_key)},
|
||||
m_impl{std::move(other.m_impl)},
|
||||
m_ptr{other.m_ptr}
|
||||
{ }
|
||||
|
||||
} // namespace detail
|
||||
@@ -40,9 +40,9 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QUuid>
|
||||
|
||||
#ifndef SYNC_WITH_SHM_FIFO
|
||||
#include <QUuid>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
@@ -137,13 +137,7 @@ RemotePlugin::RemotePlugin() :
|
||||
m_watcher( this ),
|
||||
m_commMutex( QMutex::Recursive ),
|
||||
m_splitChannels( false ),
|
||||
#ifdef USE_QT_SHMEM
|
||||
m_shmObj(),
|
||||
#else
|
||||
m_shmID( 0 ),
|
||||
#endif
|
||||
m_shmSize( 0 ),
|
||||
m_shm( nullptr ),
|
||||
m_audioBufferSize( 0 ),
|
||||
m_inputCount( DEFAULT_CHANNELS ),
|
||||
m_outputCount( DEFAULT_CHANNELS )
|
||||
{
|
||||
@@ -209,11 +203,6 @@ RemotePlugin::~RemotePlugin()
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
#ifndef USE_QT_SHMEM
|
||||
shmdt( m_shm );
|
||||
shmctl( m_shmID, IPC_RMID, nullptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SYNC_WITH_SHM_FIFO
|
||||
@@ -273,8 +262,8 @@ bool RemotePlugin::init(const QString &pluginExecutable,
|
||||
QStringList args;
|
||||
#ifdef SYNC_WITH_SHM_FIFO
|
||||
// swap in and out for bidirectional communication
|
||||
args << QString::number( out()->shmKey() );
|
||||
args << QString::number( in()->shmKey() );
|
||||
args << QString::fromStdString(out()->shmKey());
|
||||
args << QString::fromStdString(in()->shmKey());
|
||||
#else
|
||||
args << m_socketFile;
|
||||
#endif
|
||||
@@ -342,13 +331,13 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_shm == nullptr )
|
||||
if (!m_audioBuffer)
|
||||
{
|
||||
// m_shm being zero means we didn't initialize everything so
|
||||
// m_audioBuffer being zero means we didn't initialize everything so
|
||||
// far so process one message each time (and hope we get
|
||||
// information like SHM-key etc.) until we process messages
|
||||
// in a later stage of this procedure
|
||||
if( m_shmSize == 0 )
|
||||
if( m_audioBufferSize == 0 )
|
||||
{
|
||||
lock();
|
||||
fetchAndProcessAllMessages();
|
||||
@@ -361,7 +350,7 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf
|
||||
return false;
|
||||
}
|
||||
|
||||
memset( m_shm, 0, m_shmSize );
|
||||
memset( m_audioBuffer.get(), 0, m_audioBufferSize );
|
||||
|
||||
ch_cnt_t inputs = qMin<ch_cnt_t>( m_inputCount, DEFAULT_CHANNELS );
|
||||
|
||||
@@ -373,18 +362,18 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf
|
||||
{
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
{
|
||||
m_shm[ch * frames + frame] =
|
||||
m_audioBuffer[ch * frames + frame] =
|
||||
_in_buf[frame][ch];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( inputs == DEFAULT_CHANNELS )
|
||||
{
|
||||
memcpy( m_shm, _in_buf, frames * BYTES_PER_FRAME );
|
||||
memcpy( m_audioBuffer.get(), _in_buf, frames * BYTES_PER_FRAME );
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleFrame * o = (sampleFrame *) m_shm;
|
||||
sampleFrame * o = (sampleFrame *) m_audioBuffer.get();
|
||||
for( ch_cnt_t ch = 0; ch < inputs; ++ch )
|
||||
{
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
@@ -415,19 +404,19 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf
|
||||
{
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
{
|
||||
_out_buf[frame][ch] = m_shm[( m_inputCount+ch )*
|
||||
_out_buf[frame][ch] = m_audioBuffer[( m_inputCount+ch )*
|
||||
frames + frame];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( outputs == DEFAULT_CHANNELS )
|
||||
{
|
||||
memcpy( _out_buf, m_shm + m_inputCount * frames,
|
||||
memcpy( _out_buf, m_audioBuffer.get() + m_inputCount * frames,
|
||||
frames * BYTES_PER_FRAME );
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleFrame * o = (sampleFrame *) ( m_shm +
|
||||
sampleFrame * o = (sampleFrame *) ( m_audioBuffer.get() +
|
||||
m_inputCount*frames );
|
||||
// clear buffer, if plugin didn't fill up both channels
|
||||
BufferManager::clear( _out_buf, frames );
|
||||
@@ -481,37 +470,19 @@ void RemotePlugin::hideUI()
|
||||
|
||||
void RemotePlugin::resizeSharedProcessingMemory()
|
||||
{
|
||||
const size_t s = ( m_inputCount+m_outputCount ) * Engine::audioEngine()->framesPerPeriod() * sizeof( float );
|
||||
if( m_shm != nullptr )
|
||||
const size_t s = (m_inputCount + m_outputCount) * Engine::audioEngine()->framesPerPeriod();
|
||||
try
|
||||
{
|
||||
#ifdef USE_QT_SHMEM
|
||||
m_shmObj.detach();
|
||||
#else
|
||||
shmdt( m_shm );
|
||||
shmctl( m_shmID, IPC_RMID, nullptr );
|
||||
#endif
|
||||
m_audioBuffer.create(QUuid::createUuid().toString().toStdString(), s);
|
||||
}
|
||||
|
||||
static int shm_key = 0;
|
||||
#ifdef USE_QT_SHMEM
|
||||
do
|
||||
{
|
||||
m_shmObj.setKey( QString( "%1" ).arg( ++shm_key ) );
|
||||
m_shmObj.create( s );
|
||||
} while( m_shmObj.error() != QSharedMemory::NoError );
|
||||
|
||||
m_shm = (float *) m_shmObj.data();
|
||||
#else
|
||||
while( ( m_shmID = shmget( ++shm_key, s, IPC_CREAT | IPC_EXCL |
|
||||
0600 ) ) == -1 )
|
||||
catch (const std::runtime_error& error)
|
||||
{
|
||||
qCritical() << "Failed to allocate shared audio buffer:" << error.what();
|
||||
m_audioBuffer.detach();
|
||||
return;
|
||||
}
|
||||
|
||||
m_shm = (float *) shmat( m_shmID, 0, 0 );
|
||||
#endif
|
||||
m_shmSize = s;
|
||||
sendMessage( message( IdChangeSharedMemoryKey ).
|
||||
addInt( shm_key ).addInt( m_shmSize ) );
|
||||
m_audioBufferSize = s * sizeof(float);
|
||||
sendMessage(message(IdChangeSharedMemoryKey).addString(m_audioBuffer.key()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "VstSyncController.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "AudioEngine.h"
|
||||
@@ -32,53 +34,23 @@
|
||||
#include "Engine.h"
|
||||
#include "RemotePlugin.h"
|
||||
|
||||
#ifndef USE_QT_SHMEM
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
|
||||
VstSyncController::VstSyncController() :
|
||||
m_syncData( nullptr ),
|
||||
m_shmID( -1 ),
|
||||
m_shm( "/usr/bin/lmms" )
|
||||
m_syncData( nullptr )
|
||||
{
|
||||
if( ConfigManager::inst()->value( "ui", "syncvstplugins" ).toInt() )
|
||||
{
|
||||
connect( Engine::audioEngine(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) );
|
||||
|
||||
#ifdef USE_QT_SHMEM
|
||||
if ( m_shm.create( sizeof( VstSyncData ) ) )
|
||||
try
|
||||
{
|
||||
m_syncData = (VstSyncData*) m_shm.data();
|
||||
m_shm.create("usr_bin_lmms");
|
||||
m_syncData = m_shm.get();
|
||||
}
|
||||
else
|
||||
catch (const std::runtime_error& error)
|
||||
{
|
||||
qWarning() << QString( "Failed to allocate shared memory for VST sync: %1" ).arg( m_shm.errorString() );
|
||||
qWarning() << "Failed to allocate shared memory for VST sync:" << error.what();
|
||||
}
|
||||
#else
|
||||
key_t key; // make the key:
|
||||
if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
|
||||
{
|
||||
qWarning( "VstSyncController: ftok() failed" );
|
||||
}
|
||||
else
|
||||
{ // connect to shared memory segment
|
||||
if( ( m_shmID = shmget( key, sizeof( VstSyncData ), 0644 | IPC_CREAT ) ) == -1 )
|
||||
{
|
||||
qWarning( "VstSyncController: shmget() failed" );
|
||||
}
|
||||
else
|
||||
{ // attach segment
|
||||
m_syncData = (VstSyncData *)shmat( m_shmID, 0, 0 );
|
||||
if( m_syncData == (VstSyncData *)( -1 ) )
|
||||
{
|
||||
qWarning( "VstSyncController: shmat() failed" );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -111,25 +83,6 @@ VstSyncController::~VstSyncController()
|
||||
{
|
||||
delete m_syncData;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef USE_QT_SHMEM
|
||||
if( m_shm.data() )
|
||||
{
|
||||
// detach shared memory, delete it:
|
||||
m_shm.detach();
|
||||
}
|
||||
#else
|
||||
if( shmdt( m_syncData ) != -1 )
|
||||
{
|
||||
shmctl( m_shmID, IPC_RMID, nullptr );
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning( "VstSyncController: shmdt() failed" );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#cmakedefine LMMS_HAVE_SYS_TYPES_H
|
||||
#cmakedefine LMMS_HAVE_SYS_IPC_H
|
||||
#cmakedefine LMMS_HAVE_SEMAPHORE_H
|
||||
#cmakedefine LMMS_HAVE_SYS_SHM_H
|
||||
#cmakedefine LMMS_HAVE_SYS_TIME_H
|
||||
#cmakedefine LMMS_HAVE_SYS_TIMES_H
|
||||
#cmakedefine LMMS_HAVE_SCHED_H
|
||||
|
||||
Reference in New Issue
Block a user