I added a button to my midi tutorial code to adjust my synthesizer volume, and it had no effect. I hope you can help me see, thank you.
struct SineWaveSound : public juce::SynthesiserSound {
SineWaveSound() {}
bool appliesToNote(int) override { return true; }
bool appliesToChannel(int) override { return true; }
};
//==============================================================================
struct SineWaveVoice : public juce::SynthesiserVoice {
SineWaveVoice() {}
bool canPlaySound(juce::SynthesiserSound* sound) override {
return dynamic_cast<SineWaveSound*> (sound) != nullptr;
}
void startNote(int midiNoteNumber, float velocity, juce::SynthesiserSound*, int /*currentPitchWheelPosition*/) override {
currentAngle = 0.0;
level = velocity * 0.15;
tailOff = 0.0;
auto cyclesPerSecond = juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber);
auto cyclesPerSample = cyclesPerSecond / getSampleRate();
angleDelta = cyclesPerSample * 2.0 * juce::MathConstants<double>::pi;
}
void stopNote(float /*velocity*/, bool allowTailOff) override {
if (allowTailOff) {
if (tailOff == 0.0)
tailOff = 1.0;
}
else {
clearCurrentNote();
angleDelta = 0.0;
}
}
void pitchWheelMoved(int) override {}
void controllerMoved(int, int) override {}
void renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int startSample, int numSamples) override {
if (angleDelta != 0.0) {
if (tailOff > 0.0) {
while (--numSamples >= 0) {
auto currentSample = (float)(std::sin(currentAngle) * level * tailOff);
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample(i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
tailOff *= 0.99;
if (tailOff <= 0.005) {
clearCurrentNote();
angleDelta = 0.0;
break;
}
}
}
else {
while (--numSamples >= 0) {
auto currentSample = (float)(std::sin(currentAngle) * level);
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample(i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
}
}
}
}
void setLevel(float newLevel) {
level = newLevel * 0.15;
}
private:
double currentAngle = 0.0, angleDelta = 0.0, level = 0.0, tailOff = 0.0;
};
//==============================================================================
class SynthAudioSource : public juce::AudioSource {
public:
SynthAudioSource(juce::MidiKeyboardState& keyState) : keyboardState(keyState) {
for (auto i = 0; i < 4; ++i)
synth.addVoice(new SineWaveVoice());
synth.addSound(new SineWaveSound());
}
void setUsingSineWaveSound() {
synth.clearSounds();
}
void prepareToPlay(int /*samplesPerBlockExpected*/, double sampleRate) override {
synth.setCurrentPlaybackSampleRate(sampleRate);
}
void releaseResources() override {}
void getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill) override {
bufferToFill.clearActiveBufferRegion();
juce::MidiBuffer incomingMidi;
keyboardState.processNextMidiBuffer(incomingMidi, bufferToFill.startSample, bufferToFill.numSamples, true);
synth.renderNextBlock(*bufferToFill.buffer, incomingMidi, bufferToFill.startSample, bufferToFill.numSamples);
}
void setVolume(float newVolume) {
for (auto i = 0; i < synth.getNumVoices(); ++i) {
if (auto voice = dynamic_cast<SineWaveVoice*>(synth.getVoice(i))) {
voice->setLevel(newVolume);
}
}
}
private:
juce::MidiKeyboardState& keyboardState;
juce::Synthesiser synth;
};
//==============================================================================
class MainContentComponent : public juce::AudioAppComponent,
private juce::Timer,
public juce::Slider::Listener {
public:
MainContentComponent() : synthAudioSource(keyboardState),
keyboardComponent(keyboardState, juce::MidiKeyboardComponent::horizontalKeyboard) {
addAndMakeVisible(keyboardComponent);
setAudioChannels(0, 2);
volumeSlider.setRange(0.0, 1.0, 0.01);
volumeSlider.setValue(0.5);
volumeSlider.addListener(this);
addAndMakeVisible(volumeSlider);
volumeLabel.setText("Volume", juce::dontSendNotification);
addAndMakeVisible(volumeLabel);
setSize(600, 200);
startTimer(400);
}
~MainContentComponent() override {
shutdownAudio();
}
void resized() override {
auto area = getLocalBounds();
keyboardComponent.setBounds(area.removeFromTop(area.getHeight() - 50));
auto sliderArea = area.removeFromBottom(50).reduced(10);
volumeLabel.setBounds(sliderArea.removeFromLeft(80));
volumeSlider.setBounds(sliderArea);
}
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override {
synthAudioSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
}
void getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill) override {
synthAudioSource.getNextAudioBlock(bufferToFill);
}
void releaseResources() override {
synthAudioSource.releaseResources();
}
void sliderValueChanged(juce::Slider* slider) override {
if (slider == &volumeSlider) {
synthAudioSource.setVolume(volumeSlider.getValue());
}
}
private:
void timerCallback() override {
keyboardComponent.grabKeyboardFocus();
stopTimer();
}
juce::MidiKeyboardState keyboardState;
SynthAudioSource synthAudioSource;
juce::MidiKeyboardComponent keyboardComponent;
juce::Slider volumeSlider;
juce::Label volumeLabel;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainContentComponent)
};
