void CPiano::addSavedChord(CMidiEvent midiNote, CChord chord) { int key = midiNote.note(); for (unsigned int i = 0; i < arraySize(m_savedChordLookUp); i++) { if (midiNote.type() == MIDI_NOTE_ON) { if (m_savedChordLookUp[i].pitchKey == 0 ) { m_savedChordLookUp[i].pitchKey = key; m_savedChordLookUp[i].savedNoteOffChord = chord; return; } } else if (midiNote.type() == MIDI_NOTE_OFF) { if (m_savedChordLookUp[i].pitchKey == key ) { m_savedChordLookUp[i].pitchKey = 0; return; } } } m_savedChordLookUp[0].savedNoteOffChord = chord; }
/** * Add in the extra notes in rhythm practice */ void CConductor::expandPianistInput(CMidiEvent inputNote) { if (m_playMode == PB_PLAY_MODE_rhythmTapping) { CChord chord; int i; CMidiEvent newNote = inputNote; if (inputNote.type() == MIDI_NOTE_ON || inputNote.type() == MIDI_NOTE_OFF) { chord = m_wantedChord; CChord chordForOneHand; int notesFound = 0; if (inputNote.type() == MIDI_NOTE_OFF) { chord = m_piano->removeSavedChord(inputNote.note()); for(i = 0; i < chord.length(); i++) { inputNote.setNote( chord.getNote(i).pitch()); pianistInput(inputNote); } // We have already played and removed this chord if (chord.length() > 0) return; } whichPart_t targetPart = (inputNote.note() >= MIDDLE_C) ? PB_PART_right : PB_PART_left; for(i = 0; i < chord.length(); i++) { if (chord.getNote(i).part() == targetPart && playingMusic()) { newNote.setNote(chord.getNote(i).pitch()); if ( notesFound >= 1 && cfg_rhythmTapping == PB_RHYTHM_TAP_drumsOnly) newNote.setVelocity(-1); else chordForOneHand.addNote(targetPart, chord.getNote(i).pitch()); pianistInput(newNote); notesFound++; } } if (notesFound > 0) m_piano->addSavedChord(inputNote, chordForOneHand); else { inputNote.setChannel(MIDI_DRUM_CHANNEL); pianistInput(inputNote); } } else { pianistInput(inputNote); } } else { pianistInput(inputNote); } }
eventBits_t CSong::task(int ticks) { realTimeEngine(ticks); while (true) { if (m_reachedMidiEof == true) goto exitTask; while (true) { // Check that there is space if (midiEventSpace() <= 10 || chordEventSpace() <= 10) break; // and that the Score has space also if (m_scoreWin->midiEventSpace() <= 100) break; // Read the next events CMidiEvent event = m_midiFile->readMidiEvent(); //ppLogTrace("Song event delta %d type 0x%x chan %d Note %d", event.deltaTime(), event.type(), event.channel(), event.note()); // Find the next chord if (m_findChord.findChord(event, getActiveChannel(), PB_PART_both) == true) chordEventInsert( m_findChord.getChord() ); // give the Conductor the chord event // send the events to the other end m_scoreWin->midiEventInsert(event); // send the events to the other end midiEventInsert(event); if (event.type() == MIDI_PB_EOF) { m_reachedMidiEof = true; break; } } // carry on with the data until we reach the bar we want if (seekingBarNumber() && m_reachedMidiEof == false && playingMusic()) { realTimeEngine(0); m_scoreWin->drawScrollingSymbols(false); // don't display any thing just remove from the queue } else break; } exitTask: eventBits_t eventBits = m_realTimeEventBits; m_realTimeEventBits = 0; return eventBits; }
// read the file ahead to collect info about the song first void CSong::midiFileInfo() { m_trackList->clear(); setTimeSig(0,0); CStavePos::setKeySignature( NOT_USED, 0 ); // Read the next events to find the active channels CMidiEvent event; while ( true ) { event = m_midiFile->readMidiEvent(); m_trackList->examineMidiEvent(event); if (event.type() == MIDI_PB_timeSignature) { setTimeSig(event.data1(),event.data2()); } if (event.type() == MIDI_PB_EOF) break; } }
void CTrackList::examineMidiEvent(CMidiEvent event) { int chan; chan = event.channel(); assert (chan < MAX_MIDI_CHANNELS && chan >= 0); if (chan < MAX_MIDI_CHANNELS && chan >= 0) { if (event.type() == MIDI_NOTE_ON) { m_midiActiveChannels[chan] = true; // count each note so we can guess the key signature if (event.note() >= 0 && event.note() < MAX_MIDI_NOTES) m_noteFrequency[chan][event.note()]++; // If we have a note and no patch then default to grand piano patch if (m_midiFirstPatchChannels[chan] == -1) m_midiFirstPatchChannels[chan] = GM_PIANO_PATCH; } if (event.type() == MIDI_PROGRAM_CHANGE && m_midiActiveChannels[chan] == false) m_midiFirstPatchChannels[chan] = event.programme(); } }
// untangle the sound in case there is any notes off just after we have stopped void CConductor::findImminentNotesOff() { int i; CMidiEvent event; int aheadDelta = 0; i = 0; event = m_nextMidiEvent; while (deltaAdjust(m_playingDeltaTime) + aheadDelta > m_cfg_imminentNotesOffPoint) { if (event.type() == MIDI_NOTE_OFF ) m_savedNoteOffQueue->push(event); if ( i >= m_songEventQueue->length()) break; event = m_songEventQueue->index(i); aheadDelta -= event.deltaTime(); i++; } }
void CConductor::playTransposeEvent(CMidiEvent event) { if (m_transpose != 0 && event.channel() != MIDI_DRUM_CHANNEL && (event.type() == MIDI_NOTE_ON || event.type() == MIDI_NOTE_OFF) ) event.transpose(m_transpose); if (event.type() == MIDI_NOTE_ON && isChannelMuted(event.channel()) == true && CChord::isNotePlayable(event.note(), m_transpose) == true) return; // mute the note by not playing it // boost any volume events if (event.type() == MIDI_CONTROL_CHANGE && event.data1() == MIDI_MAIN_VOLUME) event.setDatat2(calcBoostVolume(event.channel(), event.data2() )); // Don't output note on if we are seeking to bar if (!seekingBarNumber()) playTrackEvent(event); // Play the midi note or event else { if (event.type() == MIDI_PROGRAM_CHANGE || event.type() == MIDI_CONTROL_CHANGE) playTrackEvent(event); // Play the midi note or event } }
void CConductor::pianistInput(CMidiEvent inputNote) { bool goodSound = true; // inputNote.transpose(+12); fixme if (m_testWrongNoteSound) goodSound = false; whichPart_t hand; hand = (inputNote.note() >= m_pianistSplitPoint)? PB_PART_right : PB_PART_left; // for rhythm tapping if ( inputNote.channel() == MIDI_DRUM_CHANNEL) hand = (inputNote.note() >= MIDDLE_C) ? PB_PART_right : PB_PART_left; if (inputNote.type() == MIDI_NOTE_ON) { if ( validatePianistNote(inputNote) == true) { m_goodPlayedNotes.addNote(hand, inputNote.note()); m_piano->addPianistNote(hand, inputNote,true); int pianistTiming; if ( ( cfg_timingMarkersFlag && m_followSkillAdvanced ) || m_playMode == PB_PLAY_MODE_rhythmTapping ) pianistTiming = m_pianistTiming; else pianistTiming = NOT_USED; m_scoreWin->setPlayedNoteColour(inputNote.note(), (!m_followPlayingTimeOut)? Cfg::playedGoodColour():Cfg::playedBadColour(), m_chordDeltaTime, pianistTiming); if (validatePianistChord() == true) { if (m_chordDeltaTime < 0) m_tempo.removePlayingTicks(-m_chordDeltaTime); m_goodPlayedNotes.clear(); fetchNextChord(); // count the good notes so that the live percentage looks OK m_rating.totalNotes(m_wantedChord.length()); m_rating.calculateAccuracy(); m_settings->pianistActive(); if (m_rating.isAccuracyGood() || m_playMode == PB_PLAY_MODE_playAlong) setFollowSkillAdvanced(true); // change the skill level only when they are good enough else setFollowSkillAdvanced(false); setEventBits( EVENT_BITS_forceRatingRedraw); } } else { if (m_playing == true) { goodSound = false; m_piano->addPianistNote(hand, inputNote, false); m_rating.wrongNotes(1); } else m_piano->addPianistNote(hand, inputNote, true); } } else if (inputNote.type() == MIDI_NOTE_OFF) { if (m_piano->removePianistNote(inputNote.note()) == true) goodSound = false; bool hasNote = m_goodPlayedNotes.removeNote(inputNote.note()); if (hasNote) m_scoreWin->setPlayedNoteColour(inputNote.note(), (!m_followPlayingTimeOut)? Cfg::noteColour():Cfg::playedStoppedColour(), m_chordDeltaTime); outputSavedNotesOff(); } if ( inputNote.velocity() == -1 ) return; if (goodSound == true || m_cfg_wrongNoteSound < 0) { if (m_cfg_rightNoteSound >= 0) // don't play anything if the sound is set to -1 (none) { bool playDrumBeat = false; if ( inputNote.channel() != MIDI_DRUM_CHANNEL) { if (cfg_rhythmTapping != PB_RHYTHM_TAP_drumsOnly || m_playMode != PB_PLAY_MODE_rhythmTapping) { inputNote.setChannel(m_pianistGoodChan); playTrackEvent( inputNote ); } } else { playDrumBeat = true; } if (cfg_rhythmTapping != PB_RHYTHM_TAP_mellodyOnly && m_playMode == PB_PLAY_MODE_rhythmTapping) playDrumBeat = true; if (playDrumBeat) { inputNote.setChannel(MIDI_DRUM_CHANNEL); ppLogTrace("note %d", inputNote.note()); inputNote.setNote((hand == PB_PART_right)? m_cfg_rhythmTapRightHandDrumSound : m_cfg_rhythmTapLeftHandDrumSound); playTrackEvent( inputNote ); } } } else { inputNote.setChannel(m_pianistBadChan); if (m_playMode == PB_PLAY_MODE_rhythmTapping) { inputNote.setChannel(MIDI_DRUM_CHANNEL); ppLogTrace("note %d", inputNote.note()); inputNote.setNote((hand == PB_PART_right)? m_cfg_rhythmTapRightHandDrumSound : m_cfg_rhythmTapLeftHandDrumSound); } playTrackEvent( inputNote ); } /* // use the same channel for the right and wrong note int pianoSound = (goodSound == true) ? m_cfg_rightNoteSound : m_cfg_wrongNoteSound; if (pianoSound != m_lastSound) { m_lastSound = pianoSound; CMidiEvent midiSound; midiSound.programChangeEvent(0,inputNote.channel(),pianoSound); playTrackEvent( midiSound ); } */ }
void CConductor::pianistInput(CMidiEvent inputNote) { bool goodSound = true; if (m_testWrongNoteSound) goodSound = false; if (inputNote.type() == MIDI_NOTE_ON) { whichPart_t hand; hand = (inputNote.note() >= m_pianistSplitPoint)? PB_PART_right : PB_PART_left; if ( validatePianistNote(inputNote) == true) { m_goodPlayedNotes.addNote(hand, inputNote.note()); m_piano->addPianistNote(hand, inputNote.note(),true); int pianistTiming = ( cfg_timingMarkersFlag && m_followSkillAdvanced) ? m_pianistTiming : NOT_USED; m_scoreWin->setPlayedNoteColour(inputNote.note(), (!m_followPlayingTimeOut)? Cfg::playedGoodColour():Cfg::playedBadColour(), m_chordDeltaTime, pianistTiming); if (validatePianistChord() == true) { if (m_chordDeltaTime < 0) m_tempo.removePlayingTicks(-m_chordDeltaTime); m_goodPlayedNotes.clear(); fetchNextChord(); // count the good notes so that the live percentage looks OK m_rating.totalNotes(m_wantedChord.length()); m_rating.calculateAccuracy(); m_settings->pianistActive(); if (m_rating.isAccuracyGood() || m_playMode == PB_PLAY_MODE_playAlong) setFollowSkillAdvanced(true); // change the skill level only when they are good enough else setFollowSkillAdvanced(false); setEventBits( EVENT_BITS_forceRatingRedraw); } } else { if (m_playing == true) { goodSound = false; m_piano->addPianistNote(hand, inputNote.note(), false); m_rating.wrongNotes(1); } else m_piano->addPianistNote(hand, inputNote.note(), true); } } else if (inputNote.type() == MIDI_NOTE_OFF) { if (m_piano->removePianistNote(inputNote.note()) == true) goodSound = false; bool hasNote = m_goodPlayedNotes.removeNote(inputNote.note()); if (hasNote) m_scoreWin->setPlayedNoteColour(inputNote.note(), (!m_followPlayingTimeOut)? Cfg::noteColour():Cfg::playedStoppedColour(), m_chordDeltaTime); outputSavedNotesOff(); } if (goodSound == true || m_cfg_wrongNoteSound < 0) { if (m_cfg_rightNoteSound >= 0) // don't play anything if the sound is set to -1 (none) { inputNote.setChannel(m_pianistGoodChan); playMidiEvent( inputNote ); } } else { inputNote.setChannel(m_pianistBadChan); playMidiEvent( inputNote ); } /* // use the same channel for the right and wrong note int pianoSound = (goodSound == true) ? m_cfg_rightNoteSound : m_cfg_wrongNoteSound; if (pianoSound != m_lastSound) { m_lastSound = pianoSound; CMidiEvent midiSound; midiSound.programChangeEvent(0,inputNote.channel(),pianoSound); playMidiEvent( midiSound ); } */ }
//! add a midi event to be played immediately void CMidiDeviceRt::playMidiEvent(const CMidiEvent & event) { if (m_midiPorts[1] < 0) return; unsigned int channel; std::vector<unsigned char> message; channel = event.channel() & 0x0f; switch(event.type()) { case MIDI_NOTE_OFF: // NOTE_OFF message.push_back( channel | MIDI_NOTE_OFF ); message.push_back( event.note()); message.push_back( event.velocity()); break; case MIDI_NOTE_ON: // NOTE_ON message.push_back( channel | MIDI_NOTE_ON ); message.push_back( event.note()); message.push_back( event.velocity()); break; case MIDI_NOTE_PRESSURE: //POLY_AFTERTOUCH: 3 bytes message.push_back( channel | MIDI_NOTE_PRESSURE); message.push_back( event.data1()); message.push_back( event.data2()); break; case MIDI_CONTROL_CHANGE: //CONTROL_CHANGE: message.push_back( channel | MIDI_CONTROL_CHANGE); message.push_back( event.data1()); message.push_back( event.data2()); break; case MIDI_PROGRAM_CHANGE: //PROGRAM_CHANGE: message.push_back( channel | MIDI_PROGRAM_CHANGE); message.push_back( event.programme()); break; case MIDI_CHANNEL_PRESSURE: //AFTERTOUCH: 2 bytes only message.push_back( channel | MIDI_CHANNEL_PRESSURE); message.push_back( event.data1()); break; case MIDI_PITCH_BEND: //PITCH_BEND: message.push_back( channel | MIDI_PITCH_BEND); message.push_back( event.data1()); message.push_back( event.data2()); break; case MIDI_PB_collateRawMidiData: //used for a SYSTEM_EVENT if (m_rawDataIndex < arraySize(m_savedRawBytes)) m_savedRawBytes[m_rawDataIndex++] = event.data1(); return; // Don't output any thing yet so just return case MIDI_PB_outputRawMidiData: //used for a SYSTEM_EVENT for (size_t i = 0; i < m_rawDataIndex; i++) message.push_back( m_savedRawBytes[i]); m_rawDataIndex = 0; break; } m_midiout->sendMessage( &message ); //event.printDetails(); // useful for debugging }