Browse Source

Merge branch 'v2' of https://github.com/VCVRack/Rack into v262

pull/1941/head
Steve Russell 1 month ago
parent
commit
bed52b1d71
8 changed files with 161 additions and 141 deletions
  1. +1
    -0
      .gitignore
  2. +4
    -0
      CHANGELOG.md
  3. +2
    -2
      Component.plist
  4. +7
    -3
      Makefile
  5. +7
    -6
      README.md
  6. +4
    -0
      src/library.cpp
  7. +2
    -2
      src/plugin.cpp
  8. +134
    -128
      src/rtaudio.cpp

+ 1
- 0
.gitignore View File

@@ -11,6 +11,7 @@
*.res *.res
*.d *.d
*.dSYM *.dSYM
*.vcvplugin
/build /build
/Rack /Rack
/dep /dep


+ 4
- 0
CHANGELOG.md View File

@@ -2,6 +2,10 @@


In this document, Ctrl means Command (⌘) on Mac. In this document, Ctrl means Command (⌘) on Mac.


### 2.6.2 (2025-03-17)
- Fix incorrect audio driver being loaded from patches made in Rack ≤2.6.0.
- Fix Mac installer creating duplicate app bundle.

### 2.6.1 (2025-03-14) ### 2.6.1 (2025-03-14)
- Add UI translations for: - Add UI translations for:
- German - German


+ 2
- 2
Component.plist View File

@@ -3,9 +3,9 @@
<plist version="1.0"> <plist version="1.0">
<array> <array>
<dict> <dict>
<key>BundleHasStrictIdentifier</key><true/>
<key>BundleHasStrictIdentifier</key><false/>
<key>BundleIsVersionChecked</key><false/> <key>BundleIsVersionChecked</key><false/>
<key>BundleIsRelocatable</key><false/>
<key>BundleIsRelocatable</key><true/>
<key>BundleOverwriteAction</key><string>upgrade</string> <key>BundleOverwriteAction</key><string>upgrade</string>
<key>RootRelativeBundlePath</key><string>VCV Rack 2 Free.app</string> <key>RootRelativeBundlePath</key><string>VCV Rack 2 Free.app</string>
</dict> </dict>


+ 7
- 3
Makefile View File

@@ -175,10 +175,14 @@ DIST_HTML := $(patsubst %.md, build/%.html, $(DIST_MD))
DIST_RES := res cacert.pem Core.json template.vcv LICENSE-GPLv3.txt $(DIST_HTML) translations DIST_RES := res cacert.pem Core.json template.vcv LICENSE-GPLv3.txt $(DIST_HTML) translations
DIST_SDK_DIR := Rack-SDK DIST_SDK_DIR := Rack-SDK
DIST_SDK = Rack-SDK-$(VERSION)-$(ARCH_NAME).zip DIST_SDK = Rack-SDK-$(VERSION)-$(ARCH_NAME).zip
FUNDAMENTAL_VERSION ?= 2.6.1
FUNDAMENTAL_FILENAME := Fundamental-$(FUNDAMENTAL_VERSION)-$(ARCH_NAME).vcvplugin




dist: $(TARGET) $(STANDALONE_TARGET) $(DIST_HTML) dist: $(TARGET) $(STANDALONE_TARGET) $(DIST_HTML)
mkdir -p dist mkdir -p dist
# Download Fundamental package if not already downloaded
[ -f "$(FUNDAMENTAL_FILENAME)" ] || curl -o "$(FUNDAMENTAL_FILENAME)" "https://api.vcvrack.com/download?slug=Fundamental&version=$(FUNDAMENTAL_VERSION)&arch=$(ARCH_NAME)"
ifdef ARCH_LIN ifdef ARCH_LIN
mkdir -p dist/"$(DIST_DIR)" mkdir -p dist/"$(DIST_DIR)"
cp $(TARGET) dist/"$(DIST_DIR)"/ cp $(TARGET) dist/"$(DIST_DIR)"/
@@ -190,7 +194,7 @@ ifdef ARCH_LIN
ldd dist/"$(DIST_DIR)"/$(STANDALONE_TARGET) ldd dist/"$(DIST_DIR)"/$(STANDALONE_TARGET)
# Copy resources # Copy resources
cp -R $(DIST_RES) dist/"$(DIST_DIR)"/ cp -R $(DIST_RES) dist/"$(DIST_DIR)"/
cp plugins/Fundamental/dist/Fundamental-*.vcvplugin dist/"$(DIST_DIR)"/
cp "$(FUNDAMENTAL_FILENAME)" dist/"$(DIST_DIR)"/
endif endif
ifdef ARCH_MAC ifdef ARCH_MAC
mkdir -p dist/"$(DIST_BUNDLE)" mkdir -p dist/"$(DIST_BUNDLE)"
@@ -210,7 +214,7 @@ ifdef ARCH_MAC
$(SED) 's/{VERSION}/$(VERSION)/g' dist/"$(DIST_BUNDLE)"/Contents/Info.plist $(SED) 's/{VERSION}/$(VERSION)/g' dist/"$(DIST_BUNDLE)"/Contents/Info.plist
cp -R icon.icns dist/"$(DIST_BUNDLE)"/Contents/Resources/ cp -R icon.icns dist/"$(DIST_BUNDLE)"/Contents/Resources/
cp -R $(DIST_RES) dist/"$(DIST_BUNDLE)"/Contents/Resources/ cp -R $(DIST_RES) dist/"$(DIST_BUNDLE)"/Contents/Resources/
cp plugins/Fundamental/dist/Fundamental-*.vcvplugin dist/"$(DIST_BUNDLE)"/Contents/Resources/
cp "$(FUNDAMENTAL_FILENAME)" dist/"$(DIST_BUNDLE)"/Contents/Resources/
endif endif
ifdef ARCH_WIN ifdef ARCH_WIN
mkdir -p dist/"$(DIST_DIR)" mkdir -p dist/"$(DIST_DIR)"
@@ -223,7 +227,7 @@ ifdef ARCH_WIN
cp /mingw64/bin/libwinpthread-1.dll dist/"$(DIST_DIR)"/ cp /mingw64/bin/libwinpthread-1.dll dist/"$(DIST_DIR)"/
cp /mingw64/bin/libstdc++-6.dll dist/"$(DIST_DIR)"/ cp /mingw64/bin/libstdc++-6.dll dist/"$(DIST_DIR)"/
cp /mingw64/bin/libgcc_s_seh-1.dll dist/"$(DIST_DIR)"/ cp /mingw64/bin/libgcc_s_seh-1.dll dist/"$(DIST_DIR)"/
cp plugins/Fundamental/dist/Fundamental-*.vcvplugin dist/"$(DIST_DIR)"/
cp "$(FUNDAMENTAL_FILENAME)" dist/"$(DIST_DIR)"/
endif endif






+ 7
- 6
README.md View File

@@ -18,14 +18,15 @@
- [Richie Hindle](http://entrian.com/audio/): Rack developer, bug fixes - [Richie Hindle](http://entrian.com/audio/): Rack developer, bug fixes
- [Grayscale](https://grayscale.info/): Module design, branding - [Grayscale](https://grayscale.info/): Module design, branding
- Christoph Scholtes: [Library reviews](https://github.com/VCVRack/library) and [plugin toolchain](https://github.com/VCVRack/rack-plugin-toolchain) - Christoph Scholtes: [Library reviews](https://github.com/VCVRack/library) and [plugin toolchain](https://github.com/VCVRack/rack-plugin-toolchain)
- Translators
- German: Stephan Müsch, Norbert Denninger
- Spanish: Kevin U. Cano Guerra, Coriander V. Pines
- French: Pyer
- Italian: Alessandro Paglia
- Chinese (Simplified): NoiseTone
- Japanese: [Leo Kuroshita](https://x.com/kurogedelic)
- Rack plugin developers: Authorship shown on each plugin's [VCV Library](https://library.vcvrack.com/) page - Rack plugin developers: Authorship shown on each plugin's [VCV Library](https://library.vcvrack.com/) page
- Rack users like you: [Bug reports and feature requests](https://vcvrack.com/support) - Rack users like you: [Bug reports and feature requests](https://vcvrack.com/support)
- German translation: Stephan Müsch, Norbert Denninger
- Spanish translation: Kevin U. Cano Guerra, Coriander V. Pines
- French translation: Pyer
- Italian translation: Alessandro Paglia
- Chinese (Simplified) translation: NoiseTone
- Japanese translation: [Leo Kuroshita](https://x.com/kurogedelic)


## Dependency libraries ## Dependency libraries




+ 4
- 0
src/library.cpp View File

@@ -57,6 +57,10 @@ void destroy() {
std::lock_guard<std::mutex> timeoutLock(timeoutMutex); std::lock_guard<std::mutex> timeoutLock(timeoutMutex);
std::lock_guard<std::mutex> appUpdateLock(appUpdateMutex); std::lock_guard<std::mutex> appUpdateLock(appUpdateMutex);
std::lock_guard<std::mutex> updateLock(updateMutex); std::lock_guard<std::mutex> updateLock(updateMutex);

// Clear globals in case init() is called again
loginStatus = "";
updateInfos.clear();
} }






+ 2
- 2
src/plugin.cpp View File

@@ -250,9 +250,9 @@ static void extractPackages(std::string path) {
} }


static std::string getFundamentalPackagePath() { static std::string getFundamentalPackagePath() {
std::regex r("Fundamental-.*-" + APP_OS + "-" + APP_CPU + "\\.vcvplugin");
for (const std::string& path : system::getEntries(asset::systemDir)) { for (const std::string& path : system::getEntries(asset::systemDir)) {
if (std::regex_match(system::getFilename(path), r))
std::string filename = system::getFilename(path);
if (string::startsWith(filename, "Fundamental-") && string::endsWith(filename, APP_OS + "-" + APP_CPU + ".vcvplugin"))
return path; return path;
} }
return ""; return "";


+ 134
- 128
src/rtaudio.cpp View File

@@ -18,22 +18,82 @@
namespace rack { namespace rack {




static const std::map<RtAudio::Api, std::string> RTAUDIO_API_NAMES = {
{RtAudio::LINUX_ALSA, "ALSA"},
{RtAudio::UNIX_JACK, "JACK"},
{RtAudio::LINUX_PULSE, "PulseAudio"},
{RtAudio::LINUX_OSS, "OSS"},
{RtAudio::WINDOWS_WASAPI, "WASAPI"},
{RtAudio::WINDOWS_ASIO, "ASIO"},
{RtAudio::WINDOWS_DS, "DirectSound"},
{RtAudio::MACOSX_CORE, "Core Audio"},
{RtAudio::RTAUDIO_DUMMY, "Dummy"},
{RtAudio::UNSPECIFIED, "Unspecified"},
struct RtAudioDevice;


struct RtAudioDriver : audio::Driver {
RtAudio::Api api;
std::string name;
RtAudio* rtAudio = NULL;
// deviceId -> Device
std::map<int, RtAudioDevice*> devices;

RtAudioDriver(RtAudio::Api api, std::string name) {
this->api = api;
this->name = name;

INFO("Creating RtAudio %s driver", name.c_str());
rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) {
WARN("RtAudio error %d: %s", type, errorText.c_str());
});

rtAudio->showWarnings(false);
}

~RtAudioDriver() {
assert(devices.empty());
if (rtAudio)
delete rtAudio;
}

std::string getName() override {
return name;
}

std::vector<int> getDeviceIds() override {
std::vector<int> deviceIds;
if (rtAudio) {
for (unsigned int id : rtAudio->getDeviceIds()) {
deviceIds.push_back(id);
}
}
return deviceIds;
}

std::string getDeviceName(int deviceId) override {
if (rtAudio) {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
if (deviceInfo.ID > 0)
return deviceInfo.name;
}
return "";
}

int getDeviceNumInputs(int deviceId) override {
if (rtAudio) {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
if (deviceInfo.ID > 0)
return deviceInfo.inputChannels;
}
return 0;
}

int getDeviceNumOutputs(int deviceId) override {
if (rtAudio) {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
if (deviceInfo.ID > 0)
return deviceInfo.outputChannels;
}
return 0;
}

audio::Device* subscribe(int deviceId, audio::Port* port) override;
void unsubscribe(int deviceId, audio::Port* port) override;
}; };




struct RtAudioDevice : audio::Device { struct RtAudioDevice : audio::Device {
RtAudio::Api api;
RtAudioDriver* driver;
int deviceId; int deviceId;
RtAudio* rtAudio; RtAudio* rtAudio;
RtAudio::DeviceInfo deviceInfo; RtAudio::DeviceInfo deviceInfo;
@@ -43,13 +103,13 @@ struct RtAudioDevice : audio::Device {
int blockSize = 0; int blockSize = 0;
float sampleRate = 0; float sampleRate = 0;


RtAudioDevice(RtAudio::Api api, int deviceId) {
this->api = api;
RtAudioDevice(RtAudioDriver* driver, int deviceId) {
this->driver = driver;
this->deviceId = deviceId; this->deviceId = deviceId;


// Create RtAudio object // Create RtAudio object
INFO("Creating RtAudio %s device", RTAUDIO_API_NAMES.at(api).c_str());
rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) {
INFO("Creating RtAudio %s device", driver->getName().c_str());
rtAudio = new RtAudio(driver->api, [](RtAudioErrorType type, const std::string& errorText) {
WARN("RtAudio error %d: %s", type, errorText.c_str()); WARN("RtAudio error %d: %s", type, errorText.c_str());
}); });


@@ -59,7 +119,7 @@ struct RtAudioDevice : audio::Device {
// Query device ID // Query device ID
deviceInfo = rtAudio->getDeviceInfo(deviceId); deviceInfo = rtAudio->getDeviceInfo(deviceId);
if (deviceInfo.ID == 0) if (deviceInfo.ID == 0)
throw Exception("Failed to query RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
throw Exception("Failed to query RtAudio %s device %d", driver->getName().c_str(), deviceId);


openStream(); openStream();
} }
@@ -78,7 +138,7 @@ struct RtAudioDevice : audio::Device {
void openStream() { void openStream() {
// Open new device // Open new device
if (deviceInfo.outputChannels == 0 && deviceInfo.inputChannels == 0) { if (deviceInfo.outputChannels == 0 && deviceInfo.inputChannels == 0) {
throw Exception("RtAudio %s device %d has 0 inputs and 0 outputs", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
throw Exception("RtAudio %s device %d has 0 inputs and 0 outputs", driver->getName().c_str(), deviceId);
} }


inputParameters = RtAudio::StreamParameters(); inputParameters = RtAudio::StreamParameters();
@@ -109,25 +169,25 @@ struct RtAudioDevice : audio::Device {


if (blockSize <= 0) { if (blockSize <= 0) {
// DirectSound should use a higher default block size // DirectSound should use a higher default block size
if (api == RtAudio::WINDOWS_DS)
if (driver->api == RtAudio::WINDOWS_DS)
blockSize = 1024; blockSize = 1024;
else else
blockSize = 256; blockSize = 256;
} }


INFO("Opening RtAudio %s device %d: %s (%d in, %d out, %d sample rate, %d block size)", RTAUDIO_API_NAMES.at(api).c_str(), deviceId, deviceInfo.name.c_str(), inputParameters.nChannels, outputParameters.nChannels, closestSampleRate, blockSize);
INFO("Opening RtAudio %s device %d: %s (%d in, %d out, %d sample rate, %d block size)", driver->getName().c_str(), deviceId, deviceInfo.name.c_str(), inputParameters.nChannels, outputParameters.nChannels, closestSampleRate, blockSize);
if (rtAudio->openStream( if (rtAudio->openStream(
outputParameters.nChannels > 0 ? &outputParameters : NULL, outputParameters.nChannels > 0 ? &outputParameters : NULL,
inputParameters.nChannels > 0 ? &inputParameters : NULL, inputParameters.nChannels > 0 ? &inputParameters : NULL,
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize,
&rtAudioCallback, this, &options)) { &rtAudioCallback, this, &options)) {
throw Exception("Failed to open RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
throw Exception("Failed to open RtAudio %s device %d", driver->getName().c_str(), deviceId);
} }


try { try {
INFO("Starting RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
INFO("Starting RtAudio %s device %d", driver->getName().c_str(), deviceId);
if (rtAudio->startStream()) { if (rtAudio->startStream()) {
throw Exception("Failed to start RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
throw Exception("Failed to start RtAudio %s device %d", driver->getName().c_str(), deviceId);
} }


// Update sample rate to actual value // Update sample rate to actual value
@@ -143,11 +203,11 @@ struct RtAudioDevice : audio::Device {


void closeStream() { void closeStream() {
if (rtAudio->isStreamRunning()) { if (rtAudio->isStreamRunning()) {
INFO("Stopping RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
INFO("Stopping RtAudio %s device %d", driver->getName().c_str(), deviceId);
rtAudio->stopStream(); rtAudio->stopStream();
} }
if (rtAudio->isStreamOpen()) { if (rtAudio->isStreamOpen()) {
INFO("Closing RtAudio %s device %d", RTAUDIO_API_NAMES.at(api).c_str(), deviceId);
INFO("Closing RtAudio %s device %d", driver->getName().c_str(), deviceId);
rtAudio->closeStream(); rtAudio->closeStream();
} }


@@ -220,126 +280,72 @@ struct RtAudioDevice : audio::Device {
}; };




struct RtAudioDriver : audio::Driver {
RtAudio::Api api;
// deviceId -> Device
std::map<int, RtAudioDevice*> devices;
RtAudio* rtAudio = NULL;

RtAudioDriver(RtAudio::Api api) {
this->api = api;
audio::Device* RtAudioDriver::subscribe(int deviceId, audio::Port* port) {
RtAudioDevice* device;
auto it = devices.find(deviceId);
if (it == devices.end()) {
// ASIO only allows one device to be used simultaneously
if (api == RtAudio::WINDOWS_ASIO && devices.size() >= 1)
throw Exception("ASIO driver only allows one audio device to be used simultaneously");


INFO("Creating RtAudio %s driver", RTAUDIO_API_NAMES.at(api).c_str());
rtAudio = new RtAudio(api, [](RtAudioErrorType type, const std::string& errorText) {
WARN("RtAudio error %d: %s", type, errorText.c_str());
});

rtAudio->showWarnings(false);
// Can throw Exception
device = new RtAudioDevice(this, deviceId);
devices[deviceId] = device;
} }

~RtAudioDriver() {
assert(devices.empty());
if (rtAudio)
delete rtAudio;
}

std::string getName() override {
return RTAUDIO_API_NAMES.at(api);
}

std::vector<int> getDeviceIds() override {
std::vector<int> deviceIds;
if (rtAudio) {
for (unsigned int id : rtAudio->getDeviceIds()) {
deviceIds.push_back(id);
}
}
return deviceIds;
else {
device = it->second;
} }


std::string getDeviceName(int deviceId) override {
if (rtAudio) {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
if (deviceInfo.ID > 0)
return deviceInfo.name;
}
return "";
}

int getDeviceNumInputs(int deviceId) override {
if (rtAudio) {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
if (deviceInfo.ID > 0)
return deviceInfo.inputChannels;
}
return 0;
}
device->subscribe(port);
return device;
}


int getDeviceNumOutputs(int deviceId) override {
if (rtAudio) {
RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(deviceId);
if (deviceInfo.ID > 0)
return deviceInfo.outputChannels;
}
return 0;
}


audio::Device* subscribe(int deviceId, audio::Port* port) override {
RtAudioDevice* device;
auto it = devices.find(deviceId);
if (it == devices.end()) {
// ASIO only allows one device to be used simultaneously
if (api == RtAudio::WINDOWS_ASIO && devices.size() >= 1)
throw Exception("ASIO driver only allows one audio device to be used simultaneously");

// Can throw Exception
device = new RtAudioDevice(api, deviceId);
devices[deviceId] = device;
}
else {
device = it->second;
}
void RtAudioDriver::unsubscribe(int deviceId, audio::Port* port) {
auto it = devices.find(deviceId);
if (it == devices.end())
return;
RtAudioDevice* device = it->second;
device->unsubscribe(port);


device->subscribe(port);
return device;
if (device->subscribed.empty()) {
devices.erase(it);
delete device;
} }
}


void unsubscribe(int deviceId, audio::Port* port) override {
auto it = devices.find(deviceId);
if (it == devices.end())
return;
RtAudioDevice* device = it->second;
device->unsubscribe(port);


if (device->subscribed.empty()) {
devices.erase(it);
delete device;
}
}
struct ApiInfo {
// Should match indices in https://github.com/VCVRack/rtaudio/blob/ece277bd839603648c80c8a5f145678e13bc23f3/RtAudio.cpp#L107-L118
int driverId;
RtAudio::Api rtApi;
// Used instead of RtAudio::getApiName()
std::string name;
};
// The vector order here defines the order in the audio driver menu
static const std::vector<ApiInfo> API_INFOS = {
{1, RtAudio::LINUX_ALSA, "ALSA"},
{2, RtAudio::LINUX_PULSE, "PulseAudio"},
{4, RtAudio::UNIX_JACK, "JACK"},
{5, RtAudio::MACOSX_CORE, "Core Audio"},
{6, RtAudio::WINDOWS_WASAPI, "WASAPI"},
{7, RtAudio::WINDOWS_ASIO, "ASIO"},
{8, RtAudio::WINDOWS_DS, "DirectSound"},
}; };




void rtaudioInit() { void rtaudioInit() {
// Get RtAudio's driver list
std::vector<RtAudio::Api> apis; std::vector<RtAudio::Api> apis;
RtAudio::getCompiledApi(apis); RtAudio::getCompiledApi(apis);


// I don't like the order returned by getCompiledApi(), so reorder it here.
std::vector<RtAudio::Api> orderedApis = {
RtAudio::LINUX_ALSA,
RtAudio::LINUX_PULSE,
RtAudio::UNIX_JACK,
RtAudio::LINUX_OSS,
RtAudio::WINDOWS_WASAPI,
RtAudio::WINDOWS_ASIO,
RtAudio::WINDOWS_DS,
RtAudio::MACOSX_CORE,
};
for (RtAudio::Api api : orderedApis) {
auto it = std::find(apis.begin(), apis.end(), api);
if (it != apis.end()) {
RtAudioDriver* driver = new RtAudioDriver(api);
audio::addDriver((int) api, driver);
}
for (const ApiInfo& apiInfo : API_INFOS) {
auto it = std::find(apis.begin(), apis.end(), apiInfo.rtApi);
if (it == apis.end())
continue;
// Create and add driver
RtAudioDriver* driver = new RtAudioDriver(apiInfo.rtApi, apiInfo.name);
audio::addDriver(apiInfo.driverId, driver);
} }
} }




Loading…
Cancel
Save