Browse Source

Added support for handling sustain and sostenuto pedals to the Synthesiser class.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
be8f34282f
10 changed files with 393 additions and 146 deletions
  1. +147
    -58
      juce_amalgamated.cpp
  2. +48
    -15
      juce_amalgamated.h
  3. +4
    -4
      src/audio/audio_file_formats/juce_WavAudioFormat.cpp
  4. +2
    -2
      src/audio/dsp/juce_Reverb.h
  5. +15
    -0
      src/audio/midi/juce_MidiMessage.cpp
  6. +22
    -4
      src/audio/midi/juce_MidiMessage.h
  7. +124
    -47
      src/audio/synthesisers/juce_Synthesiser.cpp
  8. +25
    -8
      src/audio/synthesisers/juce_Synthesiser.h
  9. +1
    -1
      src/core/juce_StandardHeader.h
  10. +5
    -7
      src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp

+ 147
- 58
juce_amalgamated.cpp View File

@@ -24040,10 +24040,10 @@ private:

int64 riffChunkSize = 4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */
+ 8 + audioDataSize + (audioDataSize & 1)
+ (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0)
+ (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0)
+ (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0)
+ (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0)
+ (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0)
+ (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0)
+ (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0)
+ (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0)
+ (listChunk.getSize() > 0 ? (12 + listChunk.getSize()) : 0)
+ (8 + 28); // (ds64 chunk)

@@ -29662,6 +29662,15 @@ const MidiMessage MidiMessage::channelPressureChange (const int channel,
return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f);
}

bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && data[2] >= 64; }
bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && data[2] < 64; }

bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && data[2] >= 64; }
bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && data[2] < 64; }

bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && data[2] >= 64; }
bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && data[2] < 64; }

bool MidiMessage::isProgramChange() const noexcept
{
return (data[0] & 0xf0) == 0xc0;
@@ -29707,6 +29716,11 @@ bool MidiMessage::isController() const noexcept
return (data[0] & 0xf0) == 0xb0;
}

bool MidiMessage::isControllerOfType (const int controllerType) const noexcept
{
return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType;
}

int MidiMessage::getControllerNumber() const noexcept
{
jassert (isController());
@@ -38599,7 +38613,9 @@ SynthesiserSound::~SynthesiserSound()
SynthesiserVoice::SynthesiserVoice()
: currentSampleRate (44100.0),
currentlyPlayingNote (-1),
noteOnTime (0)
noteOnTime (0),
keyIsDown (false),
sostenutoPedalDown (false)
{
}

@@ -38729,46 +38745,47 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer,
}

if (useEvent)
{
if (m.isNoteOn())
{
const int channel = m.getChannel();

noteOn (channel,
m.getNoteNumber(),
m.getFloatVelocity());
}
else if (m.isNoteOff())
{
noteOff (m.getChannel(),
m.getNoteNumber(),
true);
}
else if (m.isAllNotesOff() || m.isAllSoundOff())
{
allNotesOff (m.getChannel(), true);
}
else if (m.isPitchWheel())
{
const int channel = m.getChannel();
const int wheelPos = m.getPitchWheelValue();
lastPitchWheelValues [channel - 1] = wheelPos;

handlePitchWheel (channel, wheelPos);
}
else if (m.isController())
{
handleController (m.getChannel(),
m.getControllerNumber(),
m.getControllerValue());
}
}
handleMidiEvent (m);

startSample += numThisTime;
numSamples -= numThisTime;
}
}

void Synthesiser::handleMidiEvent (const MidiMessage& m)
{
if (m.isNoteOn())
{
noteOn (m.getChannel(),
m.getNoteNumber(),
m.getFloatVelocity());
}
else if (m.isNoteOff())
{
noteOff (m.getChannel(),
m.getNoteNumber(),
true);
}
else if (m.isAllNotesOff() || m.isAllSoundOff())
{
allNotesOff (m.getChannel(), true);
}
else if (m.isPitchWheel())
{
const int channel = m.getChannel();
const int wheelPos = m.getPitchWheelValue();
lastPitchWheelValues [channel - 1] = wheelPos;

handlePitchWheel (channel, wheelPos);
}
else if (m.isController())
{
handleController (m.getChannel(),
m.getControllerNumber(),
m.getControllerValue());
}
}

void Synthesiser::noteOn (const int midiChannel,
const int midiNoteNumber,
const float velocity)
@@ -38782,6 +38799,17 @@ void Synthesiser::noteOn (const int midiChannel,
if (sound->appliesToNote (midiNoteNumber)
&& sound->appliesToChannel (midiChannel))
{
// If hitting a note that's still ringing, stop it first (it could be
// still playing because of the sustain or sostenuto pedal).
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);

if (voice->getCurrentlyPlayingNote() == midiNoteNumber
&& voice->isPlayingChannel (midiChannel))
stopVoice (voice, true);
}

startVoice (findFreeVoice (sound, shouldStealNotes),
sound, midiChannel, midiNoteNumber, velocity);
}
@@ -38799,17 +38827,27 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice,
if (voice->currentlyPlayingSound != nullptr)
voice->stopNote (false);

voice->startNote (midiNoteNumber,
velocity,
sound,
voice->startNote (midiNoteNumber, velocity, sound,
lastPitchWheelValues [midiChannel - 1]);

voice->currentlyPlayingNote = midiNoteNumber;
voice->noteOnTime = ++lastNoteOnCounter;
voice->currentlyPlayingSound = sound;
voice->keyIsDown = true;
voice->sostenutoPedalDown = false;
}
}

void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff)
{
jassert (voice != nullptr);

voice->stopNote (allowTailOff);

// the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
}

void Synthesiser::noteOff (const int midiChannel,
const int midiNoteNumber,
const bool allowTailOff)
@@ -38828,17 +38866,16 @@ void Synthesiser::noteOff (const int midiChannel,
&& sound->appliesToNote (midiNoteNumber)
&& sound->appliesToChannel (midiChannel))
{
voice->stopNote (allowTailOff);
voice->keyIsDown = false;

// the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
stopVoice (voice, allowTailOff);
}
}
}
}

void Synthesiser::allNotesOff (const int midiChannel,
const bool allowTailOff)
void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
{
const ScopedLock sl (lock);

@@ -38849,10 +38886,11 @@ void Synthesiser::allNotesOff (const int midiChannel,
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->stopNote (allowTailOff);
}

sustainPedalsDown.clear();
}

void Synthesiser::handlePitchWheel (const int midiChannel,
const int wheelValue)
void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
{
const ScopedLock sl (lock);

@@ -38861,9 +38899,7 @@ void Synthesiser::handlePitchWheel (const int midiChannel,
SynthesiserVoice* const voice = voices.getUnchecked (i);

if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
{
voice->pitchWheelMoved (wheelValue);
}
}
}

@@ -38871,6 +38907,14 @@ void Synthesiser::handleController (const int midiChannel,
const int controllerNumber,
const int controllerValue)
{
switch (controllerNumber)
{
case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
default: break;
}

const ScopedLock sl (lock);

for (int i = voices.size(); --i >= 0;)
@@ -38882,6 +38926,53 @@ void Synthesiser::handleController (const int midiChannel,
}
}

void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
{
jassert (midiChannel > 0 && midiChannel <= 16);
const ScopedLock sl (lock);

if (isDown)
{
sustainPedalsDown.setBit (midiChannel);
}
else
{
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);

if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
stopVoice (voice, true);
}

sustainPedalsDown.clearBit (midiChannel);
}
}

void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
{
jassert (midiChannel > 0 && midiChannel <= 16);
const ScopedLock sl (lock);

for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);

if (voice->isPlayingChannel (midiChannel))
{
if (isDown)
voice->sostenutoPedalDown = true;
else if (voice->sostenutoPedalDown)
stopVoice (voice, true);
}
}
}

void Synthesiser::handleSoftPedal (int midiChannel, bool isDown)
{
jassert (midiChannel > 0 && midiChannel <= 16);
}

SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
const bool stealIfNoneAvailable) const
{
@@ -74836,16 +74927,14 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager&
jassert (minOutputChannels >= 0 && minOutputChannels <= maxOutputChannels);
jassert (minInputChannels >= 0 && minInputChannels <= maxInputChannels);

if (deviceManager_.getAvailableDeviceTypes().size() > 1)
const OwnedArray<AudioIODeviceType>& types = deviceManager_.getAvailableDeviceTypes();

if (types.size() > 1)
{
deviceTypeDropDown = new ComboBox (String::empty);

for (int i = 0; i < deviceManager_.getAvailableDeviceTypes().size(); ++i)
{
deviceTypeDropDown
->addItem (deviceManager_.getAvailableDeviceTypes().getUnchecked(i)->getTypeName(),
i + 1);
}
for (int i = 0; i < types.size(); ++i)
deviceTypeDropDown->addItem (types.getUnchecked(i)->getTypeName(), i + 1);

addAndMakeVisible (deviceTypeDropDown);
deviceTypeDropDown->addListener (this);


+ 48
- 15
juce_amalgamated.h View File

@@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
#define JUCE_BUILDNUMBER 92
#define JUCE_BUILDNUMBER 93

/** Current Juce version number.

@@ -39379,7 +39379,7 @@ private:
void clear() noexcept
{
last = 0;
zeromem (buffer, bufferSize * sizeof (float));
buffer.clear (bufferSize);
}

void setFeedbackAndDamp (const float f, const float d) noexcept
@@ -39429,7 +39429,7 @@ private:

void clear() noexcept
{
zeromem (buffer, bufferSize * sizeof (float));
buffer.clear (bufferSize);
}

inline float process (const float input) noexcept
@@ -39932,8 +39932,7 @@ public:

/** Returns the midi note number for note-on and note-off messages.

If the message isn't a note-on or off, the value returned will be
meaningless.
If the message isn't a note-on or off, the value returned is undefined.

@see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber
*/
@@ -39948,7 +39947,6 @@ public:
/** Returns the velocity of a note-on or note-off message.

The value returned will be in the range 0 to 127.

If the message isn't a note-on or off event, it will return 0.

@see getFloatVelocity
@@ -39958,7 +39956,6 @@ public:
/** Returns the velocity of a note-on or note-off message.

The value returned will be in the range 0 to 1.0

If the message isn't a note-on or off event, it will return 0.

@see getVelocity, setVelocity
@@ -39983,6 +39980,21 @@ public:
*/
void multiplyVelocity (float scaleFactor) noexcept;

/** Returns true if this message is a 'sustain pedal down' controller message. */
bool isSustainPedalOn() const noexcept;
/** Returns true if this message is a 'sustain pedal up' controller message. */
bool isSustainPedalOff() const noexcept;

/** Returns true if this message is a 'sostenuto pedal down' controller message. */
bool isSostenutoPedalOn() const noexcept;
/** Returns true if this message is a 'sostenuto pedal up' controller message. */
bool isSostenutoPedalOff() const noexcept;

/** Returns true if this message is a 'soft pedal down' controller message. */
bool isSoftPedalOn() const noexcept;
/** Returns true if this message is a 'soft pedal up' controller message. */
bool isSoftPedalOff() const noexcept;

/** Returns true if the message is a program (patch) change message.

@see getProgramChangeNumber, getGMInstrumentName
@@ -40111,6 +40123,11 @@ public:
*/
int getControllerValue() const noexcept;

/** Returns true if this message is a controller message and if it has the specified
controller type.
*/
bool isControllerOfType (int controllerType) const noexcept;

/** Creates a controller message.

@param channel the midi channel, in the range 1 to 16
@@ -48727,6 +48744,8 @@ private:
int currentlyPlayingNote;
uint32 noteOnTime;
SynthesiserSound::Ptr currentlyPlayingSound;
bool keyIsDown; // the voice may still be playing when the key is not down (i.e. sustain pedal)
bool sostenutoPedalDown;

JUCE_LEAK_DETECTOR (SynthesiserVoice);
};
@@ -48831,6 +48850,8 @@ public:

This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.

The midiChannel parameter is the channel, between 1 and 16 inclusive.
*/
virtual void noteOn (int midiChannel,
int midiNoteNumber,
@@ -48845,6 +48866,8 @@ public:

This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.

The midiChannel parameter is the channel, between 1 and 16 inclusive.
*/
virtual void noteOff (int midiChannel,
int midiNoteNumber,
@@ -48855,7 +48878,8 @@ public:
This will turn off any voices that are playing a sound on the given midi channel.

If midiChannel is 0 or less, then all voices will be turned off, regardless of
which channel they're playing.
which channel they're playing. Otherwise it represents a valid midi channel, from
1 to 16 inclusive.

If allowTailOff is true, the voices will be allowed to fade out the notes gracefully
(if they can do). If this is false, the notes will all be cut off immediately.
@@ -48874,7 +48898,7 @@ public:
This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.

@param midiChannel the midi channel for the event
@param midiChannel the midi channel, from 1 to 16 inclusive
@param wheelValue the wheel position, from 0 to 0x3fff, as returned by MidiMessage::getPitchWheelValue()
*/
virtual void handlePitchWheel (int midiChannel,
@@ -48888,7 +48912,7 @@ public:
This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.

@param midiChannel the midi channel for the event
@param midiChannel the midi channel, from 1 to 16 inclusive
@param controllerNumber the midi controller type, as returned by MidiMessage::getControllerNumber()
@param controllerValue the midi controller value, between 0 and 127, as returned by MidiMessage::getControllerValue()
*/
@@ -48896,6 +48920,10 @@ public:
int controllerNumber,
int controllerValue);

virtual void handleSustainPedal (int midiChannel, bool isDown);
virtual void handleSostenutoPedal (int midiChannel, bool isDown);
virtual void handleSoftPedal (int midiChannel, bool isDown);

/** Tells the synthesiser what the sample rate is for the audio it's being used to
render.

@@ -48953,15 +48981,20 @@ protected:
int midiNoteNumber,
float velocity);

#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Temporary method here to cause a compiler error - note the new parameters for this method.
int findFreeVoice (const bool) const { return 0; }
#endif

private:

double sampleRate;
uint32 lastNoteOnCounter;
bool shouldStealNotes;
BigInteger sustainPedalsDown;

void handleMidiEvent (const MidiMessage& m);
void stopVoice (SynthesiserVoice* voice, bool allowTailOff);

#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for this method.
virtual int findFreeVoice (const bool) const { return 0; }
#endif

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser);
};


+ 4
- 4
src/audio/audio_file_formats/juce_WavAudioFormat.cpp View File

@@ -878,10 +878,10 @@ private:
int64 riffChunkSize = 4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */
+ 8 + audioDataSize + (audioDataSize & 1)
+ (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0)
+ (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0)
+ (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0)
+ (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0)
+ (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0)
+ (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0)
+ (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0)
+ (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0)
+ (listChunk.getSize() > 0 ? (12 + listChunk.getSize()) : 0)
+ (8 + 28); // (ds64 chunk)


+ 2
- 2
src/audio/dsp/juce_Reverb.h View File

@@ -242,7 +242,7 @@ private:
void clear() noexcept
{
last = 0;
zeromem (buffer, bufferSize * sizeof (float));
buffer.clear (bufferSize);
}
void setFeedbackAndDamp (const float f, const float d) noexcept
@@ -293,7 +293,7 @@ private:
void clear() noexcept
{
zeromem (buffer, bufferSize * sizeof (float));
buffer.clear (bufferSize);
}
inline float process (const float input) noexcept


+ 15
- 0
src/audio/midi/juce_MidiMessage.cpp View File

@@ -420,6 +420,16 @@ const MidiMessage MidiMessage::channelPressureChange (const int channel,
return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f);
}
bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && data[2] >= 64; }
bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && data[2] < 64; }
bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && data[2] >= 64; }
bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && data[2] < 64; }
bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && data[2] >= 64; }
bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && data[2] < 64; }
bool MidiMessage::isProgramChange() const noexcept
{
return (data[0] & 0xf0) == 0xc0;
@@ -465,6 +475,11 @@ bool MidiMessage::isController() const noexcept
return (data[0] & 0xf0) == 0xb0;
}
bool MidiMessage::isControllerOfType (const int controllerType) const noexcept
{
return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType;
}
int MidiMessage::getControllerNumber() const noexcept
{
jassert (isController());


+ 22
- 4
src/audio/midi/juce_MidiMessage.h View File

@@ -256,8 +256,7 @@ public:
/** Returns the midi note number for note-on and note-off messages.
If the message isn't a note-on or off, the value returned will be
meaningless.
If the message isn't a note-on or off, the value returned is undefined.
@see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber
*/
@@ -273,7 +272,6 @@ public:
/** Returns the velocity of a note-on or note-off message.
The value returned will be in the range 0 to 127.
If the message isn't a note-on or off event, it will return 0.
@see getFloatVelocity
@@ -283,7 +281,6 @@ public:
/** Returns the velocity of a note-on or note-off message.
The value returned will be in the range 0 to 1.0
If the message isn't a note-on or off event, it will return 0.
@see getVelocity, setVelocity
@@ -308,6 +305,22 @@ public:
*/
void multiplyVelocity (float scaleFactor) noexcept;
//==============================================================================
/** Returns true if this message is a 'sustain pedal down' controller message. */
bool isSustainPedalOn() const noexcept;
/** Returns true if this message is a 'sustain pedal up' controller message. */
bool isSustainPedalOff() const noexcept;
/** Returns true if this message is a 'sostenuto pedal down' controller message. */
bool isSostenutoPedalOn() const noexcept;
/** Returns true if this message is a 'sostenuto pedal up' controller message. */
bool isSostenutoPedalOff() const noexcept;
/** Returns true if this message is a 'soft pedal down' controller message. */
bool isSoftPedalOn() const noexcept;
/** Returns true if this message is a 'soft pedal up' controller message. */
bool isSoftPedalOff() const noexcept;
//==============================================================================
/** Returns true if the message is a program (patch) change message.
@@ -440,6 +453,11 @@ public:
*/
int getControllerValue() const noexcept;
/** Returns true if this message is a controller message and if it has the specified
controller type.
*/
bool isControllerOfType (int controllerType) const noexcept;
/** Creates a controller message.
@param channel the midi channel, in the range 1 to 16


+ 124
- 47
src/audio/synthesisers/juce_Synthesiser.cpp View File

@@ -43,7 +43,9 @@ SynthesiserSound::~SynthesiserSound()
SynthesiserVoice::SynthesiserVoice()
: currentSampleRate (44100.0),
currentlyPlayingNote (-1),
noteOnTime (0)
noteOnTime (0),
keyIsDown (false),
sostenutoPedalDown (false)
{
}
@@ -176,46 +178,47 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer,
}
if (useEvent)
{
if (m.isNoteOn())
{
const int channel = m.getChannel();
noteOn (channel,
m.getNoteNumber(),
m.getFloatVelocity());
}
else if (m.isNoteOff())
{
noteOff (m.getChannel(),
m.getNoteNumber(),
true);
}
else if (m.isAllNotesOff() || m.isAllSoundOff())
{
allNotesOff (m.getChannel(), true);
}
else if (m.isPitchWheel())
{
const int channel = m.getChannel();
const int wheelPos = m.getPitchWheelValue();
lastPitchWheelValues [channel - 1] = wheelPos;
handlePitchWheel (channel, wheelPos);
}
else if (m.isController())
{
handleController (m.getChannel(),
m.getControllerNumber(),
m.getControllerValue());
}
}
handleMidiEvent (m);
startSample += numThisTime;
numSamples -= numThisTime;
}
}
void Synthesiser::handleMidiEvent (const MidiMessage& m)
{
if (m.isNoteOn())
{
noteOn (m.getChannel(),
m.getNoteNumber(),
m.getFloatVelocity());
}
else if (m.isNoteOff())
{
noteOff (m.getChannel(),
m.getNoteNumber(),
true);
}
else if (m.isAllNotesOff() || m.isAllSoundOff())
{
allNotesOff (m.getChannel(), true);
}
else if (m.isPitchWheel())
{
const int channel = m.getChannel();
const int wheelPos = m.getPitchWheelValue();
lastPitchWheelValues [channel - 1] = wheelPos;
handlePitchWheel (channel, wheelPos);
}
else if (m.isController())
{
handleController (m.getChannel(),
m.getControllerNumber(),
m.getControllerValue());
}
}
//==============================================================================
void Synthesiser::noteOn (const int midiChannel,
const int midiNoteNumber,
@@ -230,6 +233,17 @@ void Synthesiser::noteOn (const int midiChannel,
if (sound->appliesToNote (midiNoteNumber)
&& sound->appliesToChannel (midiChannel))
{
// If hitting a note that's still ringing, stop it first (it could be
// still playing because of the sustain or sostenuto pedal).
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
&& voice->isPlayingChannel (midiChannel))
stopVoice (voice, true);
}
startVoice (findFreeVoice (sound, shouldStealNotes),
sound, midiChannel, midiNoteNumber, velocity);
}
@@ -247,17 +261,27 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice,
if (voice->currentlyPlayingSound != nullptr)
voice->stopNote (false);
voice->startNote (midiNoteNumber,
velocity,
sound,
voice->startNote (midiNoteNumber, velocity, sound,
lastPitchWheelValues [midiChannel - 1]);
voice->currentlyPlayingNote = midiNoteNumber;
voice->noteOnTime = ++lastNoteOnCounter;
voice->currentlyPlayingSound = sound;
voice->keyIsDown = true;
voice->sostenutoPedalDown = false;
}
}
void Synthesiser::stopVoice (SynthesiserVoice* voice, const bool allowTailOff)
{
jassert (voice != nullptr);
voice->stopNote (allowTailOff);
// the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
}
void Synthesiser::noteOff (const int midiChannel,
const int midiNoteNumber,
const bool allowTailOff)
@@ -276,17 +300,16 @@ void Synthesiser::noteOff (const int midiChannel,
&& sound->appliesToNote (midiNoteNumber)
&& sound->appliesToChannel (midiChannel))
{
voice->stopNote (allowTailOff);
voice->keyIsDown = false;
// the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
stopVoice (voice, allowTailOff);
}
}
}
}
void Synthesiser::allNotesOff (const int midiChannel,
const bool allowTailOff)
void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
{
const ScopedLock sl (lock);
@@ -297,10 +320,11 @@ void Synthesiser::allNotesOff (const int midiChannel,
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->stopNote (allowTailOff);
}
sustainPedalsDown.clear();
}
void Synthesiser::handlePitchWheel (const int midiChannel,
const int wheelValue)
void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
{
const ScopedLock sl (lock);
@@ -309,9 +333,7 @@ void Synthesiser::handlePitchWheel (const int midiChannel,
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
{
voice->pitchWheelMoved (wheelValue);
}
}
}
@@ -319,6 +341,14 @@ void Synthesiser::handleController (const int midiChannel,
const int controllerNumber,
const int controllerValue)
{
switch (controllerNumber)
{
case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
default: break;
}
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
@@ -330,6 +360,53 @@ void Synthesiser::handleController (const int midiChannel,
}
}
void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
{
jassert (midiChannel > 0 && midiChannel <= 16);
const ScopedLock sl (lock);
if (isDown)
{
sustainPedalsDown.setBit (midiChannel);
}
else
{
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
stopVoice (voice, true);
}
sustainPedalsDown.clearBit (midiChannel);
}
}
void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
{
jassert (midiChannel > 0 && midiChannel <= 16);
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->isPlayingChannel (midiChannel))
{
if (isDown)
voice->sostenutoPedalDown = true;
else if (voice->sostenutoPedalDown)
stopVoice (voice, true);
}
}
}
void Synthesiser::handleSoftPedal (int midiChannel, bool isDown)
{
jassert (midiChannel > 0 && midiChannel <= 16);
}
//==============================================================================
SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
const bool stealIfNoneAvailable) const


+ 25
- 8
src/audio/synthesisers/juce_Synthesiser.h View File

@@ -32,6 +32,7 @@
#include "../../memory/juce_ReferenceCountedObject.h"
#include "../../containers/juce_ReferenceCountedArray.h"
#include "../../threads/juce_CriticalSection.h"
#include "../../maths/juce_BigInteger.h"
//==============================================================================
@@ -232,6 +233,8 @@ private:
int currentlyPlayingNote;
uint32 noteOnTime;
SynthesiserSound::Ptr currentlyPlayingSound;
bool keyIsDown; // the voice may still be playing when the key is not down (i.e. sustain pedal)
bool sostenutoPedalDown;
JUCE_LEAK_DETECTOR (SynthesiserVoice);
};
@@ -342,6 +345,8 @@ public:
This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.
The midiChannel parameter is the channel, between 1 and 16 inclusive.
*/
virtual void noteOn (int midiChannel,
int midiNoteNumber,
@@ -356,6 +361,8 @@ public:
This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.
The midiChannel parameter is the channel, between 1 and 16 inclusive.
*/
virtual void noteOff (int midiChannel,
int midiNoteNumber,
@@ -366,7 +373,8 @@ public:
This will turn off any voices that are playing a sound on the given midi channel.
If midiChannel is 0 or less, then all voices will be turned off, regardless of
which channel they're playing.
which channel they're playing. Otherwise it represents a valid midi channel, from
1 to 16 inclusive.
If allowTailOff is true, the voices will be allowed to fade out the notes gracefully
(if they can do). If this is false, the notes will all be cut off immediately.
@@ -385,7 +393,7 @@ public:
This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.
@param midiChannel the midi channel for the event
@param midiChannel the midi channel, from 1 to 16 inclusive
@param wheelValue the wheel position, from 0 to 0x3fff, as returned by MidiMessage::getPitchWheelValue()
*/
virtual void handlePitchWheel (int midiChannel,
@@ -399,7 +407,7 @@ public:
This method will be called automatically according to the midi data passed into
renderNextBlock(), but may be called explicitly too.
@param midiChannel the midi channel for the event
@param midiChannel the midi channel, from 1 to 16 inclusive
@param controllerNumber the midi controller type, as returned by MidiMessage::getControllerNumber()
@param controllerValue the midi controller value, between 0 and 127, as returned by MidiMessage::getControllerValue()
*/
@@ -407,6 +415,10 @@ public:
int controllerNumber,
int controllerValue);
virtual void handleSustainPedal (int midiChannel, bool isDown);
virtual void handleSostenutoPedal (int midiChannel, bool isDown);
virtual void handleSoftPedal (int midiChannel, bool isDown);
//==============================================================================
/** Tells the synthesiser what the sample rate is for the audio it's being used to
render.
@@ -465,15 +477,20 @@ protected:
int midiNoteNumber,
float velocity);
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Temporary method here to cause a compiler error - note the new parameters for this method.
int findFreeVoice (const bool) const { return 0; }
#endif
private:
//==============================================================================
double sampleRate;
uint32 lastNoteOnCounter;
bool shouldStealNotes;
BigInteger sustainPedalsDown;
void handleMidiEvent (const MidiMessage& m);
void stopVoice (SynthesiserVoice* voice, bool allowTailOff);
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for this method.
virtual int findFreeVoice (const bool) const { return 0; }
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser);
};


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
#define JUCE_BUILDNUMBER 92
#define JUCE_BUILDNUMBER 93
/** Current Juce version number.


+ 5
- 7
src/gui/components/special/juce_AudioDeviceSelectorComponent.cpp View File

@@ -925,16 +925,14 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager&
jassert (minOutputChannels >= 0 && minOutputChannels <= maxOutputChannels);
jassert (minInputChannels >= 0 && minInputChannels <= maxInputChannels);
if (deviceManager_.getAvailableDeviceTypes().size() > 1)
const OwnedArray<AudioIODeviceType>& types = deviceManager_.getAvailableDeviceTypes();
if (types.size() > 1)
{
deviceTypeDropDown = new ComboBox (String::empty);
for (int i = 0; i < deviceManager_.getAvailableDeviceTypes().size(); ++i)
{
deviceTypeDropDown
->addItem (deviceManager_.getAvailableDeviceTypes().getUnchecked(i)->getTypeName(),
i + 1);
}
for (int i = 0; i < types.size(); ++i)
deviceTypeDropDown->addItem (types.getUnchecked(i)->getTypeName(), i + 1);
addAndMakeVisible (deviceTypeDropDown);
deviceTypeDropDown->addListener (this);


Loading…
Cancel
Save