initial
This commit is contained in:
657
Source/WebViewPluginDemo.h
Normal file
657
Source/WebViewPluginDemo.h
Normal file
@ -0,0 +1,657 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE framework examples.
|
||||
Copyright (c) Raw Material Software Limited
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
to use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this PIP. A PIP is a short snippet
|
||||
of code that can be read by the Projucer and used to generate a JUCE project.
|
||||
|
||||
BEGIN_JUCE_PIP_METADATA
|
||||
|
||||
name: WebViewPluginDemo
|
||||
version: 1.0.0
|
||||
vendor: JUCE
|
||||
website: http://juce.com
|
||||
description: Filtering audio plugin using an HTML/JS user interface
|
||||
|
||||
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
|
||||
juce_audio_plugin_client, juce_audio_processors, juce_dsp,
|
||||
juce_audio_utils, juce_core, juce_data_structures,
|
||||
juce_events, juce_graphics, juce_gui_basics, juce_gui_extra
|
||||
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
|
||||
|
||||
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1, JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING=1
|
||||
|
||||
type: AudioProcessor
|
||||
mainClass: WebViewPluginAudioProcessorWrapper
|
||||
|
||||
useLocalCopy: 1
|
||||
|
||||
END_JUCE_PIP_METADATA
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DemoUtilities.h"
|
||||
#include <JuceHeader.h>
|
||||
#include "Shifter.h"
|
||||
|
||||
//using namespace juce::dsp;
|
||||
|
||||
namespace ID
|
||||
{
|
||||
#define PARAMETER_ID(str) static const ParameterID str { #str, 1 };
|
||||
|
||||
PARAMETER_ID(formantPreserve)
|
||||
PARAMETER_ID(mute)
|
||||
PARAMETER_ID(filterType)
|
||||
|
||||
#undef PARAMETER_ID
|
||||
}
|
||||
|
||||
class CircularBuffer
|
||||
{
|
||||
public:
|
||||
CircularBuffer(int numChannels, int numSamples)
|
||||
: buffer(data, (size_t)numChannels, (size_t)numSamples)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void push(dsp::AudioBlock<T> b)
|
||||
{
|
||||
jassert(b.getNumChannels() == buffer.getNumChannels());
|
||||
|
||||
const auto trimmed = b.getSubBlock(b.getNumSamples()
|
||||
- std::min(b.getNumSamples(), buffer.getNumSamples()));
|
||||
|
||||
const auto bufferLength = (int64)buffer.getNumSamples();
|
||||
|
||||
for (auto samplesRemaining = (int64)trimmed.getNumSamples(); samplesRemaining > 0;)
|
||||
{
|
||||
const auto writeOffset = writeIx % bufferLength;
|
||||
const auto numSamplesToWrite = std::min(samplesRemaining, bufferLength - writeOffset);
|
||||
|
||||
auto destSubBlock = buffer.getSubBlock((size_t)writeOffset, (size_t)numSamplesToWrite);
|
||||
const auto sourceSubBlock = trimmed.getSubBlock(trimmed.getNumSamples() - (size_t)samplesRemaining,
|
||||
(size_t)numSamplesToWrite);
|
||||
|
||||
destSubBlock.copyFrom(sourceSubBlock);
|
||||
|
||||
samplesRemaining -= numSamplesToWrite;
|
||||
writeIx += numSamplesToWrite;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void push(Span<T> s)
|
||||
{
|
||||
auto* ptr = s.begin();
|
||||
dsp::AudioBlock<T> b(&ptr, 1, s.size());
|
||||
push(b);
|
||||
}
|
||||
|
||||
void read(int64 readIx, dsp::AudioBlock<float> output) const
|
||||
{
|
||||
const auto numChannelsToUse = std::min(buffer.getNumChannels(), output.getNumChannels());
|
||||
|
||||
jassert(output.getNumChannels() == buffer.getNumChannels());
|
||||
|
||||
const auto bufferLength = (int64)buffer.getNumSamples();
|
||||
|
||||
for (auto outputOffset = (size_t)0; outputOffset < output.getNumSamples();)
|
||||
{
|
||||
const auto inputOffset = (size_t)((readIx + (int64)outputOffset) % bufferLength);
|
||||
const auto numSamplesToRead = std::min(output.getNumSamples() - outputOffset,
|
||||
(size_t)bufferLength - inputOffset);
|
||||
|
||||
auto destSubBlock = output.getSubBlock(outputOffset, numSamplesToRead)
|
||||
.getSubsetChannelBlock(0, numChannelsToUse);
|
||||
|
||||
destSubBlock.copyFrom(buffer.getSubBlock(inputOffset, numSamplesToRead)
|
||||
.getSubsetChannelBlock(0, numChannelsToUse));
|
||||
|
||||
outputOffset += numSamplesToRead;
|
||||
}
|
||||
}
|
||||
|
||||
int64 getWriteIndex() const noexcept { return writeIx; }
|
||||
|
||||
private:
|
||||
HeapBlock<char> data;
|
||||
dsp::AudioBlock<float> buffer;
|
||||
int64 writeIx = 0;
|
||||
};
|
||||
|
||||
class SpectralBars
|
||||
{
|
||||
public:
|
||||
//template <typename T>
|
||||
void push(int data)
|
||||
{
|
||||
testQueue.push(data);
|
||||
}
|
||||
|
||||
void compute(Span<int> output) {
|
||||
int index = 0;
|
||||
for (auto it = output.begin(); it != output.end(); ++it) {
|
||||
*it = testQueue.get(index++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
circ_queue<int, 256> testQueue;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class WebViewPluginAudioProcessor : public AudioProcessor
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout);
|
||||
|
||||
//==============================================================================
|
||||
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
|
||||
void releaseResources() override {}
|
||||
|
||||
bool isBusesLayoutSupported(const BusesLayout& layouts) const override;
|
||||
|
||||
void processBlock(AudioBuffer<float>&, MidiBuffer&) override;
|
||||
using AudioProcessor::processBlock;
|
||||
|
||||
//==============================================================================
|
||||
const String getName() const override { return JucePlugin_Name; }
|
||||
|
||||
bool acceptsMidi() const override { return false; }
|
||||
bool producesMidi() const override { return false; }
|
||||
bool isMidiEffect() const override { return false; }
|
||||
double getTailLengthSeconds() const override { return 0.0; }
|
||||
|
||||
//==============================================================================
|
||||
int getNumPrograms() override { return 1; }
|
||||
int getCurrentProgram() override { return 0; }
|
||||
void setCurrentProgram(int) override {}
|
||||
const String getProgramName(int) override { return {}; }
|
||||
void changeProgramName(int, const String&) override {}
|
||||
|
||||
//==============================================================================
|
||||
void getStateInformation(MemoryBlock& destData) override;
|
||||
void setStateInformation(const void* data, int sizeInBytes) override;
|
||||
|
||||
struct Parameters
|
||||
{
|
||||
public:
|
||||
explicit Parameters(AudioProcessorValueTreeState::ParameterLayout& layout)
|
||||
: formantPreserve(addToLayout<AudioParameterFloat>(layout,
|
||||
ID::formantPreserve,
|
||||
"Formant Preserve",
|
||||
NormalisableRange<float> {0.0f, 1.0f, .01f},
|
||||
.5f)),
|
||||
|
||||
autoTuneSpeed(addToLayout<AudioParameterFloat>(layout,
|
||||
ID::formantPreserve,
|
||||
"AutoTuneSpeed",
|
||||
NormalisableRange<float> {0.0f, 1.0f, .01f},
|
||||
.5f)),
|
||||
mute(addToLayout<AudioParameterBool>(layout, ID::mute, "Mute", false)),
|
||||
filterType(addToLayout<AudioParameterChoice>(layout,
|
||||
ID::filterType,
|
||||
"Filter type",
|
||||
StringArray{ "Low-pass", "High-pass", "Band-pass" },
|
||||
0))
|
||||
{
|
||||
}
|
||||
|
||||
AudioParameterFloat& formantPreserve;
|
||||
AudioParameterFloat& autoTuneSpeed;
|
||||
AudioParameterBool& mute;
|
||||
AudioParameterChoice& filterType;
|
||||
|
||||
private:
|
||||
template <typename Param>
|
||||
static void add(AudioProcessorParameterGroup& group, std::unique_ptr<Param> param)
|
||||
{
|
||||
group.addChild(std::move(param));
|
||||
}
|
||||
|
||||
template <typename Param>
|
||||
static void add(AudioProcessorValueTreeState::ParameterLayout& group, std::unique_ptr<Param> param)
|
||||
{
|
||||
group.add(std::move(param));
|
||||
}
|
||||
|
||||
template <typename Param, typename Group, typename... Ts>
|
||||
static Param& addToLayout(Group& layout, Ts&&... ts)
|
||||
{
|
||||
auto param = std::make_unique<Param>(std::forward<Ts>(ts)...);
|
||||
auto& ref = *param;
|
||||
add(layout, std::move(param));
|
||||
return ref;
|
||||
}
|
||||
};
|
||||
|
||||
Parameters parameters;
|
||||
AudioProcessorValueTreeState state;
|
||||
|
||||
std::vector<int> spectrumData = [] { return std::vector<int>(256, 0.0f); }();
|
||||
SpinLock spectrumDataLock;
|
||||
|
||||
SpectralBars spectralBars;
|
||||
|
||||
dsp::LadderFilter<float> filter;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessor)
|
||||
Shifter shifter;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
WebViewPluginAudioProcessor::WebViewPluginAudioProcessor(AudioProcessorValueTreeState::ParameterLayout layout)
|
||||
: AudioProcessor(BusesProperties()
|
||||
#if ! JucePlugin_IsMidiEffect
|
||||
#if ! JucePlugin_IsSynth
|
||||
.withInput("Input", juce::AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
.withOutput("Output", juce::AudioChannelSet::stereo(), true)
|
||||
#endif
|
||||
),
|
||||
parameters(layout),
|
||||
state(*this, nullptr, "STATE", std::move(layout))
|
||||
{
|
||||
shifter.Init();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebViewPluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
|
||||
{
|
||||
const auto channels = std::max(getTotalNumInputChannels(), getTotalNumOutputChannels());
|
||||
|
||||
if (channels == 0)
|
||||
return;
|
||||
|
||||
filter.prepare({ sampleRate, (uint32_t)samplesPerBlock, (uint32_t)channels });
|
||||
filter.reset();
|
||||
}
|
||||
|
||||
bool WebViewPluginAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const
|
||||
{
|
||||
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
|
||||
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
|
||||
return false;
|
||||
|
||||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer,
|
||||
juce::MidiBuffer& midi)
|
||||
{
|
||||
juce::ScopedNoDenormals noDenormals;
|
||||
|
||||
const auto totalNumInputChannels = getTotalNumInputChannels();
|
||||
const auto totalNumOutputChannels = getTotalNumOutputChannels();
|
||||
|
||||
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
||||
buffer.clear(i, 0, buffer.getNumSamples());
|
||||
shifter.SetFormantPreserve(parameters.formantPreserve.get());
|
||||
juce::AudioBuffer<float> const_buff;
|
||||
const_buff.makeCopyOf(buffer);
|
||||
shifter.Process(const_buff.getArrayOfReadPointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
|
||||
|
||||
for (const auto metadata : midi)
|
||||
{
|
||||
const auto msg = metadata.getMessage();
|
||||
if (msg.isNoteOn()) shifter.AddMidiNote(msg.getNoteNumber());
|
||||
else if (msg.isNoteOff()) shifter.RemoveMidiNote(msg.getNoteNumber());
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
//DBG(shifter.out_midi[MAX_VOICES]);
|
||||
//push midi note
|
||||
spectralBars.push(shifter.out_midi[MAX_VOICES]);
|
||||
const SpinLock::ScopedTryLockType lock(spectrumDataLock);
|
||||
|
||||
if (!lock.isLocked())
|
||||
return;
|
||||
|
||||
spectralBars.compute({ spectrumData.data(), spectrumData.size() });
|
||||
}
|
||||
|
||||
|
||||
/*for(auto i = 0; i < buffer.getNumSamples(); ++i)
|
||||
{
|
||||
bool process = (i % 256) == 0 && i != 0;
|
||||
|
||||
for(auto j = 0; j < totalNumInputChannels; ++j)
|
||||
{
|
||||
input[j][i] = buffer.getReadPointer(j)[i];
|
||||
}
|
||||
}
|
||||
|
||||
filter.setCutoffFrequencyHz (parameters.cutoffFreqHz.get());
|
||||
|
||||
const auto filterMode = [this]
|
||||
{
|
||||
switch (parameters.filterType.getIndex())
|
||||
{
|
||||
case 0:
|
||||
return dsp::LadderFilter<float>::Mode::LPF12;
|
||||
|
||||
case 1:
|
||||
return dsp::LadderFilter<float>::Mode::HPF12;
|
||||
|
||||
default:
|
||||
return dsp::LadderFilter<float>::Mode::BPF12;
|
||||
}
|
||||
}();
|
||||
|
||||
filter.setMode (filterMode);
|
||||
|
||||
auto outBlock = dsp::AudioBlock<float> { buffer }.getSubsetChannelBlock (0, (size_t) getTotalNumOutputChannels());
|
||||
|
||||
if (parameters.mute.get())
|
||||
outBlock.clear();
|
||||
|
||||
filter.process (dsp::ProcessContextReplacing<float> (outBlock));
|
||||
|
||||
spectralBars.push (Span { buffer.getReadPointer (0), (size_t) buffer.getNumSamples() });
|
||||
|
||||
{
|
||||
const SpinLock::ScopedTryLockType lock (spectrumDataLock);
|
||||
|
||||
if (! lock.isLocked())
|
||||
return;
|
||||
|
||||
spectralBars.compute ({ spectrumData.data(), spectrumData.size() });
|
||||
}*/
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebViewPluginAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
|
||||
{
|
||||
juce::ignoreUnused(destData);
|
||||
}
|
||||
|
||||
void WebViewPluginAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
|
||||
{
|
||||
juce::ignoreUnused(data, sizeInBytes);
|
||||
}
|
||||
|
||||
extern const String localDevServerAddress;
|
||||
|
||||
std::optional<WebBrowserComponent::Resource> getResource(const String& url);
|
||||
|
||||
//==============================================================================
|
||||
struct SinglePageBrowser : WebBrowserComponent
|
||||
{
|
||||
using WebBrowserComponent::WebBrowserComponent;
|
||||
|
||||
// Prevent page loads from navigating away from our single page web app
|
||||
bool pageAboutToLoad(const String& newURL) override;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class WebViewPluginAudioProcessorEditor : public AudioProcessorEditor, private Timer
|
||||
{
|
||||
public:
|
||||
explicit WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor&);
|
||||
|
||||
std::optional<WebBrowserComponent::Resource> getResource(const String& url);
|
||||
|
||||
//==============================================================================
|
||||
void paint(Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
int getControlParameterIndex(Component&) override
|
||||
{
|
||||
return controlParameterIndexReceiver.getControlParameterIndex();
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
static constexpr size_t numFramesBuffered = 5;
|
||||
|
||||
SpinLock::ScopedLockType lock{ processorRef.spectrumDataLock };
|
||||
|
||||
Array<var> frame;
|
||||
|
||||
for (size_t i = 1; i < processorRef.spectrumData.size(); ++i)
|
||||
frame.add(processorRef.spectrumData[i]);
|
||||
|
||||
spectrumDataFrames.clear();
|
||||
|
||||
spectrumDataFrames.push_back(std::move(frame));
|
||||
|
||||
while (spectrumDataFrames.size() > numFramesBuffered)
|
||||
spectrumDataFrames.pop_front();
|
||||
|
||||
static int64 callbackCounter = 0;
|
||||
|
||||
/*if ( spectrumDataFrames.size() == numFramesBuffered
|
||||
&& callbackCounter++ % (int64) numFramesBuffered)
|
||||
{*/
|
||||
webComponent.emitEventIfBrowserIsVisible("spectrumData", var{});
|
||||
//}
|
||||
}
|
||||
|
||||
private:
|
||||
WebViewPluginAudioProcessor& processorRef;
|
||||
|
||||
WebSliderRelay formantSliderRelay{ "formantSlider" };
|
||||
WebToggleButtonRelay muteToggleRelay{ "muteToggle" };
|
||||
WebComboBoxRelay filterTypeComboRelay{ "filterTypeCombo" };
|
||||
|
||||
WebControlParameterIndexReceiver controlParameterIndexReceiver;
|
||||
|
||||
SinglePageBrowser webComponent{ WebBrowserComponent::Options{}
|
||||
.withBackend(WebBrowserComponent::Options::Backend::webview2)
|
||||
.withWinWebView2Options(WebBrowserComponent::Options::WinWebView2{}
|
||||
.withUserDataFolder(File::getSpecialLocation(File::SpecialLocationType::tempDirectory)))
|
||||
.withNativeIntegrationEnabled()
|
||||
.withOptionsFrom(formantSliderRelay)
|
||||
.withOptionsFrom(muteToggleRelay)
|
||||
.withOptionsFrom(filterTypeComboRelay)
|
||||
.withOptionsFrom(controlParameterIndexReceiver)
|
||||
.withNativeFunction("sayHello", [](auto& var, auto complete)
|
||||
{
|
||||
complete("Hello " + var[0].toString());
|
||||
})
|
||||
.withResourceProvider([this](const auto& url)
|
||||
{
|
||||
return getResource(url);
|
||||
},
|
||||
URL { localDevServerAddress }.getOrigin()) };
|
||||
|
||||
WebSliderParameterAttachment formantAttachment;
|
||||
WebToggleButtonParameterAttachment muteAttachment;
|
||||
WebComboBoxParameterAttachment filterTypeAttachment;
|
||||
|
||||
std::deque<Array<var>> spectrumDataFrames;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessorEditor)
|
||||
};
|
||||
|
||||
static ZipFile* getZipFile()
|
||||
{
|
||||
static auto stream = createAssetInputStream("webviewplugin-gui_1.0.0.zip", AssertAssetExists::no);
|
||||
|
||||
if (stream == nullptr)
|
||||
return nullptr;
|
||||
|
||||
static ZipFile f{ stream.get(), false };
|
||||
return &f;
|
||||
}
|
||||
|
||||
static const char* getMimeForExtension(const String& extension)
|
||||
{
|
||||
static const std::unordered_map<String, const char*> mimeMap =
|
||||
{
|
||||
{ { "htm" }, "text/html" },
|
||||
{ { "html" }, "text/html" },
|
||||
{ { "txt" }, "text/plain" },
|
||||
{ { "jpg" }, "image/jpeg" },
|
||||
{ { "jpeg" }, "image/jpeg" },
|
||||
{ { "svg" }, "image/svg+xml" },
|
||||
{ { "ico" }, "image/vnd.microsoft.icon" },
|
||||
{ { "json" }, "application/json" },
|
||||
{ { "png" }, "image/png" },
|
||||
{ { "css" }, "text/css" },
|
||||
{ { "map" }, "application/json" },
|
||||
{ { "js" }, "text/javascript" },
|
||||
{ { "woff2" }, "font/woff2" }
|
||||
};
|
||||
|
||||
if (const auto it = mimeMap.find(extension.toLowerCase()); it != mimeMap.end())
|
||||
return it->second;
|
||||
|
||||
jassertfalse;
|
||||
return "";
|
||||
}
|
||||
|
||||
static String getExtension(String filename)
|
||||
{
|
||||
return filename.fromLastOccurrenceOf(".", false, false);
|
||||
}
|
||||
|
||||
static auto streamToVector(InputStream& stream)
|
||||
{
|
||||
std::vector<std::byte> result((size_t)stream.getTotalLength());
|
||||
stream.setPosition(0);
|
||||
[[maybe_unused]] const auto bytesRead = stream.read(result.data(), result.size());
|
||||
jassert(bytesRead == (ssize_t)result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<WebBrowserComponent::Resource> WebViewPluginAudioProcessorEditor::getResource(const String& url)
|
||||
{
|
||||
const auto urlToRetrive = url == "/" ? String{ "index.html" }
|
||||
: url.fromFirstOccurrenceOf("/", false, false);
|
||||
|
||||
if (auto* archive = getZipFile())
|
||||
{
|
||||
if (auto* entry = archive->getEntry(urlToRetrive))
|
||||
{
|
||||
auto stream = rawToUniquePtr(archive->createStreamForEntry(*entry));
|
||||
auto v = streamToVector(*stream);
|
||||
auto mime = getMimeForExtension(getExtension(entry->filename).toLowerCase());
|
||||
return WebBrowserComponent::Resource{ std::move(v),
|
||||
std::move(mime) };
|
||||
}
|
||||
}
|
||||
|
||||
if (urlToRetrive == "index.html")
|
||||
{
|
||||
auto fallbackIndexHtml = createAssetInputStream("webviewplugin-gui-fallback.html");
|
||||
return WebBrowserComponent::Resource{ streamToVector(*fallbackIndexHtml),
|
||||
String { "text/html" } };
|
||||
}
|
||||
|
||||
if (urlToRetrive == "data.txt")
|
||||
{
|
||||
WebBrowserComponent::Resource resource;
|
||||
static constexpr char testData[] = "testdata";
|
||||
MemoryInputStream stream{ testData, numElementsInArray(testData) - 1, false };
|
||||
return WebBrowserComponent::Resource{ streamToVector(stream), String { "text/html" } };
|
||||
}
|
||||
|
||||
if (urlToRetrive == "spectrumData.json")
|
||||
{
|
||||
Array<var> frames;
|
||||
|
||||
for (const auto& frame : spectrumDataFrames)
|
||||
frames.add(frame);
|
||||
|
||||
DynamicObject::Ptr d(new DynamicObject());
|
||||
d->setProperty("timeResolutionMs", getTimerInterval());
|
||||
d->setProperty("frames", std::move(frames));
|
||||
|
||||
const auto s = JSON::toString(d.get());
|
||||
MemoryInputStream stream{ s.getCharPointer(), s.getNumBytesAsUTF8(), false };
|
||||
return WebBrowserComponent::Resource{ streamToVector(stream), String { "application/json" } };
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#if JUCE_ANDROID
|
||||
// The localhost is available on this address to the emulator
|
||||
const String localDevServerAddress = "http://10.0.2.2:3000/";
|
||||
#else
|
||||
const String localDevServerAddress = "http://localhost:3000/";
|
||||
#endif
|
||||
|
||||
bool SinglePageBrowser::pageAboutToLoad(const String& newURL)
|
||||
{
|
||||
return newURL == localDevServerAddress || newURL == getResourceProviderRoot();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
WebViewPluginAudioProcessorEditor::WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor& p)
|
||||
: AudioProcessorEditor(&p), processorRef(p),
|
||||
formantAttachment(*processorRef.state.getParameter(ID::formantPreserve.getParamID()),
|
||||
formantSliderRelay,
|
||||
processorRef.state.undoManager),
|
||||
muteAttachment(*processorRef.state.getParameter(ID::mute.getParamID()),
|
||||
muteToggleRelay,
|
||||
processorRef.state.undoManager),
|
||||
filterTypeAttachment(*processorRef.state.getParameter(ID::filterType.getParamID()),
|
||||
filterTypeComboRelay,
|
||||
processorRef.state.undoManager)
|
||||
{
|
||||
addAndMakeVisible(webComponent);
|
||||
|
||||
webComponent.goToURL(localDevServerAddress);
|
||||
//webComponent.goToURL (WebBrowserComponent::getResourceProviderRoot());
|
||||
|
||||
setSize(500, 500);
|
||||
|
||||
startTimerHz(20);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebViewPluginAudioProcessorEditor::paint(Graphics& g)
|
||||
{
|
||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||
g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId));
|
||||
}
|
||||
|
||||
void WebViewPluginAudioProcessorEditor::resized()
|
||||
{
|
||||
webComponent.setBounds(getLocalBounds());
|
||||
}
|
||||
|
||||
class WebViewPluginAudioProcessorWrapper : public WebViewPluginAudioProcessor
|
||||
{
|
||||
public:
|
||||
WebViewPluginAudioProcessorWrapper() : WebViewPluginAudioProcessor({})
|
||||
{
|
||||
}
|
||||
|
||||
bool hasEditor() const override { return true; }
|
||||
AudioProcessorEditor* createEditor() override { return new WebViewPluginAudioProcessorEditor(*this); }
|
||||
};
|
||||
Reference in New Issue
Block a user