From 9263499a1707548d73d2efd2704133e944006009 Mon Sep 17 00:00:00 2001 From: Anton Kalmykov Date: Thu, 16 Apr 2015 01:55:29 +0300 Subject: [PATCH 1/7] Replace semaphores by futex-driven Events --- src/common/dataport.cpp | 63 ++++++------------------------------ src/common/dataport.h | 9 +++--- src/common/event.cpp | 51 ++++++++++++++++++++++++++++++ src/common/event.h | 25 +++++++++++++++ src/common/eventsignal.cpp | 65 -------------------------------------- src/common/eventsignal.h | 26 --------------- src/host/CMakeLists.txt | 2 +- src/host/host.h | 4 +-- src/plugin/CMakeLists.txt | 2 +- src/plugin/plugin.h | 4 +-- 10 files changed, 96 insertions(+), 155 deletions(-) create mode 100644 src/common/event.cpp create mode 100644 src/common/event.h delete mode 100644 src/common/eventsignal.cpp delete mode 100644 src/common/eventsignal.h diff --git a/src/common/dataport.cpp b/src/common/dataport.cpp index 1928e5a..fdb1ef8 100644 --- a/src/common/dataport.cpp +++ b/src/common/dataport.cpp @@ -47,9 +47,7 @@ bool DataPort::create(size_t frameSize) return false; } - ControlBlock* control = controlBlock(); - sem_init(&control->request, 1, 0); - sem_init(&control->response, 1, 0); + new (controlBlock()) ControlBlock; frameSize_ = frameSize; return true; @@ -89,9 +87,9 @@ void DataPort::disconnect() { if(!isNull()) { if(!isConnected()) { - ControlBlock* control = controlBlock(); - sem_destroy(&control->request); - sem_destroy(&control->response); +// ControlBlock* control = controlBlock(); +// sem_destroy(&control->request); +// sem_destroy(&control->response); } shmdt(buffer_); @@ -142,31 +140,27 @@ void* DataPort::frameBuffer() void DataPort::sendRequest() { - if(!isNull()) { - if(sem_post(&controlBlock()->request) != 0) - ERROR("sem_post() call failed: %s", strerror(errno)); - } + if(!isNull()) + controlBlock()->request.post(); } void DataPort::sendResponse() { - if(!isNull()) { - if(sem_post(&controlBlock()->response) != 0) - ERROR("sem_post() call failed: %s", strerror(errno)); - } + if(!isNull()) + controlBlock()->response.post(); } bool DataPort::waitRequest(int msecs) { - return waitForReady(&controlBlock()->request, msecs); + return controlBlock()->request.wait(msecs); } bool DataPort::waitResponse(int msecs) { - return waitForReady(&controlBlock()->response, msecs); + return controlBlock()->response.wait(msecs); } @@ -176,41 +170,4 @@ DataPort::ControlBlock* DataPort::controlBlock() } -bool DataPort::waitForReady(sem_t* semaphore, int msecs) -{ - if(isNull()) - return false; - - if(msecs < 0) { - if(sem_wait(semaphore) != 0) { - ERROR("sem_wait() call failed: %d", errno); - return false; - } - } - else { - int seconds = msecs / 1000; - msecs %= 1000; - - timespec tm; - clock_gettime(CLOCK_REALTIME, &tm); - tm.tv_sec += seconds; - tm.tv_nsec += msecs * 1000000; - - if(tm.tv_nsec >= 1000000000L) { - tm.tv_sec++; - tm.tv_nsec -= 1000000000L; - } - - if(sem_timedwait(semaphore, &tm) != 0) { - if(errno != ETIMEDOUT) - ERROR("sem_timedwait() call failed: %d", errno); - - return false; - } - } - - return true; -} - - } // namespace Airwave diff --git a/src/common/dataport.h b/src/common/dataport.h index 17ffb51..0c3671c 100644 --- a/src/common/dataport.h +++ b/src/common/dataport.h @@ -1,7 +1,7 @@ #ifndef COMMON_DATAPORT_H #define COMMON_DATAPORT_H -#include +#include "common/event.h" #include "common/types.h" @@ -35,16 +35,15 @@ class DataPort { private: struct ControlBlock { - sem_t request; - sem_t response; - } __attribute__((packed)); + Event request; + Event response; + }; int id_; size_t frameSize_; void* buffer_; ControlBlock* controlBlock(); - bool waitForReady(sem_t* semaphore, int msecs = -1); }; diff --git a/src/common/event.cpp b/src/common/event.cpp new file mode 100644 index 0000000..419f213 --- /dev/null +++ b/src/common/event.cpp @@ -0,0 +1,51 @@ +#include "event.h" + +#include +#include +#include +#include + + +#define futex_wait(count, timeout) \ + (syscall(SYS_futex, count, FUTEX_WAIT, 0, timeout, nullptr, 0) == 0) + +#define futex_post(count) \ + (syscall(SYS_futex, count, FUTEX_WAKE, 1, nullptr, nullptr, 0) == 0) + + +Event::Event() : + count_(0) +{ +} + + +bool Event::wait(int msecs) +{ + bool result; + + if(msecs < 0) { + result = futex_wait(&count_, nullptr); + } + else { + int seconds = msecs / 1000; + msecs %= 1000; + + timespec tm; + tm.tv_sec = seconds; + tm.tv_nsec = msecs * 1000000; + + result = futex_wait(&count_, &tm); + } + + if(result) + count_--; + + return result; +} + + +void Event::post() +{ + count_++; + futex_post(&count_); +} diff --git a/src/common/event.h b/src/common/event.h new file mode 100644 index 0000000..9848cd4 --- /dev/null +++ b/src/common/event.h @@ -0,0 +1,25 @@ +#ifndef FUTEX_TEST_EVENT_H +#define FUTEX_TEST_EVENT_H + +#include + +#ifdef bool +#undef bool +#endif + + +class Event { +public: + static const int kInfinite = -1; + + Event(); + + bool wait(int msecs = kInfinite); + void post(); + +//private: + std::atomic count_; +}; + + +#endif // FUTEX_TEST_EVENT_H diff --git a/src/common/eventsignal.cpp b/src/common/eventsignal.cpp deleted file mode 100644 index 10a629a..0000000 --- a/src/common/eventsignal.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "eventsignal.h" - -#include -#include -#include -#include "common/logger.h" - - -namespace Airwave { - - -EventSignal::EventSignal() -{ - sem_init(&sem_, 1, 0); -} - - -EventSignal::~EventSignal() -{ - sem_destroy(&sem_); -} - - -bool EventSignal::wait(int msecs) -{ - if(msecs < 0) { - if(sem_wait(&sem_) != 0) { - ERROR("sem_wait() call failed: %d", errno); - return false; - } - } - else { - int seconds = msecs / 1000; - msecs %= 1000; - - timespec tm; - clock_gettime(CLOCK_REALTIME, &tm); - tm.tv_sec += seconds; - tm.tv_nsec += msecs * 1000000; - - if(tm.tv_nsec >= 1000000000L) { - tm.tv_sec++; - tm.tv_nsec -= 1000000000L; - } - - if(sem_timedwait(&sem_, &tm) != 0) { - if(errno != ETIMEDOUT) - ERROR("sem_timedwait() call failed: %d", errno); - - return false; - } - } - - return true; -} - - -void EventSignal::post() -{ - if(sem_post(&sem_) != 0) - ERROR("sem_post() call failed: %s", strerror(errno)); -} - - -} // namespace Airwave diff --git a/src/common/eventsignal.h b/src/common/eventsignal.h deleted file mode 100644 index d458e24..0000000 --- a/src/common/eventsignal.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef COMMON_EVENTSIGNAL_H -#define COMMON_EVENTSIGNAL_H - -#include - - -namespace Airwave { - - -class EventSignal { -public: - EventSignal(); - ~EventSignal(); - - bool wait(int msecs = -1); - void post(); - -private: - sem_t sem_; -}; - - -} // namespace Airwave - - -#endif // COMMON_EVENTSIGNAL_H diff --git a/src/host/CMakeLists.txt b/src/host/CMakeLists.txt index 06ad1ac..bc85ced 100644 --- a/src/host/CMakeLists.txt +++ b/src/host/CMakeLists.txt @@ -28,7 +28,7 @@ endif() # Sources set(SOURCES ../common/dataport.cpp - ../common/eventsignal.cpp + ../common/event.cpp ../common/filesystem.cpp ../common/logger.cpp ../common/vsteventkeeper.cpp diff --git a/src/host/host.h b/src/host/host.h index 00e671b..721d4a9 100644 --- a/src/host/host.h +++ b/src/host/host.h @@ -7,7 +7,7 @@ #include #include "common/config.h" #include "common/dataport.h" -#include "common/eventsignal.h" +#include "common/event.h" #include "common/vst24.h" #include "common/vsteventkeeper.h" @@ -44,7 +44,7 @@ class Host { DataPort callbackPort_; DataPort audioPort_; - EventSignal condition_; + Event condition_; HANDLE audioThread_; std::atomic_flag runAudio_; diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt index 6ddad2d..aabd00b 100644 --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -27,7 +27,7 @@ set(SOURCES main.cpp plugin.cpp ../common/dataport.cpp - ../common/eventsignal.cpp + ../common/event.cpp ../common/filesystem.cpp ../common/json.cpp ../common/logger.cpp diff --git a/src/plugin/plugin.h b/src/plugin/plugin.h index 5ef048f..e7f59d7 100644 --- a/src/plugin/plugin.h +++ b/src/plugin/plugin.h @@ -8,7 +8,7 @@ #include #include #include "common/dataport.h" -#include "common/eventsignal.h" +#include "common/event.h" #include "common/vst24.h" #include "common/vsteventkeeper.h" @@ -47,7 +47,7 @@ class Plugin { DataPort callbackPort_; DataPort audioPort_; - EventSignal condition_; + Event condition_; int childPid_; From bc5687d3d2d45fd4eadc6c6b6d5078e7119f1ff6 Mon Sep 17 00:00:00 2001 From: Anton Kalmykov Date: Thu, 16 Apr 2015 22:00:44 +0300 Subject: [PATCH 2/7] Fix the Event implementation --- src/common/event.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/common/event.cpp b/src/common/event.cpp index 419f213..db3b313 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -1,16 +1,17 @@ #include "event.h" +#include #include #include #include #include -#define futex_wait(count, timeout) \ - (syscall(SYS_futex, count, FUTEX_WAIT, 0, timeout, nullptr, 0) == 0) +#define futex_wait(futex, count, timeout) \ + (syscall(SYS_futex, futex, FUTEX_WAIT, count, timeout, nullptr, 0) == 0) -#define futex_post(count) \ - (syscall(SYS_futex, count, FUTEX_WAKE, 1, nullptr, nullptr, 0) == 0) +#define futex_post(futex, count) \ + (syscall(SYS_futex, futex, FUTEX_WAKE, count, nullptr, nullptr, 0) == 0) Event::Event() : @@ -21,31 +22,35 @@ Event::Event() : bool Event::wait(int msecs) { - bool result; + timespec* timeout = nullptr; + timespec tm; - if(msecs < 0) { - result = futex_wait(&count_, nullptr); - } - else { + if(msecs >= 0) { int seconds = msecs / 1000; msecs %= 1000; - timespec tm; tm.tv_sec = seconds; tm.tv_nsec = msecs * 1000000; - result = futex_wait(&count_, &tm); + timeout = &tm; } - if(result) - count_--; + while(count_ == 0) { + if(!futex_wait(&count_, 0, timeout)) { + if(errno == EWOULDBLOCK) + continue; + + return false; + } + } - return result; + count_--; + return true; } void Event::post() { count_++; - futex_post(&count_); + futex_post(&count_, 1); } From b88dab744c94ff841d07ab7b3aacf8332ec42315 Mon Sep 17 00:00:00 2001 From: Anton Kalmykov Date: Fri, 17 Apr 2015 01:45:23 +0300 Subject: [PATCH 3/7] Switch to the single-arch plugin mode --- src/common/event.cpp | 6 +--- src/common/event.h | 2 +- src/common/moduleinfo.cpp | 52 ++++++++++++++++++++++++++++ src/common/moduleinfo.h | 33 ++++++++++++++++++ src/common/protocol.h | 19 ++++++---- src/common/types.h | 1 - src/common/vst24.h | 2 +- src/manager/CMakeLists.txt | 1 + src/manager/core/application.cpp | 3 +- src/manager/forms/linkdialog.cpp | 18 ++-------- src/manager/forms/linkdialog.h | 2 +- src/manager/forms/mainform.cpp | 22 +----------- src/manager/forms/settingsdialog.cpp | 2 +- src/plugin/CMakeLists.txt | 33 +++--------------- src/plugin/main.cpp | 41 +++++++++++++--------- 15 files changed, 138 insertions(+), 99 deletions(-) create mode 100644 src/common/moduleinfo.cpp create mode 100644 src/common/moduleinfo.h diff --git a/src/common/event.cpp b/src/common/event.cpp index db3b313..5027165 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -36,12 +36,8 @@ bool Event::wait(int msecs) } while(count_ == 0) { - if(!futex_wait(&count_, 0, timeout)) { - if(errno == EWOULDBLOCK) - continue; - + if(!futex_wait(&count_, 0, timeout) && errno != EWOULDBLOCK) return false; - } } count_--; diff --git a/src/common/event.h b/src/common/event.h index 9848cd4..dfc5d76 100644 --- a/src/common/event.h +++ b/src/common/event.h @@ -17,7 +17,7 @@ class Event { bool wait(int msecs = kInfinite); void post(); -//private: +private: std::atomic count_; }; diff --git a/src/common/moduleinfo.cpp b/src/common/moduleinfo.cpp new file mode 100644 index 0000000..f630978 --- /dev/null +++ b/src/common/moduleinfo.cpp @@ -0,0 +1,52 @@ +#include "moduleinfo.h" + + +ModuleInfo::ModuleInfo() : + isInitialized_(false) +{ + magic_ = magic_open(MAGIC_NONE); + if(!magic_) { + return; + } + + if(magic_load(magic_, nullptr) != 0) { + magic_close(magic_); + return; + } + + isInitialized_ = true; +} + + +ModuleInfo::~ModuleInfo() +{ + magic_close(magic_); +} + + +ModuleInfo* ModuleInfo::instance() +{ + static ModuleInfo info; + return &info; +} + + +ModuleInfo::Arch ModuleInfo::getArch(const std::string& fileName) const +{ + if(isInitialized_) { + const char* buffer = magic_file(magic_, fileName.c_str()); + + if(buffer) { + std::string string = buffer; + + if(string.find("80386") != std::string::npos) { + return kArch32; + } + else if(string.find("x86-64") != std::string::npos) { + return kArch64; + } + } + } + + return kArchUnknown; +} diff --git a/src/common/moduleinfo.h b/src/common/moduleinfo.h new file mode 100644 index 0000000..47249b1 --- /dev/null +++ b/src/common/moduleinfo.h @@ -0,0 +1,33 @@ +#ifndef COMMON_MODULEINFO_H +#define COMMON_MODULEINFO_H + +#include +#include + + +class ModuleInfo { +public: + enum Arch { + kArchUnknown = 0, + kArch32 = 32, + kArch64 = 64 + }; + + static ModuleInfo* instance(); + + ModuleInfo(const ModuleInfo&) = delete; + ModuleInfo& operator=(const ModuleInfo&) = delete; + + ~ModuleInfo(); + + Arch getArch(const std::string& fileName) const; + +private: + bool isInitialized_; + magic_t magic_; + + ModuleInfo(); +}; + + +#endif // COMMON_MODULEINFO_H diff --git a/src/common/protocol.h b/src/common/protocol.h index 97082db..4251f48 100644 --- a/src/common/protocol.h +++ b/src/common/protocol.h @@ -24,12 +24,19 @@ enum class Command { struct DataFrame { - Command command; - i32 opcode; - i32 index; - intptr value; - float opt; - u8 data[]; + Command command; + i32 opcode; + i32 index; + intptr_t value; + float opt; + +#ifdef __i386 + // The size of a pointer on the x86_64 in two times larger than on the x86. Because + // of this, we should use the 32 bits of padding on the x86. + void* padding; +#endif + + u8 data[]; } __attribute__((packed)); diff --git a/src/common/types.h b/src/common/types.h index 83e369f..49fe755 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -35,7 +35,6 @@ using i64 = int64_t; using u64 = uint64_t; using f32 = float; using f64 = double; -using intptr = intptr_t; template diff --git a/src/common/vst24.h b/src/common/vst24.h index 93b6650..48ecd91 100644 --- a/src/common/vst24.h +++ b/src/common/vst24.h @@ -9,7 +9,7 @@ namespace Airwave { -//using AudioMasterProc = intptr (VSTCALLBACK*)(AEffect*, i32, i32, intptr, void*, float); +//using AudioMasterProc = intptr_t (VSTCALLBACK*)(AEffect*, i32, i32, intptr_t, void*, float); //using VstPluginMainProc = AEffect* (VSTCALLBACK*)(AudioMasterProc); typedef intptr_t (VSTCALLBACK *AudioMasterProc)(AEffect*, i32, i32, intptr_t, void*, diff --git a/src/manager/CMakeLists.txt b/src/manager/CMakeLists.txt index fed4fb8..ad6ed38 100644 --- a/src/manager/CMakeLists.txt +++ b/src/manager/CMakeLists.txt @@ -19,6 +19,7 @@ set(SOURCES main.cpp ../common/filesystem.cpp ../common/json.cpp +# ../common/moduleinfo.cpp ../common/storage.cpp core/application.cpp core/logsocket.cpp diff --git a/src/manager/core/application.cpp b/src/manager/core/application.cpp index 003be65..8c81256 100644 --- a/src/manager/core/application.cpp +++ b/src/manager/core/application.cpp @@ -67,10 +67,9 @@ QStringList Application::checkMissingBinaries(const QString& path) const QStringList fileList; fileList += HOST_BASENAME "-32.exe"; - fileList += PLUGIN_BASENAME "-32.so"; + fileList += PLUGIN_BASENAME ".so"; #ifdef PLATFORM_64BIT fileList += HOST_BASENAME "-64.exe"; - fileList += PLUGIN_BASENAME "-64.so"; #endif QStringList result; diff --git a/src/manager/forms/linkdialog.cpp b/src/manager/forms/linkdialog.cpp index 63378ce..b6f6a00 100644 --- a/src/manager/forms/linkdialog.cpp +++ b/src/manager/forms/linkdialog.cpp @@ -237,7 +237,7 @@ void LinkDialog::accept() return; } - QString pluginPath = pluginPathFor(vstInfo.absoluteFilePath()); + QString pluginPath = getPluginPath(); if(pluginPath.isEmpty()) { QMessageBox::critical(this, "Error", "VST plugin is corrupted."); return; @@ -328,20 +328,8 @@ QString LinkDialog::currentPrefix() const } -QString LinkDialog::pluginPathFor(const QString& vstPath) const +QString LinkDialog::getPluginPath() const { QString pluginPath = QString::fromStdString(qApp->storage()->binariesPath()); - pluginPath += "/" PLUGIN_BASENAME; - - ModuleInfo* moduleInfo = ModuleInfo::instance(); - ModuleInfo::Arch arch = moduleInfo->getArch(vstPath); - - if(arch == ModuleInfo::kArch32) { - return pluginPath + "-32.so"; - } - else if(arch == ModuleInfo::kArch64) { - return pluginPath + "-64.so"; - } - - return QString(); + return pluginPath + "/" PLUGIN_BASENAME ".so"; } diff --git a/src/manager/forms/linkdialog.h b/src/manager/forms/linkdialog.h index 14ef5b1..e3d93be 100644 --- a/src/manager/forms/linkdialog.h +++ b/src/manager/forms/linkdialog.h @@ -30,7 +30,7 @@ class LinkDialog : public QDialog { void setupUi(); QString currentPrefix() const; - QString pluginPathFor(const QString& vstPath) const; + QString getPluginPath() const; private slots: void browsePlugin(); diff --git a/src/manager/forms/mainform.cpp b/src/manager/forms/mainform.cpp index 83a4f93..92f9090 100644 --- a/src/manager/forms/mainform.cpp +++ b/src/manager/forms/mainform.cpp @@ -321,31 +321,11 @@ void MainForm::updateLinks() LinkItem* item = qApp->links()->root()->firstChild(); while(item) { - auto prefix = qApp->storage()->prefix(item->prefix().toStdString()); - if(!prefix) - continue; - - QString prefixPath = QString::fromStdString(prefix.path()); - QFileInfo vstInfo(prefixPath + '/' + item->target()); - - ModuleInfo* moduleInfo = ModuleInfo::instance(); - ModuleInfo::Arch arch = moduleInfo->getArch(vstInfo.absoluteFilePath()); - - QString pluginName; - if(arch == ModuleInfo::kArch32) { - pluginName = PLUGIN_BASENAME "-32.so"; - } - else if(arch == ModuleInfo::kArch64) { - pluginName = PLUGIN_BASENAME "-64.so"; - } - else { - continue; - } - QFileInfo linkInfo(item->path()); QDir dir(linkInfo.absoluteDir()); dir.remove(linkInfo.fileName()); + QString pluginName = PLUGIN_BASENAME ".so"; QFile::copy(pluginPath + '/' + pluginName, linkInfo.absoluteFilePath()); item = item->nextSibling(); diff --git a/src/manager/forms/settingsdialog.cpp b/src/manager/forms/settingsdialog.cpp index 13de096..8c733ee 100644 --- a/src/manager/forms/settingsdialog.cpp +++ b/src/manager/forms/settingsdialog.cpp @@ -189,7 +189,7 @@ void SettingsDialog::browseForBinariesPath() QStringList nameFilters; nameFilters << HOST_BASENAME "-32.exe" << HOST_BASENAME "-64.exe"; - nameFilters << PLUGIN_BASENAME "-32.so" << PLUGIN_BASENAME "-64.so"; + nameFilters << PLUGIN_BASENAME ".so"; dialog.setNameFilters(nameFilters); dialog.setWindowTitle("Select directory containing airwave binaries"); diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt index aabd00b..445adf2 100644 --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -31,6 +31,7 @@ set(SOURCES ../common/filesystem.cpp ../common/json.cpp ../common/logger.cpp + ../common/moduleinfo.cpp ../common/storage.cpp ../common/vsteventkeeper.cpp ) @@ -44,39 +45,15 @@ set(HEADERS # Configure library base name set(CMAKE_SHARED_LIBRARY_PREFIX "") -if(PLATFORM_64BIT) - # Set target - add_library(${TARGET_NAME}-64 SHARED ${SOURCES} ${HEADERS}) - - set_target_properties(${TARGET_NAME}-64 PROPERTIES - COMPILE_FLAGS "-m64" - LINK_FLAGS "-m64" - ) - - # Link with libraries - target_link_libraries(${TARGET_NAME}-64 - ${LIBDL_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} - ${X11_X11_LIB} - ) - - install(TARGETS ${TARGET_NAME}-64 LIBRARY DESTINATION bin) -endif() - - # Set target -add_library(${TARGET_NAME}-32 SHARED ${SOURCES}) - -set_target_properties(${TARGET_NAME}-32 PROPERTIES - COMPILE_FLAGS "-m32" - LINK_FLAGS "-m32" -) +add_library(${TARGET_NAME} SHARED ${SOURCES}) # Link with libraries -target_link_libraries(${TARGET_NAME}-32 +target_link_libraries(${TARGET_NAME} ${LIBDL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${X11_X11_LIB} + ${LIBMAGIC_LIBRARY} ) -install(TARGETS ${TARGET_NAME}-32 LIBRARY DESTINATION bin) +install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION bin) diff --git a/src/plugin/main.cpp b/src/plugin/main.cpp index e25b4f2..a65804d 100644 --- a/src/plugin/main.cpp +++ b/src/plugin/main.cpp @@ -5,6 +5,7 @@ #include "common/config.h" #include "common/filesystem.h" #include "common/logger.h" +#include "common/moduleinfo.h" #include "common/storage.h" @@ -69,23 +70,6 @@ AEffect* VSTPluginMain(AudioMasterProc audioMasterProc) TRACE("Initializing plugin endpoint %s", VERSION_STRING); TRACE("Plugin binary: %s", selfPath.c_str()); - // Find host binary path - std::string hostName; - if(sizeof(void*) == 8) { - hostName = HOST_BASENAME "-64.exe"; - } - else { - hostName = HOST_BASENAME "-32.exe"; - } - - std::string hostPath = FileSystem::realPath(storage.binariesPath() + '/' + hostName); - if(!FileSystem::isFileExists(hostPath)) { - ERROR("Host binary '%s' doesn't exists", hostPath.c_str()); - return nullptr; - } - - TRACE("Host binary: %s", hostPath.c_str()); - std::string winePrefix = link.prefix(); Storage::Prefix prefix = storage.prefix(winePrefix); if(!prefix) { @@ -124,6 +108,29 @@ AEffect* VSTPluginMain(AudioMasterProc audioMasterProc) TRACE("VST binary: %s", vstPath.c_str()); + // Find host binary path + ModuleInfo::Arch arch = ModuleInfo::instance()->getArch(vstPath); + + std::string hostName; + if(arch == ModuleInfo::kArch64) { + hostName = HOST_BASENAME "-64.exe"; + } + else if(arch == ModuleInfo::kArch32) { + hostName = HOST_BASENAME "-32.exe"; + } + else { + ERROR("Unable to determine VST plugin architecture"); + return nullptr; + } + + std::string hostPath = FileSystem::realPath(storage.binariesPath() + '/' + hostName); + if(!FileSystem::isFileExists(hostPath)) { + ERROR("Host binary '%s' doesn't exists", hostPath.c_str()); + return nullptr; + } + + TRACE("Host binary: %s", hostPath.c_str()); + // We process only two cases, because messages with log levels lower than 'trace' // wouldn't be logged anyway. if(level == LogLevel::kTrace) { From eb88b571be2c48cddfb32e0c3030d860a86c4807 Mon Sep 17 00:00:00 2001 From: Anton Kalmykov Date: Fri, 17 Apr 2015 14:16:27 +0300 Subject: [PATCH 4/7] Fix the 64->32 bridging issue --- src/common/event.cpp | 7 +++++++ src/common/event.h | 7 ++++--- src/common/protocol.h | 19 ++++++------------- src/plugin/CMakeLists.txt | 1 + 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/common/event.cpp b/src/common/event.cpp index 5027165..1ae8a8d 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -20,6 +20,13 @@ Event::Event() : } +Event::~Event() +{ + // FIXME The current implementation is not correct as it wakes only the one waiter. + futex_post(&count_, 1); +} + + bool Event::wait(int msecs) { timespec* timeout = nullptr; diff --git a/src/common/event.h b/src/common/event.h index dfc5d76..34114f7 100644 --- a/src/common/event.h +++ b/src/common/event.h @@ -1,5 +1,5 @@ -#ifndef FUTEX_TEST_EVENT_H -#define FUTEX_TEST_EVENT_H +#ifndef COMMON_EVENT_H +#define COMMON_EVENT_H #include @@ -13,6 +13,7 @@ class Event { static const int kInfinite = -1; Event(); + ~Event(); bool wait(int msecs = kInfinite); void post(); @@ -22,4 +23,4 @@ class Event { }; -#endif // FUTEX_TEST_EVENT_H +#endif // COMMON_EVENT_H diff --git a/src/common/protocol.h b/src/common/protocol.h index 4251f48..55505d2 100644 --- a/src/common/protocol.h +++ b/src/common/protocol.h @@ -24,19 +24,12 @@ enum class Command { struct DataFrame { - Command command; - i32 opcode; - i32 index; - intptr_t value; - float opt; - -#ifdef __i386 - // The size of a pointer on the x86_64 in two times larger than on the x86. Because - // of this, we should use the 32 bits of padding on the x86. - void* padding; -#endif - - u8 data[]; + Command command; + i32 opcode; + i32 index; + i64 value; // The 64-bit value is used here to avoid 64->32 bridging issues + float opt; + u8 data[]; } __attribute__((packed)); diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt index 445adf2..068ccd6 100644 --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -3,6 +3,7 @@ set(TARGET_NAME ${PLUGIN_BASENAME}) project(${TARGET_NAME}) find_package(LibDl REQUIRED) +find_package(LibMagic REQUIRED) find_package(Threads REQUIRED) find_package(X11 REQUIRED) From a8a0711c90edccb64f10e903aae21c224bb0ed3b Mon Sep 17 00:00:00 2001 From: Anton Kalmykov Date: Fri, 17 Apr 2015 19:59:20 +0300 Subject: [PATCH 5/7] Remove the old ModuleInfo --- src/host/host.cpp | 5 ++- src/manager/CMakeLists.txt | 3 +- src/manager/core/moduleinfo.cpp | 55 ------------------------------- src/manager/core/moduleinfo.h | 35 -------------------- src/manager/forms/linkdialog.cpp | 1 - src/manager/models/linksmodel.cpp | 2 +- src/manager/models/linksmodel.h | 2 +- 7 files changed, 5 insertions(+), 98 deletions(-) delete mode 100644 src/manager/core/moduleinfo.cpp delete mode 100644 src/manager/core/moduleinfo.h diff --git a/src/host/host.cpp b/src/host/host.cpp index ef91701..e17aeab 100644 --- a/src/host/host.cpp +++ b/src/host/host.cpp @@ -281,7 +281,7 @@ bool Host::handleDispatch(DataFrame* frame) FLOOD("handleDispatch: %s", kDispatchEvents[frame->opcode]); if(isEditorOpen_ && frame->opcode != effEditIdle) { - // Delay delay the effEditIdle event by 100 milliseconds + // Postpone the effEditIdle event by 100 milliseconds SetTimer(hwnd_, timerId_, 100, nullptr); } @@ -526,9 +526,8 @@ void Host::handleProcessDouble() intptr_t Host::audioMaster(i32 opcode, i32 index, intptr_t value, void* ptr, float opt) { - if(opcode != audioMasterGetTime && opcode != audioMasterIdle) { + if(opcode != audioMasterGetTime && opcode != audioMasterIdle) FLOOD("handleAudioMaster(%s)", kAudioMasterEvents[opcode]); - } DataFrame* frame = callbackPort_.frame(); frame->command = Command::AudioMaster; diff --git a/src/manager/CMakeLists.txt b/src/manager/CMakeLists.txt index ad6ed38..5aaf99c 100644 --- a/src/manager/CMakeLists.txt +++ b/src/manager/CMakeLists.txt @@ -19,11 +19,10 @@ set(SOURCES main.cpp ../common/filesystem.cpp ../common/json.cpp -# ../common/moduleinfo.cpp + ../common/moduleinfo.cpp ../common/storage.cpp core/application.cpp core/logsocket.cpp - core/moduleinfo.cpp core/singleapplication.cpp forms/filedialog.cpp forms/folderdialog.cpp diff --git a/src/manager/core/moduleinfo.cpp b/src/manager/core/moduleinfo.cpp deleted file mode 100644 index bf187fa..0000000 --- a/src/manager/core/moduleinfo.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include "moduleinfo.h" - - -ModuleInfo::ModuleInfo() : - isInitialized_(false) -{ - magic_ = magic_open(MAGIC_NONE); - if(!magic_) { - qDebug("Unable to initialize libmagic."); - return; - } - - if(magic_load(magic_, nullptr) != 0) { - qDebug("libmagic error: %s", magic_error(magic_)); - magic_close(magic_); - return; - } - - isInitialized_ = true; -} - - -ModuleInfo::~ModuleInfo() -{ - magic_close(magic_); -} - - -ModuleInfo* ModuleInfo::instance() -{ - static ModuleInfo info; - return &info; -} - - -ModuleInfo::Arch ModuleInfo::getArch(const QString& fileName) const -{ - if(isInitialized_) { - const char* buffer = magic_file(magic_, fileName.toUtf8().data()); - - if(buffer) { - QString string = buffer; - - if(string.indexOf("80386") != -1) { - return kArch32; - } - else if(string.indexOf("x86-64") != -1) { - return kArch64; - } - } - } - - return kArchUnknown; -} diff --git a/src/manager/core/moduleinfo.h b/src/manager/core/moduleinfo.h deleted file mode 100644 index 778abb8..0000000 --- a/src/manager/core/moduleinfo.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CORE_MODULEINFO_H -#define CORE_MODULEINFO_H - -#include - - -class QString; - - -class ModuleInfo { -public: - enum Arch { - kArchUnknown = 0, - kArch32 = 32, - kArch64 = 64 - }; - - static ModuleInfo* instance(); - - ModuleInfo(const ModuleInfo&) = delete; - ModuleInfo& operator=(const ModuleInfo&) = delete; - - ~ModuleInfo(); - - Arch getArch(const QString& fileName) const; - -private: - bool isInitialized_; - magic_t magic_; - - ModuleInfo(); -}; - - -#endif // CORE_MODULEINFO_H diff --git a/src/manager/forms/linkdialog.cpp b/src/manager/forms/linkdialog.cpp index b6f6a00..4a40978 100644 --- a/src/manager/forms/linkdialog.cpp +++ b/src/manager/forms/linkdialog.cpp @@ -9,7 +9,6 @@ #include #include "common/config.h" #include "core/application.h" -#include "core/moduleinfo.h" #include "forms/filedialog.h" #include "models/linksmodel.h" #include "models/loadersmodel.h" diff --git a/src/manager/models/linksmodel.cpp b/src/manager/models/linksmodel.cpp index 31458bf..b709db7 100644 --- a/src/manager/models/linksmodel.cpp +++ b/src/manager/models/linksmodel.cpp @@ -13,7 +13,7 @@ LinkItem::LinkItem(Storage::Link link) : QString prefix = QString::fromStdString(storage->prefix(link_.prefix()).path()); QFileInfo info(QDir(prefix), QString::fromStdString(link_.target())); - arch_ = ModuleInfo::instance()->getArch(info.absoluteFilePath()); + arch_ = ModuleInfo::instance()->getArch(info.absoluteFilePath().toStdString()); } } diff --git a/src/manager/models/linksmodel.h b/src/manager/models/linksmodel.h index 98499a1..76d990d 100644 --- a/src/manager/models/linksmodel.h +++ b/src/manager/models/linksmodel.h @@ -2,8 +2,8 @@ #define MODELS_LINKSMODEL_H #include "common/logger.h" +#include "common/moduleinfo.h" #include "common/storage.h" -#include "core/moduleinfo.h" #include "models/generictreemodel.h" From bfc6e00f16b98c2235b7b04b5bf451d8511d66d8 Mon Sep 17 00:00:00 2001 From: Anton Kalmykov Date: Fri, 17 Apr 2015 20:20:56 +0300 Subject: [PATCH 6/7] Update README files --- README | 2 +- README.md | 101 ++++++++++++++++++++++++++++++------------------------ 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/README b/README index 28a02d4..769eada 100644 --- a/README +++ b/README @@ -33,7 +33,7 @@ Building the source mkdir build && cd build cmake -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX=/opt/airwave -DVSTSDK_PATH=${HOME}/VST3\ SDK .. make - make install + sudo make install Of course, you can change the CMAKE_INSTALL_PREFIX as you like. diff --git a/README.md b/README.md index 8471d08..64b6a30 100644 --- a/README.md +++ b/README.md @@ -2,48 +2,6 @@ Airwave is a [WINE](https://www.winehq.org/)-based VST bridge, that allows for the use of Windows 32- and 64-bit VST 2.4 audio plugins with Linux VST hosts. Due to the use of shared memory, only one extra copying is made for each data transfer. Airwave also uses the XEMBED protocol to correctly embed the plugin editor into the host window. -## Compatibility -The following list is not complete. It contains only plugins, that have been tested by me or by people, who sent me a report. - - VST-Plugins | works? | Notes | -------------:|:----------:|:-------| - Blue Cat Audio Oscilloscope Multi | no | doesn't work with wine - Credland Audio BigKick | no | doesn't work with wine - FabFilter Total bundle | yes | haven't tested them all - Image-Line Harmless | yes | - Image-Line Sytrus | yes | - LennarDigital Sylenth1 | no | doesn't work with wine - LePou Plugins | yes | LeCab2 has slight GUI redrawing issues - NI Absynth | yes | - NI FM8 | yes | - NI Guitar Rig 5 | yes | activation doesn't work - NI Kontakt 5 | mostly | up to v5.3.1, can import libraries only in Windows XP mode - NI Massive | yes | only 32-bit - NI Reaktor 5 | yes | - Peavey Revalver Mark III.V | yes | - ReFX Nexus2 | yes | - ReFX Vanguard | yes | - Reveal Sound Spire | yes | starting form 1.0.19 the GUI doesn't appear (wine issue) - Sonic Academy A.N.A. | yes | - Sonic Academy KICK | yes | - Smartelectronix s(M)exoscope | yes | - Synth1 by Ichiro Toda | yes | - Tone2 FireBird | yes | - Tone2 Nemesis | yes | - Tone2 Saurus | yes | - u-he A.C.E. | yes | Linux version is also available - u-he Bazille | yes | Linux version is also available - u-he Diva | yes | Linux version is also available - u-he Hive | yes | Linux version is also available - u-he Presswerk | yes | Linux version is also available - u-he Satin | yes | Linux version is also available - u-he Uhbik | yes | Linux version is also available - u-he Zebra2 | yes | Linux version is also available - Variety of Sound plugins | yes | - Voxengo SPAN | yes | - Voxengo SPAN Pro | mostly | inter plugin routing doesn't work (architecture issue) - Xfer Serum | partly | the GUI doesn't appear (wine issue), but audio works - ## Requirements - WINE, supporting XEMBED protocol (versions greater than 1.7.19 were tested, but earlier versions also may work) @@ -83,7 +41,7 @@ but earlier versions also may work) mkdir build && cd build cmake -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX=/opt/airwave -DVSTSDK_PATH=${HOME}/VST3\ SDK .. make - make install + sudo make install ``` Of course, you can change the CMAKE_INSTALL_PREFIX as you like. @@ -102,8 +60,8 @@ Of course, you can change the CMAKE_INSTALL_PREFIX as you like. ## Under the hood The bridge consists of four components: -- Plugin endpoint (airwave-plugin-.so) -- Host endpoint (airwave-host-.exe.so and airwave-host-.exe launcher script) +- Plugin endpoint (airwave-plugin.so) +- Host endpoint (airwave-host-{arch}.exe.so and airwave-host-{arch}.exe launcher script) - Configuration file (${XDG_CONFIG_PATH}/airwave/airwave.conf) - GUI configurator (airwave-manager) @@ -111,3 +69,56 @@ When the airwave-plugin is loaded by the VST host, it obtains its absolute path ## Known issues - Due to a bug in WINE, there is some hacking involved when embedding the editor window. There is a chance that you get a black window instead of the plugin GUI. Also some areas might not update correctly when increasing the window size. On some hosts (Bitwig Studio for example) this can be solved by closing and re-opening the plugin window. + +## Compatibility +The following list is not complete. It contains only plugins, that have been tested by me or by people, who sent me a report. + + VST-Plugins | works? | Notes | +------------:|:----------:|:-------| + AlgoMusic CZythia | yes | + Aly James LAB OB-Xtreme | yes | + Blue Cat Audio Oscilloscope Multi | no | doesn't work with wine + Credland Audio BigKick | no | doesn't work with wine + FabFilter Total bundle | yes | haven't tested them all + Green Oak Software Crystal | yes | + Image-Line Harmless | yes | + Image-Line Sytrus | yes | + LennarDigital Sylenth1 | no | doesn't work with wine + LePou Plugins | yes | LeCab2 has slight GUI redrawing issues + NI Absynth | yes | + NI FM8 | yes | + NI Guitar Rig 5 | yes | activation doesn't work + NI Kontakt 5 | mostly | up to v5.3.1, can import libraries only in Windows XP mode + NI Massive | yes | only 32-bit + NI Reaktor 5 | yes | + Magnus Choir | yes | + Martin Lüders pg8x | yes | + Meesha Damatriks | yes | + Odo Synths Double Six | partly | GUI doesn't show, but presets are available and functional + Peavey Revalver Mark III.V | yes | + ReFX Nexus2 | yes | + ReFX Vanguard | yes | + Reveal Sound Spire | yes | starting form 1.0.19 the GUI doesn't appear (wine issue) + Sonic Academy A.N.A. | yes | + Sonic Academy KICK | yes | + Sonic Cat LFX-1310 | yes | + Sonic Charge Cyclone | yes | + Smartelectronix s(M)exoscope | yes | + SQ8L by Siegfried Kullmann | yes | + SuperWave P8 | yes | + Synth1 by Ichiro Toda | yes | + Tone2 FireBird | yes | + Tone2 Nemesis | yes | + Tone2 Saurus | yes | + u-he A.C.E. | yes | Linux version is also available + u-he Bazille | yes | Linux version is also available + u-he Diva | yes | Linux version is also available + u-he Hive | yes | Linux version is also available + u-he Presswerk | yes | Linux version is also available + u-he Satin | yes | Linux version is also available + u-he Uhbik | yes | Linux version is also available + u-he Zebra2 | yes | Linux version is also available + Variety of Sound plugins | yes | + Voxengo SPAN | yes | + Voxengo SPAN Pro | mostly | inter plugin routing doesn't work (architecture issue) + Xfer Serum | no | the GUI doesn't appear (wine issue), but audio works From 154a2c7209c4a7724363f3cd608854be1be89643 Mon Sep 17 00:00:00 2001 From: Anton Kalmykov Date: Fri, 17 Apr 2015 20:21:58 +0300 Subject: [PATCH 7/7] Increase the minor version number --- CMakeLists.txt | 4 ++-- src/manager/airwave-manager.desktop | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea03d8a..ef4ebed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ project(${PROJECT_NAME}) # Project version set(VERSION_MAJOR 1) -set(VERSION_MINOR 0) -set(VERSION_PATCH 2) +set(VERSION_MINOR 1) +set(VERSION_PATCH 0) # Set plugin shared library base name set(PLUGIN_BASENAME ${PROJECT_NAME}-plugin) diff --git a/src/manager/airwave-manager.desktop b/src/manager/airwave-manager.desktop index 5844939..68623c8 100644 --- a/src/manager/airwave-manager.desktop +++ b/src/manager/airwave-manager.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Type=Application -Version=1.0 +Version=1.1.0 Name=Airwave manager Comment=A tool for managing the Airwave VST bridge Exec=airwave-manager