1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Converted AudioSampleBuffer into a templated class that can use either float or double types. Used this to implement 64-bit audio plugin support in VST and AU

This commit is contained in:
jules 2015-11-02 11:09:41 +00:00
parent ba672f03fb
commit c562cfc3cc
27 changed files with 1713 additions and 1104 deletions

View file

@ -85,7 +85,20 @@ public:
// not interested in controllers in this case.
}
void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override
void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
{
processBlock (outputBuffer, startSample, numSamples);
}
void renderNextBlock (AudioBuffer<double>& outputBuffer, int startSample, int numSamples) override
{
processBlock (outputBuffer, startSample, numSamples);
}
private:
template <typename FloatType>
void processBlock (AudioBuffer<FloatType>& outputBuffer, int startSample, int numSamples)
{
if (angleDelta != 0.0)
{
@ -93,7 +106,8 @@ public:
{
while (--numSamples >= 0)
{
const float currentSample = (float) (sin (currentAngle) * level * tailOff);
const FloatType currentSample =
static_cast<FloatType> (std::sin (currentAngle) * level * tailOff);
for (int i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
@ -116,7 +130,7 @@ public:
{
while (--numSamples >= 0)
{
const float currentSample = (float) (sin (currentAngle) * level);
const FloatType currentSample = static_cast<FloatType> (std::sin (currentAngle) * level);
for (int i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
@ -128,7 +142,6 @@ public:
}
}
private:
double currentAngle, angleDelta, level, tailOff;
};
@ -183,7 +196,6 @@ const float defaultDelay = 0.5f;
//==============================================================================
JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor()
: delayBuffer (2, 12000)
{
// Set up our parameters. The base class will delete them for us.
addParameter (gain = new FloatParameter (defaultGain, "Gain"));
@ -213,7 +225,19 @@ void JuceDemoPluginAudioProcessor::prepareToPlay (double newSampleRate, int /*sa
// initialisation that you need..
synth.setCurrentPlaybackSampleRate (newSampleRate);
keyboardState.reset();
delayBuffer.clear();
if (isUsingDoublePrecision())
{
delayBufferDouble.setSize (2, 12000);
delayBufferFloat.setSize (1, 1);
}
else
{
delayBufferFloat.setSize (2, 12000);
delayBufferDouble.setSize (1, 1);
}
reset();
}
void JuceDemoPluginAudioProcessor::releaseResources()
@ -227,17 +251,21 @@ void JuceDemoPluginAudioProcessor::reset()
{
// Use this method as the place to clear any delay lines, buffers, etc, as it
// means there's been a break in the audio's continuity.
delayBuffer.clear();
delayBufferFloat.clear();
delayBufferDouble.clear();
}
void JuceDemoPluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
template <typename FloatType>
void JuceDemoPluginAudioProcessor::process (AudioBuffer<FloatType>& buffer,
MidiBuffer& midiMessages,
AudioBuffer<FloatType>& delayBuffer)
{
const int numSamples = buffer.getNumSamples();
int channel, dp = 0;
// Go through the incoming data, and apply our gain to it...
for (channel = 0; channel < getNumInputChannels(); ++channel)
buffer.applyGain (channel, 0, buffer.getNumSamples(), gain->getValue());
buffer.applyGain (channel, 0, buffer.getNumSamples(), static_cast<FloatType> (gain->getValue()));
// Now pass any incoming midi messages to our keyboard state object, and let it
// add messages to the buffer if the user is clicking on the on-screen keys
@ -249,15 +277,16 @@ void JuceDemoPluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, Midi
// Apply our delay effect to the new output..
for (channel = 0; channel < getNumInputChannels(); ++channel)
{
float* channelData = buffer.getWritePointer (channel);
float* delayData = delayBuffer.getWritePointer (jmin (channel, delayBuffer.getNumChannels() - 1));
FloatType* channelData = buffer.getWritePointer (channel);
FloatType* delayData = delayBuffer.getWritePointer (jmin (channel, delayBuffer.getNumChannels() - 1));
dp = delayPosition;
for (int i = 0; i < numSamples; ++i)
{
const float in = channelData[i];
const FloatType in = channelData[i];
channelData[i] += delayData[dp];
delayData[dp] = (delayData[dp] + in) * delay->getValue();
delayData[dp] = (delayData[dp] + in) * static_cast<FloatType> (delay->getValue());
if (++dp >= delayBuffer.getNumSamples())
dp = 0;
}

View file

@ -28,13 +28,28 @@ public:
//==============================================================================
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override;
void reset() override;
//==============================================================================
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
jassert (! isUsingDoublePrecision());
process (buffer, midiMessages, delayBufferFloat);
}
void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
{
jassert (isUsingDoublePrecision());
process (buffer, midiMessages, delayBufferDouble);
}
//==============================================================================
bool hasEditor() const override { return true; }
AudioProcessorEditor* createEditor() override;
//==============================================================================
bool supportsDoublePrecisionProcessing() const override { return true; }
//==============================================================================
const String getName() const override { return JucePlugin_Name; }
@ -83,7 +98,11 @@ public:
private:
//==============================================================================
AudioSampleBuffer delayBuffer;
template <typename floatType>
void process (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages, AudioBuffer<floatType>& delayBuffer);
AudioBuffer<float> delayBufferFloat;
AudioBuffer<double> delayBufferDouble;
int delayPosition;
// the synth!

View file

@ -1087,7 +1087,8 @@ private:
//==============================================================================
GraphDocumentComponent::GraphDocumentComponent (AudioPluginFormatManager& formatManager,
AudioDeviceManager* deviceManager_)
: graph (formatManager), deviceManager (deviceManager_)
: graph (formatManager), deviceManager (deviceManager_),
graphPlayer (getAppProperties().getUserSettings()->getBoolValue ("doublePrecisionProcessing", false))
{
addAndMakeVisible (graphPanel = new GraphEditorPanel (graph));

View file

@ -88,6 +88,7 @@ public:
//==============================================================================
void createNewPlugin (const PluginDescription* desc, int x, int y);
inline void setDoublePrecision (bool doublePrecision) { graphPlayer.setDoublePrecisionProcessing (doublePrecision); }
//==============================================================================
FilterGraph graph;

View file

@ -240,6 +240,7 @@ PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String&
menu.addSeparator();
menu.addCommandItem (&getCommandManager(), CommandIDs::showAudioSettings);
menu.addCommandItem (&getCommandManager(), CommandIDs::toggleDoublePrecision);
menu.addSeparator();
menu.addCommandItem (&getCommandManager(), CommandIDs::aboutBox);
@ -331,6 +332,7 @@ void MainHostWindow::getAllCommands (Array <CommandID>& commands)
CommandIDs::saveAs,
CommandIDs::showPluginListEditor,
CommandIDs::showAudioSettings,
CommandIDs::toggleDoublePrecision,
CommandIDs::aboutBox,
CommandIDs::allWindowsForward
};
@ -376,6 +378,10 @@ void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationComma
result.addDefaultKeypress ('a', ModifierKeys::commandModifier);
break;
case CommandIDs::toggleDoublePrecision:
updatePrecisionMenuItem (result);
break;
case CommandIDs::aboutBox:
result.setInfo ("About...", String::empty, category, 0);
break;
@ -427,6 +433,23 @@ bool MainHostWindow::perform (const InvocationInfo& info)
showAudioSettings();
break;
case CommandIDs::toggleDoublePrecision:
if (PropertiesFile* props = getAppProperties().getUserSettings())
{
bool newIsDoublePrecision = ! isDoublePrecisionProcessing();
props->setValue ("doublePrecisionProcessing", var (newIsDoublePrecision));
{
ApplicationCommandInfo cmdInfo (info.commandID);
updatePrecisionMenuItem (cmdInfo);
menuItemsChanged();
}
if (graphEditor != nullptr)
graphEditor->setDoublePrecision (newIsDoublePrecision);
}
break;
case CommandIDs::aboutBox:
// TODO
break;
@ -524,3 +547,17 @@ GraphDocumentComponent* MainHostWindow::getGraphEditor() const
{
return dynamic_cast <GraphDocumentComponent*> (getContentComponent());
}
bool MainHostWindow::isDoublePrecisionProcessing()
{
if (PropertiesFile* props = getAppProperties().getUserSettings())
return props->getBoolValue ("doublePrecisionProcessing", false);
return false;
}
void MainHostWindow::updatePrecisionMenuItem (ApplicationCommandInfo& info)
{
info.setInfo ("Double floating point precision rendering", String::empty, "General", 0);
info.setTicked (isDoublePrecisionProcessing());
}

View file

@ -40,6 +40,7 @@ namespace CommandIDs
static const int showAudioSettings = 0x30200;
static const int aboutBox = 0x30300;
static const int allWindowsForward = 0x30400;
static const int toggleDoublePrecision = 0x30500;
}
ApplicationCommandManager& getCommandManager();
@ -86,6 +87,9 @@ public:
GraphDocumentComponent* getGraphEditor() const;
bool isDoublePrecisionProcessing();
void updatePrecisionMenuItem (ApplicationCommandInfo& info);
private:
//==============================================================================
AudioDeviceManager deviceManager;