I would like to make a synthesiser play some notes in an “artificial” way.
Let’s say I have the following list of values: juce::Array<int> notesToPlay = (46, 98, 55, 21, 120, 46, 34, 23, 65). I would like to pass these values to a synthesiser in order to play them.
Because MidiMessage::noteOn is a static function you need to do
auto midiMessage = MidiMessage::noteOn(10, notesToPlay[i], 1.0f);
And furthermore, while you’re using midi channel 10, which is designated to drums/rhytm instruments, there’s an even bigger chance you’ll hear something if you limit the values in notesToPlay to between 35 and 81. MidiMessage::getRhythmInstrumentName will give you a clue.
class MidiSource : public juce::AudioProcessor
{
public:
MidiSource()
{
keyStatePtr.reset(new juce::MidiKeyboardState());
for (auto i = 0; i < 5; i++)
synth.addVoice(new SineWaveVoice());
synth.addSound(new SineWaveSound());
notesToPlay.add(56, 76, 65, 77, 81, 45);
playNotes();
}
/===========================================================/
juce::Synthesiser synth;
std::unique_ptr<juce::MidiKeyboardState> keyStatePtr;
}
Then, I call the function playNotes() which is in charge of populating a MidiBuffer with the notes to be played:
void playNote()
{
juce::MidiBuffer myBuff;
int time;
for (auto note : notesToPlay)
{
juce::MidiMessage myMessage;
myMessage = juce::MidiMessage::noteOn(1, note, (std::uint8_t)1.0);
myMessage.setTimeStamp(time);
myBuff.addEvent(myMessage, time);
juce::MidiMessage mySecondMessage;
mySecondMessage = juce::MidiMessage::noteOff(1, note, (std::uint8_t)1.0);
mySecondMessage.setTimeStamp(time + 1000);
time = time + 100;
myBuff.addEvent(mySecondMessage, time);
time = time + 100;
}
Finally, I would like to reproduce this buffer by my processBlock. In the previous post, I tried to use a MidiMessageSequence, (i.e.: juce::MidiMessageSequence msgSeq in the previous messages), but with no results
I’d suggest you start with just one noteOn message and follow it’s way in the processBlock in the debugger. When you see that’s working, add a noteOn, and so on…
By the way, there’s two versions of MidiMessage::noteOn, noteOn(uint8 velocity) and noteOn(float velocity). They take 127 resp 1.0f to render at full velocity. Your code produces a note of the absolute minimum velocity/volume possible, 1 /127, which can be a reason you don’t hear much…
And try a midi note higher than 1, e.g. 60. 1 is so low it’s barely reproducible in ordinary audio equipment.
I checked my code and actually I’m using this value for velocity: (std::uint8_t)1.0, both for noteOn and noteOff methods. As a matter of fact, if I want to reproduce a single note, I can hear the note really loud.
The problem is that: I’m not able to reproduce the entire sequence of MIDI notes declared inside the buffer juce::MidiBuffer myBuff
Yeap, it’s also depending on if your synth is velocity sensitive or not. If it’s not, it doesn’t matter much if you feed it with 127 or 1, as long as it’s not 0
I can’t follow his logic on that, but that could be a shortcoming of mine.
One common mistake people make when experimenting with midi is to shove a ton of messages up a single processBlock.
They’re lucky if a single midi message survives.
A processBlock is typically called every 10:th millisecond.
If you add a noteOn in a processBlock call you have to wait another 100 processBlock calls before
it’s time to add the corresponding notOff if you want the tone to last a second. Typically.
It won’t work to inject a noteOn and its noteOff in the same processBlcok call.
This means you have to implement a process call counter of some kind (or a timer, but that’s never gonna be precise enough for pratical use) to keep track of the time so you can inject the midi events at proper time inteval.
Hi @balthus89, I had to do something similar recently for testing some synths. Here’s my processBlock, the first parameter is a slider that enables playback if its value is 1.
void MIDIGenerator::processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer& midiBuffer)
{
int numSamples = buffer.getNumSamples();
if(getParameters()[0]->getValue() == 1)
{
for (int i = 0; i < numSamples; i++, sampleIndex++)
{
int note = Random::getSystemRandom().nextInt(Range<int>(60, 72));
if (sampleIndex == 1)
{
midiBuffer.addEvent(MidiMessage::noteOff(1, lastMidiNote, 0.f), 0); //this event is within our time slot.
midiBuffer.addEvent(MidiMessage::noteOn(1, note, .7f), 0); //this event is within our time slot.
lastMidiNote = note;
}
sampleIndex = sampleIndex > 44100 ? 0 : sampleIndex + 1;
}
}
}
My example was just a quick hack for a MIDI output plugin. It wasn’t really meant to be wrapped up as a synth voice. I’m sorry, but I don’t have to time to delve further into this.