pianobooster/src/TrackList.cpp

542 lines
18 KiB
C++

/*********************************************************************************/
/*!
@file TrackList.cpp
@brief The Design.
@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/>.
*/
/*********************************************************************************/
#include <QtGui>
#include "TrackList.h"
#include "Song.h"
#include "Settings.h"
void CTrackList::init(CSong* songObj, CSettings* settings)
{
m_song = songObj;
m_settings = settings;
}
void CTrackList::reset(int numberOfTracks)
{
m_partsList.clear();
m_midiChannels.clear();
for (int chan = 0; chan < MAX_MIDI_CHANNELS; chan++) {
for (int i = 0; i < MAX_MIDI_NOTES; i++) {
m_noteFrequency[chan][i]=0;
}
m_midiChannels.append(AnalyseItem(numberOfTracks));
}
}
void CTrackList::currentRowChanged(int currentRow)
{
if (!m_song) return;
if (currentRow >= m_partsList.size()|| currentRow < 0)
return;
m_song->setActiveChannel(m_partsList[currentRow].midiChannel());
}
void CTrackList::examineMidiEvent(CMidiEvent event)
{
int chan = event.channel();
assert (chan < MAX_MIDI_CHANNELS && chan >= 0);
if (chan < MAX_MIDI_CHANNELS && chan >= 0)
{
if (event.type() == MIDI_NOTE_ON)
{
m_midiChannels[chan].addNoteEvent(event);
// 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 (event.type() == MIDI_PROGRAM_CHANGE) {
m_midiChannels[chan].addPatch(event.programme());
}
}
}
// Returns true if there is a piano part on channels 3 & 4
bool CTrackList::pianoPartConvetionTest()
{
AnalyseItem left = m_midiChannels[CONVENTION_LEFT_HAND_CHANNEL];
AnalyseItem right = m_midiChannels[CONVENTION_RIGHT_HAND_CHANNEL];
// Both hands on channels 3 & 4
if (left.active() && right.active()) {
if (left.firstPatch() == right.firstPatch() && isPianoOrOrganPatch(right.firstPatch())) {
CNote::setChannelHands(CONVENTION_LEFT_HAND_CHANNEL, CONVENTION_RIGHT_HAND_CHANNEL);
return true;
}
}
// Right hand only channel 4 and no left hand on channel 3
if (right.active() && !left.active()) {
if (isPianoOrOrganPatch(right.firstPatch())) {
CNote::setChannelHands(CONVENTION_LEFT_HAND_CHANNEL, CONVENTION_RIGHT_HAND_CHANNEL);
return true;
}
}
// Left hand only channel 3 and no right hand on channel 4
if (left.active() && !right.active()) {
if (isPianoOrOrganPatch(left.firstPatch())) {
CNote::setChannelHands(CONVENTION_LEFT_HAND_CHANNEL, CONVENTION_RIGHT_HAND_CHANNEL);
return true;
}
}
return false;
}
bool CTrackList::findLeftAndRightPianoParts()
{
int patchA = -1;
int chanA = -1;
for (int chan = 0 ; chan < MAX_MIDI_CHANNELS; chan++) {
if (chan == MIDI_DRUM_CHANNEL) {
continue;
}
if (m_midiChannels[chan].active()) {
int patch = m_midiChannels[chan].firstPatch();
if (isPianoOrOrganPatch(patch)) {
if (m_midiChannels[chan].trackCount() > 1) {
CNote::setChannelHands(chan, chan);
return true;
}
if (patchA == -1) {
patchA = patch;
chanA = chan;
} else {
if (patchA == patch) {
if (averageNotePitch(chan) < averageNotePitch(chanA)) {
CNote::setChannelHands(chan, chanA);
} else {
CNote::setChannelHands(chanA, chan);
}
return true;
}
}
}
}
}
return false;
}
int CTrackList::guessKeySignature(int chanA, int chanB)
{
int chan;
int i;
int keySignature = 0;
int highScore = 0;
int scale[MIDI_OCTAVE];
for (i=0; i < MIDI_OCTAVE; i++)
scale[i] = 0;
for (chan = 0 ; chan < MAX_MIDI_CHANNELS; chan++)
{
if (chanA == -1 || chan == chanA || chan == chanB)
{
for (int note = 0; note < MAX_MIDI_NOTES; note++)
scale[note % MIDI_OCTAVE] += m_noteFrequency[chan][note];
}
}
for (i = 0; i < MIDI_OCTAVE; i++)
{
int score = 0;
struct {
int offset;
int key;
} keyLookUp[MIDI_OCTAVE] =
{
{0, 0}, // 0 C
{7, 1}, // 1 G 1#
{5, -1}, // 2 F 1b
{2, 2}, // 3 D 2#
{10,-2}, // 4 Bb 2b
{9, 3}, // 5 A 3#
{3, -3}, // 6 Eb 3b
{4, 4}, // 7 E 4#
{8, -4}, // 8 Ab 4b
{11, 5}, // 9 B 5#
{1, -5}, // 10 Db 5b
{6, 6}, // 11 F# 6#
};
int idx = keyLookUp[i].offset;
score += scale[(idx + 0 )%MIDI_OCTAVE]; // First note in the scale
score += scale[(idx + 2 )%MIDI_OCTAVE]; // Tone
score += scale[(idx + 4 )%MIDI_OCTAVE]; // Tone
score += scale[(idx + 5 )%MIDI_OCTAVE]; // Semi tone
score += scale[(idx + 7 )%MIDI_OCTAVE]; // Tone
score += scale[(idx + 9 )%MIDI_OCTAVE]; // Tone
score += scale[(idx + 11)%MIDI_OCTAVE]; // Tone
// the Last note don't count it
if (score > highScore)
{
highScore = score;
keySignature = keyLookUp[i].key;
}
}
return keySignature;
}
// Find an unused channel
int CTrackList::findFreeChannel(int startChannel)
{
int chan;
for (chan = startChannel; chan < MAX_MIDI_CHANNELS; chan++)
{
if (chan == Cfg::keyboardLightsChan)
continue;
if (chan == MIDI_DRUM_CHANNEL)
continue;
if (!m_midiChannels[chan].active())
return chan;
}
return -1; // Not found
}
void CTrackList::refresh()
{
int rowCount = 0;
m_partsList.clear();
for (int chan = 0; chan < MAX_MIDI_CHANNELS; chan++)
{
if (m_midiChannels[chan].active())
{
m_partsList.append(CTrackListItem(chan));
rowCount++;
}
}
if (CNote::bothHandsChan() != -2 ) { // -2 for not set -1 for not used
m_song->setActiveChannel(CNote::bothHandsChan());
} else if (pianoPartConvetionTest()) {
m_song->setActiveChannel(CNote::bothHandsChan());
ppLogInfo("Active both");
}
else if (findLeftAndRightPianoParts()) {
m_song->setActiveChannel(CNote::bothHandsChan());
} else {
if (m_partsList.count() > 0) {
// for the case when there is no piano or organ patch
m_song->setActiveChannel(m_partsList[0].midiChannel());
}
}
for (int chan = 0; chan < MAX_MIDI_CHANNELS; chan++)
{
AnalyseItem item = m_midiChannels[chan];
if (item.active())
{
if (item.firstPatch() == GM_PIANO_PATCH) {
// For those midi files that do not include ANY patch we want to set it piano
CMidiEvent midi;
midi.programChangeEvent(0, chan, GM_PIANO_PATCH);
m_song->playMidiEvent(midi);
}
}
}
if (CStavePos::getKeySignature() == NOT_USED)
CStavePos::setKeySignature(guessKeySignature(CNote::rightHandChan(), CNote::leftHandChan()), 0);
// Find an unused channel that we can use for the keyboard
m_song->reset();
int goodChan = findFreeChannel(0);
int badChan = findFreeChannel(goodChan + 1);
int spareChan = findFreeChannel(badChan +1 );
if (badChan == -1)
{
// As we have not found two we have not found to empty channels to use
goodChan = 15 -1;
badChan = 16-1;
}
m_song->setPianistChannels(goodChan, badChan);
ppLogInfo("Using Pianist Channels %d + %d", goodChan +1, badChan +1);
if (Cfg::keyboardLightsChan != -1 && spareChan != -1)
m_song->mapTrack2Channel(Cfg::keyboardLightsChan, spareChan);
for (int chan = 0; chan < MAX_MIDI_CHANNELS; chan++) {
AnalyseItem item = m_midiChannels[chan];
CNote::setRightHandTrack(chan, item.rightHandTrack());
}
}
int CTrackList::getActiveItemIndex()
{
int chan;
for (int i = 0; i < m_partsList.size(); ++i)
{
chan = m_partsList[i].midiChannel();
if (chan == CNote::rightHandChan() )
return i;
}
return 0; // Not found so return first item on the list
}
QStringList CTrackList::getAllChannelProgramNames(bool raw)
{
QStringList items;
int chan;
QString text;
QString hand;
for (int i = 0; i < m_partsList.size(); ++i)
{
hand.clear();
chan = m_partsList[i].midiChannel();
if (raw == false)
{
if (CNote::leftHandChan() == chan)
hand += QObject::tr("L");
if (CNote::rightHandChan() == chan)
hand += QObject::tr("R");
}
text = QString::number(chan+1) + hand + " " + getChannelProgramName(chan);
items += text;
}
return items;
}
int CTrackList::getActiveHandIndex(whichPart_t whichPart)
{
int index = 0;
for (int i = 0; i < m_partsList.size(); ++i)
if (m_partsList[i].midiChannel() == CNote::getHandChannel( whichPart))
return index;
return index;
}
void CTrackList::setActiveHandsIndex(int leftIndex, int rightIndex)
{
int leftChannel = -1;
int rightChannel = -1;
if (leftIndex>=0)
leftChannel = m_partsList.at(leftIndex).midiChannel();
if (rightIndex>=0)
rightChannel = m_partsList.at(rightIndex).midiChannel();
m_settings->setChannelHands(leftChannel, rightChannel);
refresh();
m_song->rewind();
}
// get the track index number of the selected hand
int CTrackList::getHandTrackIndex(whichPart_t whichPart)
{
int index = 0;
int midiHand = CNote::getHandChannel(whichPart);
for (int i = 0; i < m_partsList.size(); ++i)
{
if (m_partsList[i].midiChannel() == midiHand)
return index;
index++;
}
return -1;
}
void CTrackList::changeListWidgetItemView( int index, QListWidgetItem* listWidgetItem )
{
int chan = m_partsList[index].midiChannel();
if ( CNote::hasPianoPart( chan ))
{
QFont font = listWidgetItem->font();
if (CNote::rightHandChan() >= 0 && CNote::leftHandChan() >= 0 )
font.setBold(true);
listWidgetItem->setFont(font);
listWidgetItem->setForeground(Qt::darkBlue);
}
else if ( m_song->hasPianistKeyboardChannel( chan ) )
listWidgetItem->setForeground(Qt::lightGray);
}
QString CTrackList::getChannelProgramName(int chan)
{
if(chan<0 || chan>= MAX_MIDI_CHANNELS)
{
assert(true);
return QString();
}
int program = m_midiChannels[chan].firstPatch();
if (chan==10-1)
return QObject::tr("Drums");
QString name = getProgramName(program +1); // Skip
if (name.isEmpty())
name = QObject::tr("Unknown");
return name;
}
QString CTrackList::getProgramName(int program)
{
const char * const gmInstrumentNames[] =
{
QT_TR_NOOP("(None)"), // Don't use
/* 1. */ QT_TR_NOOP("Grand Piano"),
/* 2. */ QT_TR_NOOP("Bright Piano"),
/* 3. */ QT_TR_NOOP("Electric Grand"),
/* 4. */ QT_TR_NOOP("Honky-tonk Piano"),
/* 5. */ QT_TR_NOOP("Electric Piano 1"),
/* 6. */ QT_TR_NOOP("Electric Piano 2"),
/* 7. */ QT_TR_NOOP("Harpsichord"),
/* 8. */ QT_TR_NOOP("Clavi"),
/* 9. */ QT_TR_NOOP("Celesta"),
/* 10. */ QT_TR_NOOP("Glockenspiel"),
/* 11. */ QT_TR_NOOP("Music Box"),
/* 12. */ QT_TR_NOOP("Vibraphone"),
/* 13. */ QT_TR_NOOP("Marimba"),
/* 14. */ QT_TR_NOOP("Xylophone"),
/* 15. */ QT_TR_NOOP("Tubular Bells"),
/* 16. */ QT_TR_NOOP("Dulcimer"),
/* 17. */ QT_TR_NOOP("Drawbar Organ"),
/* 18. */ QT_TR_NOOP("Percussive Organ"),
/* 19. */ QT_TR_NOOP("Rock Organ"),
/* 20. */ QT_TR_NOOP("Church Organ"),
/* 21. */ QT_TR_NOOP("Reed Organ"),
/* 22. */ QT_TR_NOOP("Accordion"),
/* 23. */ QT_TR_NOOP("Harmonica"),
/* 24. */ QT_TR_NOOP("Tango Accordion"),
/* 25. */ QT_TR_NOOP("Acoustic Guitar (nylon)"),
/* 26. */ QT_TR_NOOP("Acoustic Guitar (steel)"),
/* 27. */ QT_TR_NOOP("Electric Guitar (jazz)"),
/* 28. */ QT_TR_NOOP("Electric Guitar (clean)"),
/* 29. */ QT_TR_NOOP("Electric Guitar (muted)"),
/* 30. */ QT_TR_NOOP("Overdriven Guitar"),
/* 31. */ QT_TR_NOOP("Distortion Guitar"),
/* 32. */ QT_TR_NOOP("Guitar harmonics"),
/* 33. */ QT_TR_NOOP("Acoustic Bass"),
/* 34. */ QT_TR_NOOP("Electric Bass (finger)"),
/* 35. */ QT_TR_NOOP("Electric Bass (pick)"),
/* 36. */ QT_TR_NOOP("Fretless Bass"),
/* 37. */ QT_TR_NOOP("Slap Bass 1"),
/* 38. */ QT_TR_NOOP("Slap Bass 2"),
/* 39. */ QT_TR_NOOP("Synth Bass 1"),
/* 40. */ QT_TR_NOOP("Synth Bass 2"),
/* 41. */ QT_TR_NOOP("Violin"),
/* 42. */ QT_TR_NOOP("Viola"),
/* 43. */ QT_TR_NOOP("Cello"),
/* 44. */ QT_TR_NOOP("Contrabass"),
/* 45. */ QT_TR_NOOP("Tremolo Strings"),
/* 46. */ QT_TR_NOOP("Pizzicato Strings"),
/* 47. */ QT_TR_NOOP("Orchestral Harp"),
/* 48. */ QT_TR_NOOP("Timpani"),
/* 49. */ QT_TR_NOOP("String Ensemble 1"),
/* 50. */ QT_TR_NOOP("String Ensemble 2"),
/* 51. */ QT_TR_NOOP("SynthStrings 1"),
/* 52. */ QT_TR_NOOP("SynthStrings 2"),
/* 53. */ QT_TR_NOOP("Choir Aahs"),
/* 54. */ QT_TR_NOOP("Voice Oohs"),
/* 55. */ QT_TR_NOOP("Synth Voice"),
/* 56. */ QT_TR_NOOP("Orchestra Hit"),
/* 57. */ QT_TR_NOOP("Trumpet"),
/* 58. */ QT_TR_NOOP("Trombone"),
/* 59. */ QT_TR_NOOP("Tuba"),
/* 60. */ QT_TR_NOOP("Muted Trumpet"),
/* 61. */ QT_TR_NOOP("French Horn"),
/* 62. */ QT_TR_NOOP("Brass Section"),
/* 63. */ QT_TR_NOOP("SynthBrass 1"),
/* 64. */ QT_TR_NOOP("SynthBrass 2"),
/* 65. */ QT_TR_NOOP("Soprano Sax"),
/* 66. */ QT_TR_NOOP("Alto Sax"),
/* 67. */ QT_TR_NOOP("Tenor Sax"),
/* 68. */ QT_TR_NOOP("Baritone Sax"),
/* 69. */ QT_TR_NOOP("Oboe"),
/* 70. */ QT_TR_NOOP("English Horn"),
/* 71. */ QT_TR_NOOP("Bassoon"),
/* 72. */ QT_TR_NOOP("Clarinet"),
/* 73. */ QT_TR_NOOP("Piccolo"),
/* 74. */ QT_TR_NOOP("Flute"),
/* 75. */ QT_TR_NOOP("Recorder"),
/* 76. */ QT_TR_NOOP("Pan Flute"),
/* 77. */ QT_TR_NOOP("Blown Bottle"),
/* 78. */ QT_TR_NOOP("Shakuhachi"),
/* 79. */ QT_TR_NOOP("Whistle"),
/* 80. */ QT_TR_NOOP("Ocarina"),
/* 81. */ QT_TR_NOOP("Lead 1 (square)"),
/* 82. */ QT_TR_NOOP("Lead 2 (sawtooth)"),
/* 83. */ QT_TR_NOOP("Lead 3 (calliope)"),
/* 84. */ QT_TR_NOOP("Lead 4 (chiff)"),
/* 85. */ QT_TR_NOOP("Lead 5 (charang)"),
/* 86. */ QT_TR_NOOP("Lead 6 (voice)"),
/* 87. */ QT_TR_NOOP("Lead 7 (fifths)"),
/* 88. */ QT_TR_NOOP("Lead 8 (bass + lead)"),
/* 89. */ QT_TR_NOOP("Pad 1 (new age)"),
/* 90. */ QT_TR_NOOP("Pad 2 (warm)"),
/* 91. */ QT_TR_NOOP("Pad 3 (polysynth)"),
/* 92. */ QT_TR_NOOP("Pad 4 (choir)"),
/* 93. */ QT_TR_NOOP("Pad 5 (bowed)"),
/* 94. */ QT_TR_NOOP("Pad 6 (metallic)"),
/* 95. */ QT_TR_NOOP("Pad 7 (halo)"),
/* 96. */ QT_TR_NOOP("Pad 8 (sweep)"),
/* 97. */ QT_TR_NOOP("FX 1 (rain)"),
/* 98. */ QT_TR_NOOP("FX 2 (soundtrack)"),
/* 99. */ QT_TR_NOOP("FX 3 (crystal)"),
/* 100. */ QT_TR_NOOP("FX 4 (atmosphere)"),
/* 101. */ QT_TR_NOOP("FX 5 (brightness)"),
/* 102. */ QT_TR_NOOP("FX 6 (goblins)"),
/* 103. */ QT_TR_NOOP("FX 7 (echoes)"),
/* 104. */ QT_TR_NOOP("FX 8 (sci-fi)"),
/* 105. */ QT_TR_NOOP("Sitar"),
/* 106. */ QT_TR_NOOP("Banjo"),
/* 107. */ QT_TR_NOOP("Shamisen"),
/* 108. */ QT_TR_NOOP("Koto"),
/* 109. */ QT_TR_NOOP("Kalimba"),
/* 110. */ QT_TR_NOOP("Bag pipe"),
/* 111. */ QT_TR_NOOP("Fiddle"),
/* 112. */ QT_TR_NOOP("Shanai"),
/* 113. */ QT_TR_NOOP("Tinkle Bell"),
/* 114. */ QT_TR_NOOP("Agogo"),
/* 115. */ QT_TR_NOOP("Steel Drums"),
/* 116. */ QT_TR_NOOP("Woodblock"),
/* 117. */ QT_TR_NOOP("Taiko Drum"),
/* 118. */ QT_TR_NOOP("Melodic Tom"),
/* 119. */ QT_TR_NOOP("Synth Drum"),
/* 120. */ QT_TR_NOOP("Reverse Cymbal"),
/* 121. */ QT_TR_NOOP("Guitar Fret Noise"),
/* 122. */ QT_TR_NOOP("Breath Noise"),
/* 123. */ QT_TR_NOOP("Seashore"),
/* 124. */ QT_TR_NOOP("Bird Tweet"),
/* 125. */ QT_TR_NOOP("Telephone Ring"),
/* 126. */ QT_TR_NOOP("Helicopter"),
/* 127. */ QT_TR_NOOP("Applause"),
/* 128. */ QT_TR_NOOP("Gunshot"),
};
if (program >= 0 && program < arraySize(gmInstrumentNames))
return tr(gmInstrumentNames[program]);
else
return QString();
}