1
// RCS-ID: $Id: Score.cpp,v 1.3 2006/02/23 19:23:54 cecilios Exp $
2
//--------------------------------------------------------------------------------------
3
// LenMus Phonascus: The teacher of music
4
// Copyright (c) 2002-2006 Cecilio Salmeron
6
// This program is free software; you can redistribute it and/or modify it under the
7
// terms of the GNU General Public License as published by the Free Software Foundation;
8
// either version 2 of the License, or (at your option) any later version.
10
// This program is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12
// PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
// You should have received a copy of the GNU General Public License along with this
15
// program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
16
// Fifth Floor, Boston, MA 02110-1301, USA.
18
// For any comment, suggestion or feature request, please contact the manager of
19
// the project at cecilios@users.sourceforge.net
21
//-------------------------------------------------------------------------------------
23
@brief Implementation file for class lmScore
26
//--------------------------------------------------------------------------------------------------
30
A score is a collection of instruments (\<parts\> in MusicXML).
31
An instrument is, normally, one VStaff, but more VStaves are posible (for what?)
32
And an VStaff is a collection of StaffObjs.
35
//--------------------------------------------------------------------------------------------------
38
// #pragma implementation
41
// For compilers that support precompilation, includes "wx/wx.h".
42
#include "wx/wxprec.h"
55
#include "../app/global.h"
57
// global unique variables used during score building
58
lmNoteRest* g_pLastNoteRest;
59
lmBeam* g_pCurBeam; // lmBeam object that is being built ot NULL if none in process. See lmNote constructor
62
#include "../globals/Colors.h"
63
extern lmColors* g_pColors;
65
//---------------------------------------------------------------------------------------
66
// lmScore constructors and destructor
67
//---------------------------------------------------------------------------------------
71
//Set up an empty score, that is, without any lmInstrument.
73
m_pTitle = (lmText*)NULL; //no title
74
m_pSubtitle = (lmText*)NULL; //no subtitle
76
//create the renderer object
77
m_pFormatter = new lmFormatter3();
78
m_pFormatter->SetScore(this);
81
m_pSoundMngr = (lmSoundManager*)NULL;
83
//! @todo fill this not with constants
84
m_nSystemsDistance = 20000; // 2 cm = 20 mm = 20,000 microns
85
m_nTopSystemDistance = 20000; // 2 cm = 20 mm = 20,000 microns
86
m_nSystemsLeftMargin = 0;
87
m_nSystemsRightMargin = 0;
93
//wxLogMessage(_T("[lmScore::~lmScore] Deleting lmScore object"));
94
if (m_pFormatter) delete m_pFormatter;
95
m_cInstruments.DeleteContents(true);
96
m_cInstruments.Clear();
98
m_cGlobalStaffobjs.DeleteContents(true);
99
m_cGlobalStaffobjs.Clear();
102
m_pSoundMngr->DeleteEventsTable();
106
m_cHighlighted.DeleteContents(false); //Staffobjs must not be deleted, only the list
107
m_cHighlighted.Clear();
109
if (m_pTitle) delete m_pTitle;
110
if (m_pSubtitle) delete m_pSubtitle;
114
//---------------------------------------------------------------------------------------
115
// score object methods
116
//---------------------------------------------------------------------------------------
118
void lmScore::SetTitle(wxString title)
121
m_pTitle = new lmText(this, title, 0, 0, false, false, _T("Garamond"), 14, true, false );
122
IncludeInGlobalList(m_pTitle);
125
m_pTitle->SetText(title);
129
void lmScore::SetSubtitle(wxString subtitle)
132
m_pSubtitle = new lmText(this, subtitle, 0, 0, false, false, _T("Garamond"), 11, false, true );
133
IncludeInGlobalList(m_pSubtitle);
136
m_pSubtitle->SetText(subtitle);
140
wxInt32 lmScore::GetNumMeasures()
142
//! @limit it is being assumed that all instruments and staves have the same number of bars
143
InstrumentsList::Node *node = m_cInstruments.GetFirst();
144
lmInstrument *pInstr = node->GetData();
145
lmVStaff *pStaff = pInstr->GetVStaff(1);
146
return(pStaff->GetNumMeasures());
149
lmInstrument* lmScore::AddInstrument(wxInt32 nVStaves, wxInt32 nMIDIChannel, wxInt32 nMIDIInstr)
151
//add an lmInstrument with nVStaves (1..m) empty VStaves.
152
//nMIDIChannel is the MIDI channel to use for playing this instrument
153
lmInstrument* pInstr = new lmInstrument(this, nVStaves, nMIDIChannel, nMIDIInstr);
154
m_cInstruments.Append(pInstr);
159
//returns lmVStaff number nVStaff (1..n), of lmInstrument nInstr (1..m)
160
lmVStaff* lmScore::GetVStaff(wxInt32 nInstr, wxInt32 nVStaff)
163
InstrumentsList::Node *node;
164
lmInstrument *pInstr;
165
//iterate over the list to locate lmInstrument nInstr
166
for (i=1, node = m_cInstruments.GetFirst();
167
node && i < nInstr; node = node->GetNext(), i++ ) {}
168
// wxASSERT_MSG{pInstr != 0, _T("No existe el Instrumento num. nInstr"));
169
pInstr = node->GetData();
170
return(pInstr->GetVStaff(nVStaff));
173
lmInstrument* lmScore::XML_FindInstrument(wxString sId)
175
// iterate over instrument list to find the one with id == sId
176
wxInstrumentsListNode *node;
177
lmInstrument* pInstr = (lmInstrument*)NULL;
178
for (node = m_cInstruments.GetFirst(); node; node = node->GetNext()) {
179
pInstr = (lmInstrument*)node->GetData();
180
if (pInstr->XML_GetId() == sId) break;
185
void lmScore::Draw(lmPaper *pPaper)
187
// Optional fMetodoJustificado As Boolean = True, _
188
// Optional nTipoEspaciado As ESpacingMethod = esm_PropConstantShortNote, _
189
// Optional fJustificada As Boolean = True, _
190
// Optional fTruncarUltimoSistema As Boolean = False, _
191
// Optional rFactorAjuste As Single = 1#)
195
////DEBUG: draw green lines to show page borders
196
//wxDC* pDC = pPaper->GetDC();
198
//lmMicrons yTop = pPaper->GetCursorY();
199
//lmMicrons xLeft = pPaper->GetLeftMarginXPos();
200
//lmMicrons xRight = pPaper->GetRightMarginXPos();
201
//pDC->SetPen(*wxGREEN_PEN);
202
//pDC->DrawLine(xLeft, yTop, xRight, yTop); //top horizontal
203
//pDC->DrawLine(xLeft, yTop, xLeft, (pPaper->GetPaperSize()).GetHeight()); //left vertical
204
//pDC->DrawLine(xRight, yTop, xRight, (pPaper->GetPaperSize()).GetHeight()); //right vertical
205
////End DEBUG --------------------------------------------
207
//WriteTitles(DO_MEASURE, pPaper);
208
//WriteTitles(DO_DRAW, pPaper);
209
m_pFormatter->RenderScore(pPaper);
213
void lmScore::WriteTitles(bool fMeasuring, lmPaper *pPaper)
215
long nWidth, nHeight;
219
// Measurement phase ---------------------------------------------------
221
m_nHeadersHeight = 0;
223
// measure Title if exits
226
if (!m_pTitle->IsFixed()) {
227
m_pTitle->Draw(DO_MEASURE, pPaper);
228
nWidth = m_pTitle->GetSelRect().width;
229
nHeight = m_pTitle->GetSelRect().height;
230
xPos = (pPaper->GetRightMarginXPos() - pPaper->GetLeftMarginXPos() - nWidth)/2;
231
pPaper->SetCursorX(pPaper->GetLeftMarginXPos() + xPos);
233
m_pTitle->Draw(DO_MEASURE, pPaper);
234
m_pTitle->SetFixed(true);
235
nWidth = m_pTitle->GetSelRect().width;
236
nHeight = m_pTitle->GetSelRect().height;
238
// advance paper and update headers total height
239
pPaper->IncrementCursorY(nHeight);
240
m_nHeadersHeight += nHeight;
243
// measure Subtitle if exits
246
if (!m_pSubtitle->IsFixed()) {
247
m_pSubtitle->Draw(DO_MEASURE, pPaper);
248
nWidth = m_pSubtitle->GetSelRect().width;
249
nHeight = m_pSubtitle->GetSelRect().height;
250
xPos = (pPaper->GetRightMarginXPos() - pPaper->GetLeftMarginXPos() - nWidth)/2;
251
pPaper->SetCursorX(pPaper->GetLeftMarginXPos() + xPos);
255
m_pSubtitle->Draw(DO_MEASURE, pPaper);
256
m_pSubtitle->SetFixed(true);
257
nWidth = m_pSubtitle->GetSelRect().width;
258
nHeight = m_pSubtitle->GetSelRect().height;
260
//update headers total height
261
m_nHeadersHeight += nHeight;
267
// Drawing phase ---------------------------------------------------
268
if (m_pTitle) m_pTitle->Draw(DO_DRAW, pPaper);
269
if (m_pSubtitle) m_pSubtitle->Draw(DO_DRAW, pPaper);
274
void lmScore::IncludeInGlobalList(lmStaffObj* pSO)
276
m_cGlobalStaffobjs.Append(pSO);
279
void lmScore::RemoveFromGlobalList(lmStaffObj* pSO)
281
m_cGlobalStaffobjs.DeleteObject(pSO);
284
//=========================================================================================
285
// Methods for finding StaffObjs
286
//=========================================================================================
288
lmScoreObj* lmScore::FindSelectableObject(wxPoint& pt)
291
lmInstrument *pInstr;
293
InstrumentsList::Node *node;
296
//Look up in the global StaffObjs list
297
wxStaffObjsListNode* pNode;
298
for(pNode = m_cGlobalStaffobjs.GetFirst(); pNode; pNode = pNode->GetNext()) {
299
pScO = (lmScoreObj*)pNode->GetData();
300
if (pScO->IsAtPoint(pt)) {
305
//Not found in global list. Look up in the VStaffs' lists
307
//for each instrument
308
for (node = m_cInstruments.GetFirst(); node; node=node->GetNext()) {
309
pInstr = node->GetData();
312
for (iVStaff=1; iVStaff <= pInstr->GetNumStaves(); iVStaff++) {
313
pStaff = pInstr->GetVStaff(iVStaff);
315
//look for posible lmStaffObj and exit if any found
316
pScO = pStaff->FindSelectableObject(pt);
317
if (pScO) return(pScO);
320
return (lmScoreObj *)NULL; //none found
323
////Friend methods for lmFormatter object and for internal use
324
////=========================================================================================
326
lmInstrument* lmScore::GetFirstInstrument()
328
m_pNode = m_cInstruments.GetFirst();
329
return (m_pNode ? (lmInstrument *)m_pNode->GetData() : (lmInstrument *)m_pNode);
332
lmInstrument* lmScore::GetNextInstrument()
335
m_pNode = m_pNode->GetNext();
336
return (m_pNode ? (lmInstrument *)m_pNode->GetData() : (lmInstrument *)m_pNode);
339
lmInstrument* lmScore::GetLastInstrument()
342
m_pNode = m_cInstruments.GetLast();
343
return (m_pNode ? (lmInstrument *)m_pNode->GetData() : (lmInstrument *)m_pNode);
346
////Returns the lmInstrument number nInstr (1..n)
347
//Friend Property Get lmInstrument(nInstr As Long) As CInstrumento
348
// Set lmInstrument = m_cInstruments.Item(nInstr)
352
////Returns the number of Instruments in the Score
353
//Friend Property Get InstrumentsCount() As Long
354
// InstrumentsCount = m_cInstruments.Count
358
////== End of Friend methods for lmFormatter object ============================================
360
wxString lmScore::Dump()
362
wxString sDump = _T("Global objects:\n");
364
//loop to dump global StaffObjs
366
wxStaffObjsListNode* pNode;
367
for(pNode = m_cGlobalStaffobjs.GetFirst(); pNode; pNode = pNode->GetNext()) {
368
pSO = (lmStaffObj*)pNode->GetData();
369
sDump += pSO->Dump();
372
//loop to dump all instruments
373
sDump += _T("\nLocal objects:\n");
374
lmInstrument *pInstr = GetFirstInstrument();
375
for (int i=1; i<= (int)m_cInstruments.GetCount(); i++, pInstr = GetNextInstrument())
377
sDump += wxString::Format(_T("\nInstrument %d\n"), i );
378
sDump += pInstr->Dump();
383
wxString lmScore::SourceLDP()
386
wxString::Format(_T("Partitura\n (Vers 1.3)\n (NumInstrumentos %d)\n"),
387
m_cInstruments.GetCount() );
389
//loop for each instrument
390
lmInstrument *pInstr = GetFirstInstrument();
391
for (int i=1; i<= (int)m_cInstruments.GetCount(); i++, pInstr = GetNextInstrument())
393
sSource += wxString::Format(_T(" (Instrumento %d\n"), i);
394
sSource += pInstr->SourceLDP();
395
sSource += _T(" )\n");
401
wxString lmScore::SourceXML()
403
wxString sSource = _T("TODO: lmScore XML Source code generation methods");
405
// Dim i As Long, sFuente As String
407
// sFuente = "<?xml version=""1.0"" standalone=""no""?>" & sCrLf & _
408
// "<!DOCTYPE score-partwise PUBLIC ""-//Recordare//DTD MusicXML 0.7 Partwise//EN"" " & _
409
// """http://www.musicxml.org/dtds/partwise.dtd"">" & sCrLf & _
410
// "<score-partwise>" & sCrLf & _
411
// " <part-list>" & sCrLf
412
//// <score-part id="P1">
413
//// <part-name>Voice</part-name>
416
//// sFuente = sFuente & "<Partitura" & sCrLf & " (Vers 1.3)" & sCrLf & " (NumInstrumentos " & _
417
//// m_cInstruments.Count & ")" & sCrLf
418
// for (i = 1 To m_cInstruments.Count
419
// sFuente = sFuente & " <score-part id=""P" & i & """ >" & sCrLf
420
//// sFuente = sFuente & m_cInstruments.Item(i).FuenteXML
421
//// sFuente = sFuente & " )" & sCrLf
422
// sFuente = sFuente & " </score-part>" & sCrLf
424
// sFuente = sFuente & " </part-list>" & sCrLf
425
// for (i = 1 To m_cInstruments.Count
426
// sFuente = sFuente & " <part id=""P" & i & """ >" & sCrLf
427
// sFuente = sFuente & m_cInstruments.Item(i).FuenteXML
428
// sFuente = sFuente & " </part>" & sCrLf
430
// FuenteXML = sFuente & "</score-partwise>" & sCrLf
435
////Toca el compas nCompas (1 .. n)
436
//void lmScore::PlayMeasure(nCompas As Long, fVisualTracking As Boolean, _
437
// fRecuadrarCompas As Boolean, nPlayMode As EPlayMode)
440
// if (!fMIDIEnabled) return;
441
// if (m_pSoundMngr Is Nothing) { ComputeMidiEvents
443
// m_pSoundMngr->PlayMeasure nCompas, fVisualTracking, fRecuadrarCompas, nPlayMode
447
void lmScore::Play(bool fVisualTracking, bool fMarcarCompasPrevio, EPlayMode nPlayMode,
448
long nMM, wxWindow* pWindow)
451
m_pSoundMngr = new lmSoundManager();
455
m_pSoundMngr->Play(fVisualTracking, fMarcarCompasPrevio, nPlayMode, nMM, pWindow);
459
void lmScore::PlayMeasure(int nMeasure, bool fVisualTracking, EPlayMode nPlayMode,
460
long nMM, wxWindow* pWindow)
463
m_pSoundMngr = new lmSoundManager();
467
m_pSoundMngr->PlayMeasure(nMeasure, fVisualTracking, nPlayMode, nMM, pWindow);
470
void lmScore::Pause()
472
if (!m_pSoundMngr) return;
473
m_pSoundMngr->Pause();
479
if (!m_pSoundMngr) return;
480
m_pSoundMngr->Stop();
484
void lmScore::WaitForTermination()
486
if (!m_pSoundMngr) return;
487
m_pSoundMngr->WaitForTermination();
491
void lmScore::ScoreHighlight(lmStaffObj* pSO, lmPaper* pPaper, EHighlightType nHighlightType)
493
switch (nHighlightType) {
495
m_cHighlighted.Append(pSO);
496
pSO->Draw(DO_DRAW, pPaper, g_pColors->ScoreHighlight() );
500
m_cHighlighted.DeleteObject(pSO);
501
RemoveHighlight(pSO, pPaper);
504
case eRemoveAllHighlight:
505
//remove highlight from all staffobjs in m_cHighlighted list
506
wxStaffObjsListNode* pNode;
507
for(pNode = m_cHighlighted.GetFirst(); pNode; pNode = pNode->GetNext()) {
508
RemoveHighlight( (lmStaffObj*)pNode->GetData(), pPaper );
511
m_cHighlighted.DeleteContents(false); //Staffobjs must not be deleted, only the list
512
m_cHighlighted.Clear();
521
void lmScore::RemoveHighlight(lmStaffObj* pSO, lmPaper* pPaper)
524
If we paint in black it remains a red aureole around
525
the note. By painting it first in white the size of the aureole
526
is smaller but still visible. A posible better solution is to
527
modify Draw() method to accept an additional parameter: a flag
528
to signal that XOR draw mode in RED followed by a normal
529
draw in BLACK must be done.
531
pSO->Draw(DO_DRAW, pPaper, *wxWHITE);
532
pSO->Draw(DO_DRAW, pPaper, g_pColors->ScoreNormal() );
535
void lmScore::ComputeMidiEvents()
537
int nChannel, nInstr; //MIDI info. for instrument in process
541
m_pSoundMngr->DeleteEventsTable();
543
m_pSoundMngr = new lmSoundManager();
545
//Loop to generate Midi events for each instrument
547
lmInstrument *pInstr = GetFirstInstrument();
548
for (int i=1; i<= (int)m_cInstruments.GetCount(); i++, pInstr = GetNextInstrument())
550
nChannel = pInstr->GetMIDIChannel();
551
nInstr = pInstr->GetMIDIInstrument();
554
for (int iVStaff=1; iVStaff <= pInstr->GetNumStaves(); iVStaff++) {
555
pVStaff = pInstr->GetVStaff(iVStaff);
556
pSM = pVStaff->ComputeMidiEvents(nChannel);
557
m_pSoundMngr->Append(pSM);
561
//Add an event to program sound for this instrument
562
m_pSoundMngr->StoreEvent(0, eSET_ProgInstr, nChannel, nInstr, 0, (lmStaffObj*)NULL, 0);
566
//End up Midi events table and sort it by time
567
m_pSoundMngr->CloseTable();
571
wxString lmScore::DumpMidiEvents()
573
if (!m_pSoundMngr) ComputeMidiEvents();
574
return m_pSoundMngr->DumpMidiEvents();