pianobooster/src/Scroll.cpp

400 lines
13 KiB
C++

/*********************************************************************************/
/*!
@file Scroll.c
@brief xxx.
@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 "Cfg.h"
#include "Scroll.h"
//#define NOTE_AHEAD_GAP 50
//#define NOTE_BEHIND_GAP 14
#define NOTE_AHEAD_GAP 22 // the notes on the left hand side of the score
#define NOTE_BEHIND_GAP 14
void CScroll::compileSlot(CSlotDisplayList info)
{
if (m_show == false || info.m_displayListId == 0)
return;
glNewList (info.m_displayListId, GL_COMPILE);
glTranslatef (static_cast<float>(info.getDeltaTime()) * m_noteSpacingFactor, 0.0f, 0.0f); /* move position */
info.transpose(m_transpose);
drawSlot(&info);
/*
int i;
CStavePos stavePos;
int av8Left = info.getAv8Left();
for (i=0; i < info.length(); i++)
{
stavePos.notePos(info.getSymbol(i).getHand(), info.getSymbol(i).getNote());
//ppLogTrace ("compileSlot len %d id %2d next %2d time %2d type %2d note %2d", info.length(), info.m_displayListId,
//info.m_nextDisplayListId, info.getDeltaTime(), info.getSymbol(i).getType(), info.getSymbol(i).getNote());
drawSymbol(info.getSymbol(i), 0.0, stavePos.getPosYRelative()); // we add this back when drawing this symbol
}
*/
glCallList (info.m_nextDisplayListId); /* Automatically draw the next slot even if it is not there yet */
glEndList ();
}
/*! Insert a symbol into the display list
* @return false when we have run out of symbols
*/
bool CScroll::insertSlots()
{
GLuint nextListId = 0;
if (m_headSlot.length() == 0)
m_headSlot = m_notation->nextSlot();
if (m_headSlot.length() == 0 || m_headSlot.getSymbolType(0) == PB_SYMBOL_theEndMarker) // this means we have reached the end of the file
return false;
while (true)
{
float headDelta = deltaAdjustF(m_deltaHead) * m_noteSpacingFactor;
float slotDetlta = Cfg::staveEndX() - Cfg::playZoneX() - static_cast<float>(m_headSlot.getDeltaTime()) * m_noteSpacingFactor - NOTE_BEHIND_GAP;
if (headDelta > slotDetlta)
break;
if (m_show)
{
if (m_symbolID == 0)
m_symbolID = glGenLists (1);
nextListId = glGenLists (1);
}
else
{
nextListId = m_symbolID = 0;
}
CSlotDisplayList info(m_headSlot, m_symbolID, nextListId);
m_deltaHead += info.getDeltaTime() * SPEED_ADJUST_FACTOR;
compileSlot(info);
m_scrollQueue->push(info);
m_symbolID = nextListId;
m_headSlot = m_notation->nextSlot();
if (m_headSlot.length() == 0 || m_headSlot.getSymbolType(0) == PB_SYMBOL_theEndMarker) // this means we have reached the end of the file
return false;
}
return true;
}
void CScroll::removeEarlyTimingMakers()
{
float delta = deltaAdjustF(m_deltaTail) * m_noteSpacingFactor + Cfg::playZoneX() - Cfg::scrollStartX() - NOTE_AHEAD_GAP;
// only look a few steps (10) into the scroll queue
for (int i = 0; i < 10 && i < m_scrollQueue->length(); i++ )
{
if (delta < -(static_cast<float>(m_scrollQueue->index(i).getLeftSideDeltaTime()) * m_noteSpacingFactor))
{
m_scrollQueue->indexPtr(i)->clearAllNoteTimmings();
compileSlot(m_scrollQueue->index(i));
}
delta += static_cast<float>(m_scrollQueue->index(i).getDeltaTime()) * m_noteSpacingFactor;
}
}
void CScroll::removeSlots()
{
while (m_scrollQueue->length() > 0)
{
if (deltaAdjustF(m_deltaTail) * m_noteSpacingFactor > -Cfg::playZoneX() + Cfg::scrollStartX() + NOTE_AHEAD_GAP -(static_cast<float>(m_scrollQueue->index(0).getLeftSideDeltaTime()) * m_noteSpacingFactor) )
break;
CSlotDisplayList info = m_scrollQueue->pop();
m_deltaTail += info.getDeltaTime() * SPEED_ADJUST_FACTOR;
//ppLogTrace("Remove slot id %2d time %2d type %2d note %2d", info.m_displayListId, info.getDeltaTime(), info.getSymbol(0).getType(), info.getSymbol(0).getNote());
if (info.m_displayListId)
glDeleteLists( info.m_displayListId, 1);
if (m_wantedIndex > 0)
m_wantedIndex--; // also the Chord has moved down one place
else
{
m_wantedIndex = 0;
m_wantedDelta = m_deltaTail;
}
}
}
//! Draw all the symbols that we have in the list
void CScroll::drawScrollingSymbols(bool show)
{
insertSlots(); // new symbols at the end of the score
removeSlots(); // delete old symbols no longer required
removeEarlyTimingMakers();
if (show == false) // Just update the queue only
return;
if (m_scrollQueue->length() == 0 || m_scrollQueue->indexPtr(0)->m_displayListId == 0)
return;
glPushMatrix();
glTranslatef (Cfg::playZoneX() + deltaAdjustF(m_deltaTail) * m_noteSpacingFactor, CStavePos::getStaveCenterY(), 0.0f);
BENCHMARK(8, "glTranslatef");
if (m_scrollQueue->length() > 0)
glCallList (m_scrollQueue->indexPtr(0)->m_displayListId);
BENCHMARK(9, "glCallList");
glPopMatrix();
}
void CScroll::scrollDeltaTime(qint64 ticks)
{
m_deltaHead -= ticks;
m_deltaTail -= ticks;
m_wantedDelta -= ticks;
}
bool CScroll::validPianistChord(int index)
{
CSlot* pSlot = m_scrollQueue->indexPtr(index);
assert(pSlot->length()!=0);
if (pSlot->getSymbol(0).getType() >= PB_SYMBOL_noteHead)
{
if (m_displayHand == PB_PART_both)
return true;
//eventually we need two slot queues one for each hand
for (int i = 0; i < pSlot->length(); i++)
{
if (pSlot->getSymbol(i).getHand() == m_displayHand)
return true;
}
}
return false;
}
int CScroll::findWantedChord(int note, CColor color, qint64 wantedDelta)
{
Q_UNUSED(note)
if (color == Cfg::colorTheme().playedBadColor) // fixme should be an enum
return m_wantedIndex;
{
while ( m_wantedIndex + 1 < m_scrollQueue->length())
{
if ((m_wantedDelta + m_scrollQueue->indexPtr(m_wantedIndex)->getDeltaTime() * SPEED_ADJUST_FACTOR) >= -wantedDelta)
{
if (validPianistChord(m_wantedIndex) == true)
break;
}
m_wantedDelta += m_scrollQueue->indexPtr(m_wantedIndex)->getDeltaTime() * SPEED_ADJUST_FACTOR;
m_wantedIndex++;
}
}
return m_wantedIndex;
}
void CScroll::setPlayedNoteColor(int note, CColor color, qint64 wantedDelta, qint64 pianistTimming)
{
int index;
if (m_wantedIndex >= m_scrollQueue->length())
return;
index = findWantedChord(note, color, wantedDelta);
note -= m_transpose;
m_scrollQueue->indexPtr(index)->setNoteColor(note, color);
if (pianistTimming != NOT_USED)
{
pianistTimming = deltaAdjustL(pianistTimming) * DEFAULT_PPQN / CMidiFile::getPulsesPerQuarterNote();
m_scrollQueue->indexPtr(index)->setNoteTimming(note, pianistTimming);
}
compileSlot(m_scrollQueue->index(index));
}
void CScroll::refresh()
{
int i;
if (m_show == false)
return;
for ( i = 0; i < m_scrollQueue->length(); i++)
compileSlot(m_scrollQueue->index(i));
}
void CScroll::refreshNoteColor(CColor color)
{
m_notation->resetNoteColor(color);
m_headSlot.setNoteColor(0, color);
for (auto i = 0, len = m_scrollQueue->length(); i != len; ++i) {
m_scrollQueue->indexPtr(i)->setNoteColor(0, color);
}
}
bool CScroll::getKeyboardInfo(int *notes)
{
int stoppedScrollIdx = -1;
const auto &colorTheme = Cfg::colorTheme();
for(int i=0; i<m_scrollQueue->length(); ++i) {
CSlotDisplayList &info = *m_scrollQueue->indexPtr(i);
if(m_show == false || info.m_displayListId == 0) continue;
CSlot* slot = &info;
for(int j=0; j<slot->length(); ++j) {
if(slot->getSymbol(j).getType() < PB_SYMBOL_noteHead) continue;
if(slot->getSymbol(j).getColor() == colorTheme.playedStoppedColor) {
stoppedScrollIdx = i;
break;
}
}
if(stoppedScrollIdx > -1) break;
}
if(stoppedScrollIdx > -1) {
for(int i=0; i<stoppedScrollIdx; ++i) {
CSlotDisplayList &info = *m_scrollQueue->indexPtr(i);
if(m_show == false || info.m_displayListId == 0) continue;
CSlot* slot = &info;
for(int j=0; j<slot->length(); ++j) {
if(slot->getSymbol(j).getType() < PB_SYMBOL_noteHead) continue;
slot->getSymbolPtr(j)->setColor(colorTheme.playedGoodColor);
}
}
}
int *note = notes;
for(int i=0; i<m_scrollQueue->length(); ++i) {
CSlotDisplayList &info = *m_scrollQueue->indexPtr(i);
if(m_show == false || info.m_displayListId == 0) continue;
CSlot* slot = &info;
bool stopped = false;
for(int j=0; j<slot->length(); ++j) {
if(slot->getSymbol(j).getType() < PB_SYMBOL_noteHead) continue;
if(slot->getSymbol(j).getColor() == colorTheme.noteColor ||
slot->getSymbol(j).getColor() == colorTheme.playedStoppedColor)
*(note++) = slot->getSymbol(j).getNote();
if(slot->getSymbol(j).getColor() == colorTheme.playedStoppedColor) stopped = true;
}
if(note != notes) return stopped;
}
return false;
}
void CScroll::transpose(int transpose)
{
if (m_transpose == transpose)
return;
m_transpose = transpose;
refresh();
}
void CScroll::showScroll(bool show)
{
int i;
GLuint nextListId = 0;
m_show = show;
if (show == true)
{
if (m_symbolID == 0)
m_symbolID = glGenLists (1);
// add in the missing GL display list
for ( i = 0; i < m_scrollQueue->length(); i++)
{
//assert (m_scrollQueue->indexPtr(i)->m_displayListId == 0);
nextListId = glGenLists (1);
m_scrollQueue->indexPtr(i)->m_displayListId = m_symbolID;
m_scrollQueue->indexPtr(i)->m_nextDisplayListId = nextListId;
m_symbolID = nextListId;
}
// And now compile the slot (remember that each slot points to the next one)
for ( i = 0; i < m_scrollQueue->length(); i++)
{
compileSlot(m_scrollQueue->index(i));
}
}
else
{
// Remove all the gl items
for ( i = 0; i < m_scrollQueue->length(); i++)
{
if (m_scrollQueue->indexPtr(i)->m_displayListId != 0 && m_scrollQueue->indexPtr(i)->m_displayListId != nextListId)
glDeleteLists(m_scrollQueue->index(i).m_displayListId, 1);
m_scrollQueue->indexPtr(i)->m_displayListId = 0;
nextListId = m_scrollQueue->indexPtr(i)->m_nextDisplayListId;
m_scrollQueue->indexPtr(i)->m_nextDisplayListId = 0;
if (nextListId != 0)
glDeleteLists(nextListId, 1);
}
if (m_symbolID != 0)
glDeleteLists(m_symbolID, 1);
m_symbolID = 0;
}
}
CScroll::CSlotDisplayList::CSlotDisplayList(const CSlot& slot, GLuint displayListId, GLuint nextDisplayListId) : CSlot(slot),
m_displayListId(displayListId), m_nextDisplayListId(nextDisplayListId)
{
// It is all done in the initialisation list
}
void CScroll::reset()
{
int i;
m_wantedIndex = 0;
m_wantedDelta = 0;
m_deltaHead = m_deltaTail = 0;
m_notation->reset();
m_headSlot.clear();
for ( i = 0; i < m_scrollQueue->length(); i++)
{
if (m_scrollQueue->index(i).m_displayListId)
glDeleteLists(m_scrollQueue->index(i).m_displayListId, 1);
}
if (m_symbolID != 0)
glDeleteLists(m_symbolID, 1);
m_symbolID = 0;
m_scrollQueue->clear();
m_ppqnFactor = static_cast<float>(DEFAULT_PPQN) / static_cast<float>(CMidiFile::getPulsesPerQuarterNote());
m_noteSpacingFactor = m_ppqnFactor * HORIZONTAL_SPACING_FACTOR;
}