pianobooster/src/Conductor.h

319 lines
9.8 KiB
C++

/*********************************************************************************/
/*!
@file Conductor.h
@brief The realtime midi engine. Send notes to the midi device and responds to the midi keyboard.
@author L. J. Barman
Copyright (c) 2008-2013, L. J. Barman, all rights reserved
This file is part of the PianoBooster application
PianoBooster is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PianoBooster is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PianoBooster. If not, see <http://www.gnu.org/licenses/>.
*/
/*********************************************************************************/
#ifndef __CONDUCTOR_H__
#define __CONDUCTOR_H__
#include "MidiEvent.h"
#include "Queue.h"
#include "MidiDevice.h"
#include "Cfg.h"
#include "Chord.h"
#include "Rating.h"
#include "Tempo.h"
#include "Bar.h"
class CScore;
class CPiano;
class CSettings;
typedef enum {
PB_FOLLOW_searching,
PB_FOLLOW_earlyNotes,
PB_FOLLOW_waiting
} followState_t;
enum {
PB_PLAY_MODE_listen,
PB_PLAY_MODE_rhythmTapping,
PB_PLAY_MODE_followYou,
PB_PLAY_MODE_playAlong
};
typedef int playMode_t;
typedef enum {
PB_RHYTHM_TAP_drumsOnly,
PB_RHYTHM_TAP_mellodyOnly,
PB_RHYTHM_TAP_drumsAndMellody
} rhythmTapping_t;
typedef enum {
PB_STOP_POINT_MODE_automatic,
PB_STOP_POINT_MODE_onTheBeat,
PB_STOP_POINT_MODE_afterTheBeat
} stopPointMode_t;
/*!
* @brief xxxxx.
*/
class CConductor : public CMidiDevice
{
public:
CConductor();
~CConductor();
void init2(CScore * scoreWin, CSettings* settings);
//! add a midi event to be analysed and played
void midiEventInsert(CMidiEvent event);
//! first check if there is space to add a midi event
int midiEventSpace();
//! add a chord to be played by the pianist
void chordEventInsert(CChord chord) {m_wantedChordQueue->push(chord);}
//! first check if there is space to add a chord event
int chordEventSpace() { return m_wantedChordQueue->space();}
void rewind();
//! rest the conductor between each song
void reset();
void realTimeEngine(qint64 mSecTicks);
void playMusic(bool start);
bool playingMusic() {return m_playing;}
void reconnectMidi();
float getSpeed() {return m_tempo.getSpeed();}
void setSpeed(float speed)
{
m_tempo.setSpeed(speed);
m_leadLagAdjust = m_tempo.mSecToTicks( -getLatencyFix() );
}
void setLatencyFix(int latencyFix)
{
m_latencyFix = latencyFix;
m_leadLagAdjust = m_tempo.mSecToTicks( -getLatencyFix());
}
int getLatencyFix() { return m_latencyFix; }
void transpose(int transpose);
int getTranspose() {return m_transpose;}
int getSkill() {return m_skill;}
void setSkill(int skill)
{
m_skill = skill;
if (m_skill > 5)
m_skill = 5;
if (m_skill < 0)
m_skill = 0;
}
void pianistInput(CMidiEvent inputNote);
void expandPianistInput(CMidiEvent inputNote);
void setPlayMode(playMode_t mode);
int getBoostVolume() {return m_boostVolume;}
void boostVolume(int boostVolume)
{
m_boostVolume = boostVolume;
if (m_boostVolume < -100 ) m_boostVolume = -100;
if (m_boostVolume > 100 ) m_boostVolume = 100;
outputBoostVolume();
}
int getPianoVolume() {return m_pianoVolume;}
void pianoVolume(int pianoVolume)
{
m_pianoVolume = pianoVolume;
if (m_pianoVolume < -100 ) m_pianoVolume = -100;
if (m_pianoVolume > 100 ) m_pianoVolume = 100;
outputBoostVolume();
}
static playMode_t getPlayMode() {return m_playMode;}
CChord getWantedChord() {return m_wantedChord;}
void setActiveHand(whichPart_t hand);
void setActiveChannel(int channel);
int getActiveChannel(){return m_activeChannel;}
void setPianistChannels(int goodChan, int badChan){
m_pianistGoodChan = goodChan;
m_pianistBadChan = badChan;
}
bool hasPianistKeyboardChannel(int chan) { return (m_pianistGoodChan == chan || m_pianistBadChan == chan ) ? true : false;}
bool shouldMutePianistPart() {return m_playMode != PB_PLAY_MODE_listen && m_mutePianistPart == true;}
CRating* getRating(){return &m_rating;}
// You MUST clear the time sig to 0 first before setting an new start time Sig
void setTimeSig(int top, int bottom) { m_bar.setTimeSig(top, bottom);}
void getTimeSig(int *top, int *bottom) {m_bar.getTimeSig(top, bottom);}
void testWrongNoteSound(bool enable);
// -1 means no sound -2 means ignore this parameter
void setPianoSoundPatches(int rightSound, int wrongSound, bool update = false){
m_cfg_rightNoteSound = rightSound;
if ( wrongSound != -2)
m_cfg_wrongNoteSound = wrongSound;
if (update)
updatePianoSounds();
}
void setEventBits(eventBits_t bits) { m_realTimeEventBits |= bits; } // don't change the other bits
// set to true to force the score to be redrawn
void forceScoreRedraw(){ setEventBits( EVENT_BITS_forceFullRedraw); }
int getBarNumber(){ return m_bar.getBarNumber();}
double getCurrentBarPos(){ return m_bar.getCurrentBarPos();}
void setPlayFromBar(double bar){ m_bar.setPlayFromBar(bar);}
void setPlayUptoBar(double bar){ m_bar.setPlayUptoBar(bar);}
double getPlayUptoBar(){ return m_bar.getPlayUptoBar();}
void setLoopingBars(double bars){ m_bar.setLoopingBars(bars);}
double getLoopingBars(){ return m_bar.getLoopingBars();}
void mutePianistPart(bool state);
void mapTrack2Channel(int trackNumber, int channelNumber)
{
m_track2ChannelLookUp[trackNumber] = channelNumber;
}
bool cfg_timingMarkersFlag;
stopPointMode_t cfg_stopPointMode;
rhythmTapping_t cfg_rhythmTapping;
protected:
CScore* m_scoreWin;
CSettings* m_settings;
CQueue<CMidiEvent>* m_songEventQueue;
CQueue<CChord>* m_wantedChordQueue;
eventBits_t m_realTimeEventBits; //used to signal real time events to the caller of task()
void outputSavedNotes();
void resetWantedChord();
void playWantedChord (CChord chord, CMidiEvent inputNote);
bool validatePianistNote( const CMidiEvent& inputNote);
bool validatePianistChord();
bool seekingBarNumber() { return m_bar.seekingBarNumber();}
void doneSeekingBarNumber() { m_bar.doneSeekingBarNumber(); }
int track2Channel(int track) {return m_track2ChannelLookUp[track];}
private:
void allSoundOff();
void resetAllChannels();
void outputBoostVolume();
void outputPianoVolume();
void channelSoundOff(int channel);
void findSplitPoint();
void fetchNextChord();
void playTransposeEvent(CMidiEvent event);
void playTrackEvent(CMidiEvent event);
void outputSavedNotesOff();
void findImminentNotesOff();
void updatePianoSounds();
void followPlaying();
void missedNotesColor(CColor color);
int calcBoostVolume(int chan, int volume);
void addDeltaTime(qint64 ticks);
void turnOnKeyboardLights(bool on);
qint64 m_playingDeltaTime;
qint64 m_chordDeltaTime;
bool m_playing;
int m_transpose; // the number of semitones to transpose the music
followState_t m_followState;
followState_t getfollowState()
{
if ( m_playMode == PB_PLAY_MODE_listen )
return PB_FOLLOW_searching;
return m_followState;
}
CRating m_rating;
CQueue<CMidiEvent>* m_savedNoteQueue;
CQueue<CMidiEvent>* m_savedNoteOffQueue;
CMidiEvent m_nextMidiEvent;
void setFollowSkillAdvanced(bool enable);
CPiano* m_piano;
CBar m_bar;
qint64 m_leadLagAdjust; // Synchronise the sound with the video
qint64 m_silenceTimeOut; // used to create silence if the student stops for toooo long
CChord m_wantedChord; // The chord the pianist needs to play
CChord m_savedWantedChord; // A copy of the wanted chord complete with both left and right parts
CChord m_goodPlayedNotes; // The good notes the pianist plays
CTempo m_tempo;
bool m_KeyboardLightsOn;
int m_pianistSplitPoint; // Defines which notes go in the base and treble clef
bool m_followSkillAdvanced;
int m_lastSound;
qint64 m_stopPoint; // Were we stop the music if the pianist is late
int m_cfg_rightNoteSound;
int m_cfg_wrongNoteSound;
int m_pianistGoodChan;
int m_pianistBadChan;
qint64 m_cfg_earlyNotesPoint; // don't press the note too early
qint64 m_cfg_stopPointAdvanced; // Were we stop the music if the pianist is late
qint64 m_cfg_stopPointBeginner; // Were we stop the music if the pianist is late
qint64 m_cfg_imminentNotesOffPoint;
qint64 m_cfg_playZoneEarly; // when playing along
qint64 m_cfg_playZoneLate;
int m_cfg_rhythmTapLeftHandDrumSound;
int m_cfg_rhythmTapRightHandDrumSound;
qint64 m_pianistTiming; //measure whether the pianist is playing early or late
bool m_followPlayingTimeOut; // O dear, the student is too slow
bool m_testWrongNoteSound;
int m_boostVolume;
int m_pianoVolume;
int m_activeChannel; // The current part that is being displayed (used for boost)
int m_savedMainVolume[MAX_MIDI_CHANNELS];
static playMode_t m_playMode;
int m_skill;
bool m_mutePianistPart;
int m_latencyFix; // Try to fix the latency (put the time in msec, 0 disables it)
int m_track2ChannelLookUp[MAX_MIDI_TRACKS];
};
#endif //__CONDUCTOR_H__