diff --git a/patch.js b/patch.js index c916795..ded436f 100644 --- a/patch.js +++ b/patch.js @@ -4,7 +4,7 @@ const patchs = { 'JUCE/modules/juce_gui_basics/native/juce_win32_Windowing.cpp': { from: 'hwnd = CreateWindowEx', to: 'hwnd = parentToAddTo != nullptr && (styleFlags & (1 << 28)) ? CreateWindow(WindowClassHolder::getInstance()->getWindowClassName(), L"",\ -WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | ((type & windowIsResizable) ? WS_MAXIMIZEBOX | WS_THICKFRAME : 0),\ +WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | ((styleFlags & windowIsResizable) ? WS_MAXIMIZEBOX | WS_THICKFRAME : 0),\ 0, 0, 0, 0, parentToAddTo, nullptr, (HINSTANCE)Process::getCurrentModuleInstanceHandle(), nullptr) : CreateWindowEx' }, 'JUCE/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp': { diff --git a/src/Main.cpp b/src/Main.cpp index 6e578a6..525fdc4 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -20,23 +20,26 @@ juce::AudioDeviceManager::AudioDeviceSetup setup; template inline void write(T var) { std::fwrite(&var, sizeof(var), 1, stdout); } template inline void writeCerr(T var) { std::fwrite(&var, sizeof(var), 1, stderr); } -void writeError(juce::String str, FILE* file) { - write((char)127); +void writeString(juce::String str, FILE* file = stdout) { write(str.length()); std::fwrite(str.toRawUTF8(), sizeof(char), str.length(), file); fflush(file); +} +void writeError(juce::String str, FILE* file) { + write((char)127); + writeString(str, file); (file == stderr ? std::cout : std::cerr) << str << '\n'; } std::string readString() { - int len; - READ(len); - char* str = new char[len + 1]; - std::fread(str, sizeof(char), len, stdin); + int len; + READ(len); + char* str = new char[len + 1]; + std::fread(str, sizeof(char), len, stdin); str[len] = '\0'; - return str; + return str; } -class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, private juce::Thread { +class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, public juce::AudioProcessorListener, private juce::Thread { public: EIMPluginHost(): juce::AudioPlayHead(), juce::Thread("IO Thread") { } @@ -61,7 +64,8 @@ class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, return; } processor->setPlayHead(this); - if (args->containsOption("-P|--preset")) loadState(args->getValueForOption("-P|--preset")); + processor->addListener(this); + if (args->containsOption("-P|--preset")) loadState(args->getValueForOption("-P|--preset")); freopen(nullptr, "rb", stdin); freopen(nullptr, "wb", stderr); @@ -107,27 +111,31 @@ class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, double timeInSeconds = (double)timeInSamples / sampleRate; juce::MessageManagerLock mml(Thread::getCurrentThread()); if (!mml.lockWasGained()) return; - auto _isRealtime = (flags & FLAGS_IS_REALTIME) != 0; + auto _isRealtime = (flags & FLAGS_IS_REALTIME) != 0; if (isRealtime != _isRealtime) { processor->setNonRealtime(!_isRealtime); isRealtime = _isRealtime; } positionInfo.setIsPlaying((flags & FLAGS_IS_PLAYING) != 0); - positionInfo.setIsLooping((flags & FLAGS_IS_LOOPING) != 0); - positionInfo.setIsRecording((flags & FLAGS_IS_RECORDING) != 0); + positionInfo.setIsLooping((flags & FLAGS_IS_LOOPING) != 0); + positionInfo.setIsRecording((flags & FLAGS_IS_RECORDING) != 0); positionInfo.setBpm(bpm); positionInfo.setTimeInSamples(timeInSamples); positionInfo.setTimeInSeconds(timeInSeconds); positionInfo.setPpqPosition(timeInSeconds / 60.0 * bpm); for (int i = 0; i < numInputChannels; i++) std::fread(buffer.getWritePointer(i), sizeof(float), bufferSize, stdin); juce::MidiBuffer buf; - for (int i = 0; i < midiEvents; i++) { + for (int i = 0; i < midiEvents; i++) { int data; short time; - READ(data); - READ(time); - buf.addEvent(juce::MidiMessage(data & 0xFF, (data >> 8) & 0xFF, (data >> 16) & 0xFF), time); - } + READ(data); + READ(time); + buf.addEvent(juce::MidiMessage(data & 0xFF, (data >> 8) & 0xFF, (data >> 16) & 0xFF), time); + } + if (hostBufferPos > 0 && mtx.try_lock()) { + std::fwrite(hostBuffer, sizeof(char), hostBufferPos, stderr); + hostBufferPos = 0; + } processor->processBlock(buffer, buf); writeCerr((char) 1); for (int i = 0; i < numOutputChannels; i++) std::fwrite(buffer.getReadPointer(i), sizeof(float), bufferSize, stderr); @@ -145,7 +153,7 @@ class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, juce::MessageManagerLock mml(Thread::getCurrentThread()); if (!mml.lockWasGained()) return; juce::MemoryBlock memory; - processor->getStateInformation(memory); + processor->getStateInformation(memory); juce::File(readString()).replaceWithData(memory.getData(), memory.getSize()); break; } @@ -171,6 +179,22 @@ class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, static juce::JUCEApplicationBase* createInstance() { return new EIMPluginHost(); } + bool canControlTransport() override { return true; } + + virtual void transportPlay(bool shouldStartPlaying) override { + writeToHostBuffer((char)2); + writeToHostBuffer((char)shouldStartPlaying); + } + + void audioProcessorParameterChanged(juce::AudioProcessor*, int parameterIndex, float newValue) override { + writeToHostBuffer((char)3); + writeToHostBuffer(parameterIndex); + writeToHostBuffer(newValue); + } + + void audioProcessorChanged(juce::AudioProcessor*, const ChangeDetails&) override { + } + private: juce::MidiBuffer midiBuffer; juce::AudioBuffer buffer; @@ -178,10 +202,13 @@ class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, std::unique_ptr processor; juce::AudioPlayHead::PositionInfo positionInfo; bool isRealtime = true; - int sampleRate = 44800, bufferSize = 1024, ppq = 96; + int sampleRate = 48000, bufferSize = 1024, ppq = 96; + volatile int hostBufferPos = 0; + char hostBuffer[8192]; + std::mutex mtx; void createEditorWindow() { - if (!processor->hasEditor()) return; + if (!processor->hasEditor()) return; auto component = processor->createEditorIfNeeded(); if (!component) return; window.reset(new PluginWindow("[EIMHost] " + processor->getName() + " (" + @@ -191,12 +218,20 @@ class EIMPluginHost : public juce::JUCEApplication, public juce::AudioPlayHead, } void loadState(juce::String file) { - juce::FileInputStream stream(file); + juce::FileInputStream stream(file); juce::MemoryBlock memory; stream.readIntoMemoryBlock(memory); processor->setStateInformation(memory.getData(), (int)memory.getSize()); } + template inline void writeToHostBuffer(T var) { + T* p = reinterpret_cast(&var); + if (mtx.try_lock()) { + for (int i = 0; i < sizeof(T); i++) hostBuffer[hostBufferPos++] = ((char*)p)[i]; + mtx.unlock(); + } + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EIMPluginHost) }; @@ -225,7 +260,13 @@ class AudioCallback : public juce::AudioIODeviceCallback { } void audioDeviceAboutToStart(juce::AudioIODevice* device) override { - std::cerr << "Device started: " << device->getName() << ", " << device->getOutputLatencyInSamples() << '\n'; + write((char)1); + writeString("[" + device->getTypeName() + "] " + device->getName()); + write(device->getInputLatencyInSamples()); + write(device->getOutputLatencyInSamples()); + write((float)device->getCurrentSampleRate()); + write(device->getCurrentBufferSizeSamples()); + std::cerr << "Device started: " << device->getName() << '\n'; } void audioDeviceStopped() override { } @@ -254,7 +295,7 @@ int main(int argc, char* argv[]) { puts(juce::JSON::toString(paths).toRawUTF8()); return 0; #else - std::cerr << "No any file specified.\n"; + std::cerr << "No any file specified.\n"; return -1; #endif } @@ -295,21 +336,29 @@ int main(int argc, char* argv[]) { juce::JUCEApplicationBase::main(argc, (const char**)argv); juce::shutdownJuce_GUI(); } else if (args->containsOption("-O|--output")) { - auto deviceType = args->getValueForOption("-T|--type"); - auto deviceName = args->getValueForOption("-O|--output"); - setup.bufferSize = args->containsOption("-B|--bufferSize") ? args->getValueForOption("-B|--bufferSize").getIntValue() : 1024; - setup.sampleRate = args->containsOption("-R|--sampleRate") ? args->getValueForOption("-R|--sampleRate").getIntValue() : 44800; - #ifdef JUCE_WINDOWS CoInitialize(nullptr); #endif juce::AudioDeviceManager deviceManager; + if (args->containsOption("-A|--all")) { + for (auto& it : deviceManager.getAvailableDeviceTypes()) { + it->scanForDevices(); + for (auto& j : it->getDeviceNames()) { + std::cout << "[" << it->getTypeName() << "] " << j << "$EIM$"; + } + } + fflush(stdout); + return 0; + } + auto deviceType = args->getValueForOption("-T|--type"); + auto deviceName = args->getValueForOption("-O|--output"); + setup.bufferSize = args->containsOption("-B|--bufferSize") ? args->getValueForOption("-B|--bufferSize").getIntValue() : 1024; + setup.sampleRate = args->containsOption("-R|--sampleRate") ? args->getValueForOption("-R|--sampleRate").getIntValue() : 48000; + AudioCallback audioCallback(deviceManager); for (auto& it : deviceManager.getAvailableDeviceTypes()) { if (deviceType == it->getTypeName()) it->scanForDevices(); - /*std::cerr << it->getTypeName() << "\n"; - for (auto& j : it->getDeviceNames()) std::cerr << " " << j << "\n";*/ } if (deviceType.isNotEmpty()) deviceManager.setCurrentAudioDeviceType(deviceType, true); if (deviceName.isNotEmpty()) setup.outputDeviceName = setup.inputDeviceName = deviceName; diff --git a/src/PluginWindow.h b/src/PluginWindow.h index 9b6a34f..d26a7dd 100644 --- a/src/PluginWindow.h +++ b/src/PluginWindow.h @@ -35,11 +35,13 @@ class PluginWindow : public juce::ResizableWindow { } void moved() override { + juce::ResizableWindow::moved(); _x = getX(); _y = getY(); } void resized() override { + juce::ResizableWindow::resized(); _width = getWidth(); _height = getHeight(); }