Is it possible to use JUCE’s DSP together with PortAudio, and that everything is compatible at the same time.
I don’t see why it wouldn’t work, but what is the point of using PortAudio if you are willing to get a dependency on Juce anyway? Is there something the Juce AudioDeviceManager and related classes can’t do that PortAudio can do?
I just don’t really understand how to output sound correctly. I can’t hear myself.
Code:
#include "MainComponent.h"
#include <Windows.h>
//==============================================================================
MainComponent::MainComponent()
{
// Make sure you set the size of the component after
// you add any child components.
setSize (800, 600);
juce::String error;
error = deviceManager.initialiseWithDefaultDevices(1, 2);
}
MainComponent::~MainComponent()
{
// This shuts down the audio device and clears the audio source.
shutdownAudio();
}
//==============================================================================
void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
// This function will be called when the audio device is started, or when
// its settings (i.e. sample rate, block size, etc) are changed.
// You can use this function to initialise any resources you might need,
// but be careful - it will be called on the audio thread, not the GUI thread.
// For more details, see the help for AudioProcessor::prepareToPlay()
//processors.prepare(
// {
// sampleRate,
// (juce::uint32)samplesPerBlockExpected,
// 1
// }
//);
//auto& inputGain = processors.template get<0>();
//inputGain.setGainDecibels(6.0f);
//auto& noiseGate = processors.template get<1>();
//noiseGate.setThreshold(-40.0f);
//noiseGate.setRatio(1.0f / 100.0f);
//noiseGate.setAttack(5.0f);
//noiseGate.setRelease(50.0f);
//auto& equalizer = processors.template get<2>();
//equalizer.coefficients = dsp::IIR::Coefficients<float>::makeHighPass(sampleRate, 80.0f, 0.707f);
//auto& compressor = processors.template get<3>();
//compressor.setThreshold(-20.0f);
//compressor.setRatio(4.0f);
//compressor.setAttack(5.0f);
//compressor.setRelease(100.0f);
//auto& limiter = processors.template get<4>();
//limiter.setThreshold(-1.0f);
//limiter.setRelease(50.0f);
//auto& outputGain = processors.template get<5>();
//outputGain.setGainDecibels(4.0f);
}
void MainComponent::getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill)
{
auto* buffer = bufferToFill.buffer;
if (buffer == nullptr) return;
// Получаем устройство и активные каналы
auto* device = deviceManager.getCurrentAudioDevice();
if (device == nullptr) return;
auto activeInputChannels = device->getActiveInputChannels();
auto activeOutputChannels = device->getActiveOutputChannels();
int maxInputChannels = activeInputChannels.getHighestBit() + 1;
int maxOutputChannels = activeOutputChannels.getHighestBit() + 1;
bufferToFill.clearActiveBufferRegion();
for (int channel = 0; channel < maxOutputChannels; ++channel)
{
if (!activeOutputChannels[channel])
{
buffer->clear(channel, bufferToFill.startSample, bufferToFill.numSamples);
continue;
}
if (maxInputChannels == 0)
{
buffer->clear(channel, bufferToFill.startSample, bufferToFill.numSamples);
continue;
}
int actualInputChannel = channel % maxInputChannels;
if (activeInputChannels[actualInputChannel])
{
auto* inBuffer = buffer->getReadPointer(actualInputChannel, bufferToFill.startSample);
auto* outBuffer = buffer->getWritePointer(channel, bufferToFill.startSample);
juce::FloatVectorOperations::copy(outBuffer, inBuffer, bufferToFill.numSamples);
juce::FloatVectorOperations::multiply(outBuffer, 2.0f, bufferToFill.numSamples);
}
else
{
buffer->clear(channel, bufferToFill.startSample, bufferToFill.numSamples);
}
}
}
void MainComponent::releaseResources()
{
// This will be called when the audio device stops, or when it is being
// restarted due to a setting change.
// For more details, see the help for AudioProcessor::releaseResources()
}
//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
// You can add your drawing code here!
}
void MainComponent::resized()
{
// This is called when the MainContentComponent is resized.
// If you add any child components, this is where you should
// update their positions.
}
I’m asking about PortAudio because I have more experience with it.
Did you check if you get some kind of descriptive error text out of that? Did you check with the debugger that your prepareToPlay and getNextAudioBlock methods are actually being called? It may be possible the 1 in 2 out configuration isn’t working on your system with Juce but PortAudio somehow manages that automatically. You could try with 2,2 configuration instead in Juce.
All that said, there shouldn’t be any particular problem combining PortAudio and the Juce DSP code. You will probably want the PortAudio callback working with floating point audio I/O, so you don’t need to convert between floats and ints needlessly, as the Juce DSP classes have been designed to work with floating point audio.
Did I understand you correctly? Can I use the juce_dsp module together with PortAudio?
It seems you are misunderstanding how the AudioAppComponent works. It is an AudioSource and an AudioSourcePlayer already hooked up.
You only need to implement the getNextAudioBlock().
You find the audio input in the bufferToFill.buffer and you replace it with your output.
If you cannot hear yourself, make sure the AudioDeviceSelector selected a sensible output (I found it a couple of times picking the HDMI, where almost nobody has speakers hooked up).
Then make sure via breakpoint or DBG that getNextAudioBlock is actually called.
You can call juce_dsp from portaudio, but I would encourage you to figure out what’s wrong with your example. Bringing frameworks together is another point of failure, and since juce and portaudio overlap in their features, I would recommend to avoid it, unless there are stronger technical reasons.
EDIT: I forgot to mention: at no place you need to query the AudioDeviceManager, except maybe to store the actual number of input and output channels after the initializeAudio() call.
I don’t know anything about PortAudio, but assuming it has some way to access arrays of floats, then yes you can use objects from the juce_dsp module to process your audio.
Start with a juce::dsp::AudioBlock, using this constructor: JUCE: juce::dsp::AudioBlock< SampleType > Class Template Reference
I understand you. PortAudio is a library for audio I/O.
