~s-cecilio/lenmus/v5.3

« back to all changes in this revision

Viewing changes to src/auxmusic/lenmus_composer.cpp

  • Committer: cecilios
  • Date: 2007-05-19 11:39:03 UTC
  • Revision ID: svn-v4:2587a929-2f0e-0410-ae78-fe6f687d5efe:trunk:236

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
//---------------------------------------------------------------------------------------
2
 
//    LenMus Phonascus: The teacher of music
3
 
//    Copyright (c) 2002-2012 LenMus project
4
 
//
5
 
//    This program is free software; you can redistribute it and/or modify it under the
6
 
//    terms of the GNU General Public License as published by the Free Software Foundation,
7
 
//    either version 3 of the License, or (at your option) any later version.
8
 
//
9
 
//    This program is distributed in the hope that it will be useful, but WITHOUT ANY
10
 
//    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11
 
//    PARTICULAR PURPOSE.  See the GNU General Public License for more details.
12
 
//
13
 
//    You should have received a copy of the GNU General Public License along with this
14
 
//    program. If not, see <http://www.gnu.org/licenses/>.
15
 
//
16
 
//    For any comment, suggestion or feature request, please contact the manager of
17
 
//    the project at cecilios@users.sourceforge.net
18
 
//
19
 
//---------------------------------------------------------------------------------------
20
 
 
21
 
//lenmus
22
 
#include "lenmus_composer.h"
23
 
 
24
 
#include "lenmus_generators.h"
25
 
#include "lenmus_utilities.h"
26
 
 
27
 
//lomse
28
 
#include <lomse_internal_model.h>
29
 
#include <lomse_im_note.h>
30
 
#include <lomse_im_factory.h>
31
 
#include <lomse_score_utilities.h>
32
 
#include <lomse_staffobjs_table.h>
33
 
#include <lomse_staffobjs_cursor.h>
34
 
#include <lomse_time.h>
35
 
using namespace lomse;
36
 
 
37
 
////wxWidgets
38
 
//#include <wx/wxprec.h>
39
 
//#include <wx/arrstr.h>      // to use wxArrayString
40
 
 
41
 
//other
42
 
#include <algorithm>
43
 
//#include <vector>
44
 
 
45
 
#define TRACE_COMPOSER  0
46
 
#define TRACE_PITCH     0
47
 
 
48
 
 
49
 
namespace lenmus
50
 
{
51
 
 
52
 
//---------------------------------------------------------------------------------------
53
 
enum lmEHarmonicFunction
54
 
{
55
 
    lmTONIC             = 0x00000001,   // I
56
 
    lmSUPERTONIC        = 0x00000002,   // ii
57
 
    lmMEDIANT           = 0x00000003,   // iii
58
 
    lmSUBDOMINANT       = 0x00000004,   // IV
59
 
    lmDOMINANT          = 0x00000005,   // V
60
 
    lmSUBMEDIANT        = 0x00000006,   // vi
61
 
    lmLEADING           = 0x00000007,   // vii
62
 
    lmSUBTONIC          = 0x00000007,   // vii
63
 
 
64
 
    // alias
65
 
    lm_I                = 0x00000001,
66
 
    lm_II               = 0x00000002,
67
 
    lm_III              = 0x00000003,
68
 
    lm_IV               = 0x00000004,
69
 
    lm_V                = 0x00000005,
70
 
    lm_VI               = 0x00000006,
71
 
    lm_VII              = 0x00000007,
72
 
 
73
 
 
74
 
    lmROOT_POSITION     = 0x00000008,
75
 
    lmFIRST_INVERSION   = 0x00000010,
76
 
    lmSECOND_INVERSION  = 0x00000020,
77
 
    lmTHIRD_INVERSION   = 0x00000040,
78
 
 
79
 
    lmSEVENTH_ADDED     = 0x00000100,
80
 
 
81
 
    // masks
82
 
    lmGRADE_MASK        = 0x00000007,   // to extract grade
83
 
};
84
 
 
85
 
//---------------------------------------------------------------------------------------
86
 
// table of typical harmonic progressions, to base compositions on them
87
 
static lmEHarmonicFunction m_aProgression[][8] =
88
 
{
89
 
    {lm_I,  lm_V,   lm_I,   lm_IV,  lm_II,  lm_III, lm_IV,  lm_I },
90
 
    {lm_I,  lm_II,  lm_V,   lm_I,   lm_III, lm_IV,  lm_V,   lm_I },
91
 
 
92
 
};
93
 
 
94
 
 
95
 
 
96
 
//---------------------------------------------------------------------------------------
97
 
Composer::Composer()
98
 
{
99
 
}
100
 
 
101
 
//---------------------------------------------------------------------------------------
102
 
Composer::~Composer()
103
 
{
104
 
}
105
 
 
106
 
//---------------------------------------------------------------------------------------
107
 
ImoScore* Composer::GenerateScore(ScoreConstrains* pConstrains, Document* pDoc)
108
 
{
109
 
    //Synthesises a score
110
 
    //
111
 
    //Algorithm 1 to generate N measures
112
 
    //----------------------------------
113
 
    //
114
 
    //    1. Determine how may beats we have to generate:
115
 
    //            BeatsToGenerate = Beats_per_measure x N
116
 
    //            NumBeats = 0
117
 
    //    2. while (NumBeats < BeatsToGenerate) {
118
 
    //        2.1 Randomly choose a pattern satisfying the constraints (lesson, level, time
119
 
    //            signature, etc.).
120
 
    //        2.2 Instantiate the choosen pattern by assingning note pitches
121
 
    //        2.3 NumBeats = NumBeats + num. beats in choosen pattern
122
 
    //    }
123
 
    //    3. Divide generated beats string into measures (just assign to each measure the
124
 
    //       next n beats -- where n=Beats_per_measure -- )
125
 
    //
126
 
    //    Problems:
127
 
    //        - difficulties for using patterns that are not full measures
128
 
    //        - difficulties for aligning patterns to barlines at specific points
129
 
    //        - strong limitation: requires patterns divided into beats. It would be
130
 
    //            impossible, for example, include a dotted quarter note in a binary
131
 
    //            measure.
132
 
    //
133
 
    //
134
 
    //
135
 
    //Algorithm 2 to generate N measures
136
 
    //----------------------------------
137
 
    //    Rationale:
138
 
    //        Instead of generating beats, let's organize the main loop around generating
139
 
    //        measures.
140
 
    //        Lets use patterns that contains musical phrases instead of full measures.
141
 
    //        Patterns will contain, when necessary, barline alignment information.
142
 
    //        Before adding a pattern to the current measure, lets align the pattern by
143
 
    //        inserting rests, if required.
144
 
    //
145
 
    //
146
 
    //    while (NumMeasures < MeasuresToGenerate) {
147
 
    //        If (no measure is opened) start a measure
148
 
    //        If (no beats in pattern) {
149
 
    //            Randomly choose a pattern satisfying the constraints (lesson,
150
 
    //                level, time signature, etc.).
151
 
    //            Instantiate the choosen pattern by assingning note pitches.
152
 
    //            Align pattern to measure by adding any required rest to the measure
153
 
    //        }
154
 
    //        While (there are beats in the choosen pattern and measure not full) {
155
 
    //            add beats.
156
 
    //        }
157
 
    //        If measure full {
158
 
    //            Close measure
159
 
    //            NumMeasures++
160
 
    //        }
161
 
    //    }
162
 
    //
163
 
    //
164
 
    //Algorithm 3 to generate N measures (8/Feb/06)
165
 
    //---------------------------------------------
166
 
    //
167
 
    //    Rationale:
168
 
    //        Algorithm 2 generates scores with mis-aligments and irregular measures.
169
 
    //        The problem is due to lack of information about alignment. So in this
170
 
    //        improved algorithm I will also use patterns containing musical phrases
171
 
    //        instead of full measures.
172
 
    //        These patterns will be called 'fragments'. Fragments are divided into
173
 
    //        'segments' (a segment is a group of elements - notes and rests - that must
174
 
    //        go together and occupies one or more full beats).
175
 
    //        Fragments will contain baline alignment information.
176
 
    //
177
 
    //        The algorithm is essentialy the same, but the method to add beats is
178
 
    //        improved by taking into account alignment and duration information. Also
179
 
    //        it is necessary to take into account that a segment may contain one or
180
 
    //        more beats (not necessarily full beats).
181
 
    //
182
 
    //        Another improvement is generating the final measure not by using fragments
183
 
    //        but by generation a note lasting one ore more beats. And its pitch is
184
 
    //        the root pitch of the used key signature.
185
 
    //
186
 
    //        Notes.
187
 
    //        - It is assumed that all beats are equal. Therefore, current algorithm
188
 
    //          works only for regular time signature rhythms. It will
189
 
    //          not work, for example, with 7/8.
190
 
    //
191
 
    //
192
 
    //
193
 
    //    while (NumMeasures < MeasuresToGenerate - 1 ) {
194
 
    //        If (no measure is opened) start a measure
195
 
    //        If (no beats in pattern) {
196
 
    //            Randomly choose a pattern satisfying the constraints (lesson,
197
 
    //                level, time signature, etc.).
198
 
    //            Instantiate the choosen pattern by assingning note pitches.
199
 
    //        }
200
 
    //        While (there are beats in the choosen pattern and measure not full) {
201
 
    //            add beats.
202
 
    //        }
203
 
    //        If measure full {
204
 
    //            Close measure
205
 
    //            NumMeasures++
206
 
    //        }
207
 
    //    }
208
 
    //    Add final measure
209
 
    //
210
 
    //
211
 
 
212
 
    //Save parameters
213
 
    m_pConstrains = pConstrains;
214
 
 
215
 
    //Generate a random key, time signature and clef satisfying the constrains
216
 
    m_nClef = RandomGenerator::generate_clef(m_pConstrains->GetClefConstrains());
217
 
    m_nKey = RandomGenerator::generate_key(m_pConstrains->GetKeyConstrains());
218
 
    m_nTimeSign = RandomGenerator::GenerateTimeSign(m_pConstrains->GetTimeSignConstrains());
219
 
    bool fCompound = get_num_ref_notes_per_pulse_for(m_nTimeSign) != 1;
220
 
 
221
 
    // prepare and initialize the score
222
 
    ImoScore* pScore = static_cast<ImoScore*>(ImFactory::inject(k_imo_score, pDoc));
223
 
    ImoInstrument* pInstr = pScore->add_instrument();
224
 
    // (g_pMidi->DefaultVoiceChannel(), g_pMidi->DefaultVoiceInstr(), _T(""));
225
 
    ImoSystemInfo* pInfo = pScore->get_first_system_info();
226
 
    pInfo->set_top_system_distance( pInstr->tenths_to_logical(30) );     // 3 lines
227
 
    pInstr->add_clef(m_nClef);
228
 
    pInstr->add_key_signature(m_nKey);
229
 
 
230
 
#if 0   //useful for debugging and to generate scores with a chosen rhythm line to write documentation
231
 
    pInstr->add_time_signature(2, 4);
232
 
    pInstr->add_staff_objects("(r e)(n * e)(n * e g+)(n * e l g-)(barline simple)");
233
 
    pInstr->add_staff_objects("(n * e g+)(n * s)(n * s g-)(n * e g+)(n * e g-)(barline simple)");
234
 
    pInstr->add_staff_objects("(n * q)(r e)(n * e)(barline simple)");
235
 
    pInstr->add_staff_objects("(n * s g+)(n * s)(n * s)(n * s g-)(n * e g+)(n * e l g-)(barline simple)");
236
 
    pInstr->add_staff_objects("(n * e g+)(n * s)(n * s g-)(n * s g+)(n * s)(n * s)(n * s g-)(barline simple)");
237
 
    pInstr->add_staff_objects("(n * s g+)(n * s)(n * s)(n * s g-)(n * e g+)(n * e l g-)(barline simple)");
238
 
    pInstr->add_staff_objects("(n * q)(r e)(n * e)(barline simple)");
239
 
    pInstr->add_staff_objects("(n * b)(barline end)");
240
 
    pScore->close();
241
 
    return pScore;
242
 
#else
243
 
    int beats = get_top_number_for(m_nTimeSign);
244
 
    int type = get_bottom_number_for(m_nTimeSign);
245
 
    pInstr->add_time_signature(beats, type);
246
 
#endif
247
 
 
248
 
    //
249
 
    // Content generation
250
 
    //
251
 
 
252
 
    // Determine how may measures we have to generate:
253
 
    #define NUM_MEASURES   8        //num of measures to generate
254
 
    int nMeasuresToGenerate = NUM_MEASURES - 1;
255
 
    int nNumMeasures = 0;
256
 
    float rMeasureDuration = get_measure_duration_for(m_nTimeSign);       //tm
257
 
    float rBeatDuration = get_ref_note_duration_for(m_nTimeSign);             //tb
258
 
    float rTimeRemaining;           //tr
259
 
    float rSegmentDuration;         //ts
260
 
    float rConsumedBeatTime;        //tcb
261
 
    float rSegmentAlignBeatTime;    //tab
262
 
 
263
 
 
264
 
    // Loop to generate the required measures
265
 
    wxString sMeasure;                //source code of current measure
266
 
    bool fFits;                     //current segment fits in current measure
267
 
    float rOccupiedDuration;        //consumed time in current measure (tc)
268
 
    bool fMeasure = false;          //there is a measure started
269
 
    SegmentEntry* pSegment;       //segment to add to measure
270
 
 
271
 
    //select all usable fragments for current time signature
272
 
    if (m_pConstrains->SelectFragments(m_nTimeSign) == 0)
273
 
    {
274
 
        //TODO: error logging. Suppress message
275
 
        wxLogMessage(_T("[Composer::GenerateScore] No usable fragments!"));
276
 
        return pScore;
277
 
    }
278
 
 
279
 
    //chose ramdomly a fragment satisfying the constraints, and take the first segment
280
 
    pConstrains->ChooseRandomFragment();
281
 
    pSegment = pConstrains->GetNextSegment();
282
 
    //TODO: what if no fragment satisfies the constraints?
283
 
 
284
 
    int nSegmentLoopCounter = 0;
285
 
    while (nNumMeasures < nMeasuresToGenerate)
286
 
    {
287
 
        //If no measure is opened start a new measure
288
 
        if (!fMeasure)
289
 
        {
290
 
            sMeasure = _T("");
291
 
            rOccupiedDuration = 0.0;
292
 
            fMeasure = true;
293
 
        }
294
 
 
295
 
        //If there are no more segments in current fragment, choose a new fragment
296
 
        if (!pSegment)
297
 
        {
298
 
            //Randomly choose a new fragment satisfying the constraints
299
 
            pConstrains->ChooseRandomFragment();
300
 
            pSegment = pConstrains->GetNextSegment();
301
 
            wxASSERT(pSegment);     //there must exits a fragment satisfying
302
 
                                    //the constraints. Otherwise this would have been
303
 
                                    //detected before entering the main while loop
304
 
        }
305
 
 
306
 
        //While (there are segments in the current fragment and the measure is not full) {
307
 
        while (pSegment && rOccupiedDuration < rMeasureDuration)
308
 
        {
309
 
            //check if segment fits in. A segment S will fit in the measure
310
 
            //only when (tr >= ts && tcb <= tab)
311
 
 
312
 
            rTimeRemaining = rMeasureDuration - rOccupiedDuration;
313
 
            rSegmentDuration = pSegment->GetSegmentDuration();
314
 
            rConsumedBeatTime = rOccupiedDuration;  //this line and next two ones compute tcb = tc % tb;
315
 
            while (is_greater_time(rConsumedBeatTime, 0.0f))
316
 
                rConsumedBeatTime -= rBeatDuration;
317
 
            if (rConsumedBeatTime < 0.0)
318
 
                rConsumedBeatTime += rBeatDuration;
319
 
            rSegmentAlignBeatTime = pSegment->GetTimeAlignBeat();
320
 
            fFits = (!is_lower_time(rTimeRemaining, rSegmentDuration)
321
 
                     && !is_greater_time(rConsumedBeatTime, rSegmentAlignBeatTime));
322
 
 
323
 
            #if (TRACE_COMPOSER == 1)
324
 
            wxLogMessage(_T("[Composer::GenerateScore] sMeasure=%s, pSegment=%s, tr=%.2f, ts=%.2f, tcb=%.2f, tab=%.2f, tc=%.2f, tb=%.2f, fits=%s"),
325
 
                    sMeasure.c_str(),
326
 
                    (pSegment->GetSource()).c_str(), rTimeRemaining, rSegmentDuration,
327
 
                    rConsumedBeatTime, rSegmentAlignBeatTime,
328
 
                    rOccupiedDuration, rBeatDuration,
329
 
                    (fFits ? _T("yes") : _T("no")) );
330
 
            #endif
331
 
 
332
 
            //if segment fits add it to current measure
333
 
            if (fFits)
334
 
            {
335
 
                //it fits. Add it to current measure
336
 
                float rNoteTime = rSegmentAlignBeatTime - rConsumedBeatTime;
337
 
                if (is_greater_time(rNoteTime, 0.0f))
338
 
                {
339
 
                    if (rConsumedBeatTime > 0.0f)
340
 
                        sMeasure += CreateNote((int)rNoteTime, fCompound, false /*not final note*/);
341
 
                    else
342
 
                        sMeasure += CreateRest((int)rNoteTime, fCompound, false /*not final rest*/);
343
 
                }
344
 
 
345
 
                //add segment
346
 
                sMeasure += pSegment->GetSource();
347
 
                #if (TRACE_COMPOSER == 1)
348
 
                wxLogMessage(_T("[Composer::GenerateScore] Adding segment. Measure = '%s')"),
349
 
                             sMeasure.c_str());
350
 
                #endif
351
 
 
352
 
                //update tr
353
 
                rOccupiedDuration += rSegmentDuration + rNoteTime;
354
 
 
355
 
                //get next segment
356
 
                pSegment = pConstrains->GetNextSegment();
357
 
                nSegmentLoopCounter = 0;
358
 
            }
359
 
            else
360
 
            {
361
 
                //does not fit.
362
 
                #if (TRACE_COMPOSER == 1)
363
 
                wxLogMessage(_T("[Composer::GenerateScore] Segment does not fit. Ignored"));
364
 
                #endif
365
 
                if (nSegmentLoopCounter++ > 100)
366
 
                {
367
 
                    //let's assume that no segment fits. Fill the measure with a note
368
 
                    sMeasure += CreateNote((int)rTimeRemaining, fCompound, false /*not final note*/);
369
 
                    rOccupiedDuration += rTimeRemaining;
370
 
                    nSegmentLoopCounter = 0;
371
 
                }
372
 
                else
373
 
                {
374
 
                    // Ignore segment and take a new one
375
 
                    pSegment = pConstrains->GetNextSegment();
376
 
                }
377
 
            }
378
 
        }
379
 
 
380
 
        // if measure is full, close it and increment measures count
381
 
       if (rOccupiedDuration >= rMeasureDuration)
382
 
       {
383
 
 
384
 
            // close current measure
385
 
            fMeasure = false;   // no measure opened
386
 
            sMeasure += _T("(barline simple)");
387
 
 
388
 
            // increment measures counter
389
 
            nNumMeasures++;
390
 
 
391
 
            // Instantiate the notes by assigning note pitches and add
392
 
            // the measure to the score
393
 
            #if (TRACE_COMPOSER == 1)
394
 
            wxLogMessage(_T("[Composer::GenerateScore] Adding measure = '%s')"),
395
 
                         sMeasure.c_str());
396
 
            #endif
397
 
            pInstr->add_staff_objects( to_std_string(sMeasure) );
398
 
        }
399
 
 
400
 
    }
401
 
 
402
 
    pScore->close();    //generate ColStaffObjs, to traverse it in following code lines
403
 
 
404
 
    // In Music Reading, level 1, introduction lessons use only quarter notes. In those
405
 
    // exercises we should not use half notes in the last measure. So lets check if
406
 
    // only quarter notes are used in the composed piece of music.
407
 
    bool fOnlyQuarterNotes = true;
408
 
    ColStaffObjs* pColStaffObjs = pScore->get_staffobjs_table();
409
 
    ColStaffObjs::iterator it = pColStaffObjs->begin();
410
 
    while(it != pColStaffObjs->end())
411
 
    {
412
 
        ImoObj* pImo = (*it)->imo_object();
413
 
        if (pImo->is_note())
414
 
        {
415
 
            //Note fount. Take duration
416
 
            ImoNote* pNote = static_cast<ImoNote*>(pImo);
417
 
            fOnlyQuarterNotes &= is_equal_time(pNote->get_duration(),
418
 
                                               float(k_duration_quarter) );
419
 
            if (!fOnlyQuarterNotes)
420
 
                break;
421
 
        }
422
 
        ++it;
423
 
    }
424
 
 
425
 
    #if (TRACE_COMPOSER == 1)
426
 
    wxLogMessage(_T("[Composer::GenerateScore] fOnlyQuarterNotes=%s)"),
427
 
            (fOnlyQuarterNotes ? _T("True") : _T("False")) );
428
 
    #endif
429
 
 
430
 
    // add a final measure with a root pitch note lasting, at least, one beat
431
 
    sMeasure = CreateLastMeasure(++nNumMeasures, m_nTimeSign, fOnlyQuarterNotes);
432
 
    pInstr->add_staff_objects( to_std_string(sMeasure) );
433
 
 
434
 
    pScore->close();
435
 
 
436
 
    #if (TRACE_COMPOSER == 1)
437
 
    wxLogMessage(_T("[Composer::GenerateScore] Adding final measure = '%s')"), sMeasure.c_str());
438
 
    #endif
439
 
 
440
 
 
441
 
     //Score is built but pitches are not yet defined.
442
 
     //Proceed to instatiate pitches according to key signature
443
 
    GetNotesRange();
444
 
    //pScore->Dump(_T("lemus_score_dump.txt"));
445
 
 
446
 
#if 0   //useful to generate only the rhymth line, to write documenation
447
 
    InstantiateWithNote(pScore, FPitch("b3") );
448
 
#else
449
 
    InstantiateNotes(pScore, m_nKey, nNumMeasures);
450
 
#endif
451
 
 
452
 
    // done
453
 
    //pScore->Dump(_T("lemus_score_dump.txt"));
454
 
    return pScore;
455
 
}
456
 
 
457
 
//---------------------------------------------------------------------------------------
458
 
void Composer::GetNotesRange()
459
 
{
460
 
    //get the minimum and maximum notes
461
 
    wxString sMinPitch = (m_pConstrains->GetClefConstrains())->GetLowerPitch(m_nClef);
462
 
    m_fpMinPitch = FPitch( to_std_string(sMinPitch) );
463
 
 
464
 
    wxString sMaxPitch = (m_pConstrains->GetClefConstrains())->GetUpperPitch(m_nClef);
465
 
    m_fpMaxPitch = FPitch( to_std_string(sMaxPitch) );
466
 
}
467
 
 
468
 
//---------------------------------------------------------------------------------------
469
 
wxString Composer::CreateNoteRest(int nNoteRestDuration, bool fNote, bool fCompound,
470
 
                                  bool fFinal)
471
 
{
472
 
    //Returns a string with one or more LDP elements containing notes o rests up to a total
473
 
    //duration nNoteDuration. They will be notes if fNote==true; otherwise they will be rests.
474
 
    //For example, for nNoteDuration=64 it will return "(n * n)"
475
 
 
476
 
    wxString sElement = _T("");
477
 
    int nDuration;
478
 
    int nTimeNeeded = nNoteRestDuration;
479
 
 
480
 
    if (fCompound && fFinal)
481
 
    {
482
 
        while (nTimeNeeded > 0)
483
 
        {
484
 
            sElement += (fNote ? _T("(n * ") : _T("(r ") );
485
 
            if (nTimeNeeded >= k_duration_whole_dotted)
486
 
            {
487
 
                sElement += _T("w.)");
488
 
                nDuration = k_duration_whole_dotted;
489
 
            }
490
 
            else if (nTimeNeeded >= k_duration_half_dotted)
491
 
            {
492
 
                sElement += _T("h.)");
493
 
                nDuration = k_duration_half_dotted;
494
 
            }
495
 
            else if (nTimeNeeded >= k_duration_quarter_dotted)
496
 
            {
497
 
                sElement += _T("q.)");
498
 
                nDuration = k_duration_quarter_dotted;
499
 
            }
500
 
            else if (nTimeNeeded >= k_duration_eighth_dotted)
501
 
            {
502
 
                sElement += _T("e.)");
503
 
                nDuration = k_duration_eighth_dotted;
504
 
            }
505
 
            else if (nTimeNeeded >= k_duration_16th_dotted)
506
 
            {
507
 
                sElement += _T("s.)");
508
 
                nDuration = k_duration_16th_dotted;
509
 
            }
510
 
            else if (nTimeNeeded >= k_duration_32th_dotted)
511
 
            {
512
 
                sElement += _T("t.)");
513
 
                nDuration = k_duration_32th_dotted;
514
 
            }
515
 
            else if (nTimeNeeded >= k_duration_64th_dotted)
516
 
            {
517
 
                sElement += _T("i.)");
518
 
                nDuration = k_duration_64th_dotted;
519
 
            }
520
 
            else if (nTimeNeeded >= k_duration_128th_dotted)
521
 
            {
522
 
                sElement += _T("o.)");
523
 
                nDuration = k_duration_128th_dotted;
524
 
            }
525
 
            else
526
 
            {
527
 
                sElement += _T("f)");
528
 
                nDuration = k_duration_256th;
529
 
            }
530
 
 
531
 
            nTimeNeeded -= nDuration;
532
 
        }
533
 
    }
534
 
    else
535
 
    {
536
 
        while (nTimeNeeded > 0)
537
 
        {
538
 
            sElement += (fNote ? _T("(n * ") : _T("(r ") );
539
 
            if (nTimeNeeded >= k_duration_whole_dotted)
540
 
            {
541
 
                sElement += _T("w.)");
542
 
                nDuration = k_duration_whole_dotted;
543
 
            }
544
 
            else if (nTimeNeeded >= k_duration_whole)
545
 
            {
546
 
                sElement += _T("w)");
547
 
                nDuration = k_duration_whole;
548
 
            }
549
 
            else if (nTimeNeeded >= k_duration_half_dotted)
550
 
            {
551
 
                sElement += _T("h.)");
552
 
                nDuration = k_duration_half_dotted;
553
 
            }
554
 
            else if (nTimeNeeded >= k_duration_half)
555
 
            {
556
 
                sElement += _T("h)");
557
 
                nDuration = k_duration_half;
558
 
            }
559
 
            else if (nTimeNeeded >= k_duration_quarter_dotted)
560
 
            {
561
 
                sElement += _T("q.)");
562
 
                nDuration = k_duration_quarter_dotted;
563
 
            }
564
 
            else if (nTimeNeeded >= k_duration_quarter)
565
 
            {
566
 
                sElement += _T("q)");
567
 
                nDuration = k_duration_quarter;
568
 
            }
569
 
            else if (nTimeNeeded >= k_duration_eighth_dotted)
570
 
            {
571
 
                sElement += _T("e.)");
572
 
                nDuration = k_duration_eighth_dotted;
573
 
            }
574
 
            else if (nTimeNeeded >= k_duration_eighth)
575
 
            {
576
 
                sElement += _T("e)");
577
 
                nDuration = k_duration_eighth;
578
 
            }
579
 
            else if (nTimeNeeded >= k_duration_16th_dotted)
580
 
            {
581
 
                sElement += _T("s.)");
582
 
                nDuration = k_duration_16th_dotted;
583
 
            }
584
 
            else if (nTimeNeeded >= k_duration_16th)
585
 
            {
586
 
                sElement += _T("s)");
587
 
                nDuration = k_duration_16th;
588
 
            }
589
 
            else if (nTimeNeeded >= k_duration_32th_dotted)
590
 
            {
591
 
                sElement += _T("t.)");
592
 
                nDuration = k_duration_32th_dotted;
593
 
            }
594
 
            else if (nTimeNeeded >= k_duration_32th)
595
 
            {
596
 
                sElement += _T("t)");
597
 
                nDuration = k_duration_32th;
598
 
            }
599
 
            else if (nTimeNeeded >= k_duration_64th_dotted)
600
 
            {
601
 
                sElement += _T("i.)");
602
 
                nDuration = k_duration_64th_dotted;
603
 
            }
604
 
            else if (nTimeNeeded >= k_duration_64th)
605
 
            {
606
 
                sElement += _T("i)");
607
 
                nDuration = k_duration_64th;
608
 
            }
609
 
            else if (nTimeNeeded >= k_duration_128th_dotted)
610
 
            {
611
 
                sElement += _T("o.)");
612
 
                nDuration = k_duration_128th_dotted;
613
 
            }
614
 
            else if (nTimeNeeded >= k_duration_128th)
615
 
            {
616
 
                sElement += _T("o)");
617
 
                nDuration = k_duration_128th;
618
 
            }
619
 
            else
620
 
            {
621
 
                sElement += _T("f)");
622
 
                nDuration = k_duration_256th;
623
 
            }
624
 
 
625
 
            nTimeNeeded -= nDuration;
626
 
        }
627
 
    }
628
 
 
629
 
    #if (TRACE_COMPOSER == 1)
630
 
    wxLogMessage(_T("[Composer::CreateNoteRest] Needed duration= %d, added=%s"),
631
 
        nNoteRestDuration, sElement.c_str());
632
 
    #endif
633
 
 
634
 
    return sElement;
635
 
}
636
 
 
637
 
//---------------------------------------------------------------------------------------
638
 
wxString Composer::CreateLastMeasure(int nNumMeasure, ETimeSignature nTimeSign,
639
 
                                     bool fOnlyQuarterNotes)
640
 
{
641
 
    // Returns a final meaure. This final measure has only a note, long enough, and
642
 
    // a final bar
643
 
 
644
 
    wxString sMeasure = _T("");
645
 
    float rMeasureDuration = get_measure_duration_for(nTimeSign);
646
 
    float rPulseDuration = get_ref_note_duration_for(nTimeSign) *
647
 
                            get_num_ref_notes_per_pulse_for(nTimeSign);
648
 
    float rNoteDuration = rPulseDuration;
649
 
    bool fCompound = (get_num_ref_notes_per_pulse_for(nTimeSign) != 1);
650
 
    if (!fOnlyQuarterNotes && rMeasureDuration / rPulseDuration >= 2.0)
651
 
    {
652
 
        //flip coin to randomly add a one-beat note or a two-beats note
653
 
        if (RandomGenerator::flip_coin())
654
 
            rNoteDuration += rPulseDuration;
655
 
    }
656
 
 
657
 
    sMeasure += CreateNote((int)rNoteDuration, fCompound, true /*final note*/);
658
 
    rNoteDuration = rMeasureDuration - rNoteDuration;
659
 
    if (rNoteDuration > 0.0f)
660
 
        sMeasure += CreateRest((int)rNoteDuration, fCompound, true /*final rest*/);
661
 
 
662
 
    sMeasure += _T("(barline end)");
663
 
    return sMeasure;
664
 
}
665
 
 
666
 
//---------------------------------------------------------------------------------------
667
 
// Methods to deal with tonality
668
 
//---------------------------------------------------------------------------------------
669
 
 
670
 
bool Composer::InstantiateNotes(ImoScore* pScore, EKeySignature nKey, int nNumMeasures)
671
 
{
672
 
    // Returns true if error
673
 
 
674
 
    // Choose a chord progression, based on key signature: nChords[]
675
 
    std::vector<long> nChords(nNumMeasures);
676
 
    GetRandomHarmony(nNumMeasures, nChords);
677
 
 
678
 
    // Lets compute the notes in the natural scale of the key signature to use
679
 
    // This will be used later in various places
680
 
    FPitch scale[7];             // the notes in the scale
681
 
    GenerateScale(nKey, scale);
682
 
 
683
 
    // In a later step we are goin to choose and compute a contour curve.
684
 
    // The contour curve will have as many points as on-chord notes in the music line.
685
 
    // So first we have to compute the number of on-chord notes. The following code is a
686
 
    // loop to count on-chord notes in first staff of first instrument
687
 
    // and to locate last note. This is necessary to assign it the root pitch (later)
688
 
//    ImoNote* pLastNote = NULL;
689
 
    int nNumPoints = 0;
690
 
    ImoTimeSignature* pTS = NULL;
691
 
    StaffObjsCursor cursor(pScore);
692
 
    while(!cursor.is_end())
693
 
    {
694
 
        ImoStaffObj* pSO = cursor.get_staffobj();
695
 
        if (pSO->is_note())
696
 
        {
697
 
//            ImoNote* pNote = static_cast<ImoNote*>(pSO);
698
 
            int pos = k_off_beat;
699
 
            if (pTS)
700
 
                pos = get_beat_position(cursor.time(), pTS);
701
 
 
702
 
            if(pos != k_off_beat)
703
 
                nNumPoints++;   // on beat note
704
 
 
705
 
//            pLastNote = pNote;
706
 
        }
707
 
        else if (pSO->is_time_signature())
708
 
        {
709
 
            pTS = static_cast<ImoTimeSignature*>( pSO );
710
 
        }
711
 
 
712
 
        cursor.move_next();
713
 
    }
714
 
 
715
 
 
716
 
    // If number of points is small (i.e < 8) forget about this. Instatiate notes
717
 
    // with random pitch and finish. This bypasess the only problem found, when a
718
 
    // score has rests in all beat positions (L2_musicReading, Lesson 17, exercise 1)
719
 
    if (nNumPoints < 8)
720
 
    {
721
 
        InstantiateNotesRandom(pScore);
722
 
        return false;       // no error
723
 
    }
724
 
 
725
 
 
726
 
    // Now we are going to choose at random a contour curve and compute its points
727
 
    // The curve will always start and finish in the root note. Its amplitude will
728
 
    // be adjusted to satisfy the constrains (min and max pitch)
729
 
    std::vector<DiatonicPitch> aContour(nNumPoints);
730
 
    GenerateContour(nNumPoints, aContour);
731
 
 
732
 
    // allocate a vector for valid notes in chord (all notes in valid notes range)
733
 
    int iC = 0;                                         //index to current chord (ic)
734
 
    DiatonicPitch dnMinPitch = m_fpMinPitch.to_diatonic_pitch();
735
 
    DiatonicPitch dnMaxPitch = m_fpMaxPitch.to_diatonic_pitch();
736
 
    #if (TRACE_PITCH == 1)
737
 
    wxLogMessage(_T("Composer::InstantiateNotes"), _T("min pitch %d (%s), max pitch %d (%s)"),
738
 
        int(dnMinPitch), dnMinPitch.get_ldp_name().c_str(),
739
 
        int(dnMaxPitch), dnMaxPitch.get_ldp_name().c_str() );
740
 
    #endif
741
 
    std::vector<FPitch> aOnChordPitch;
742
 
    aOnChordPitch.reserve((int(dnMaxPitch) - int(dnMinPitch))/2);    // Reserve space. Upper limit estimation
743
 
    FPitch nRootNote = GenerateInChordList(nKey, nChords[iC], aOnChordPitch);
744
 
 
745
 
    // Loop to process notes/rests in first staff of first instrument
746
 
    ImoNote* pOnChord1 = NULL;      //Pair of on-chord notes. First one
747
 
    ImoNote* pOnChord2 = NULL;      //Pair of on-chord notes. Second one
748
 
    ImoNote* pNonChord[20];         //non-chod notes between the two on-chord notes
749
 
    int nCount = 0;                 //number of non-chord notes between the two on-chord notes
750
 
 
751
 
    ImoNote* pNotePrev = NULL;
752
 
    ImoNote* pNoteCur;
753
 
    int iPt = 0;
754
 
    FPitch fpNew;
755
 
    wxString sDbg = _T("");
756
 
    StaffObjsCursor cursor2(pScore);
757
 
    while(!cursor2.is_end())
758
 
    {
759
 
        ImoStaffObj* pImo = cursor2.get_staffobj();
760
 
        if (pImo->is_note_rest())
761
 
        {
762
 
            // 1. It is a note or a rest
763
 
            if (pImo->is_note())
764
 
            {
765
 
                // It is a note. Get its chord position
766
 
                pNoteCur = static_cast<ImoNote*>(pImo);
767
 
                int pos = k_off_beat;
768
 
                ImoTimeSignature* pTS = cursor2.get_applicable_time_signature();
769
 
                if (pTS)
770
 
                    pos = get_beat_position(cursor2.time(), pTS);
771
 
                if (pos != k_off_beat)
772
 
                {
773
 
                    // on beat note. Pitch must be on chord.
774
 
                    // Assign a pitch from nChords[iC].
775
 
    #if (TRACE_PITCH == 1)
776
 
                    for(int k=0; k < (int)aOnChordPitch.size(); k++)
777
 
                        wxLogMessage(_T("[Composer::InstantiateNotes] OnChord %d = %s"), k, aOnChordPitch[k].to_abs_ldp_name().c_str() );
778
 
    #endif
779
 
                    fpNew = NearestNoteOnChord(aContour[iPt++], pNotePrev, pNoteCur,
780
 
                                                    aOnChordPitch);
781
 
    #if (TRACE_PITCH == 1)
782
 
                    for(int k=0; k < (int)aOnChordPitch.size(); k++)
783
 
                        wxLogMessage(_T("[Composer::InstantiateNotes] OnChord %d = %s"), k, aOnChordPitch[k].to_abs_ldp_name().c_str() );
784
 
                    string sNoteName = fpNew.to_abs_ldp_name();
785
 
                    wxLogMessage(_T("[Composer::InstantiateNotes] on-chord note %d. Assigned pitch = %d (%s), chord=%d"),
786
 
                        iPt, int(fpNew.to_diatonic_pitch()), sNoteName.c_str(),
787
 
                        nChords[iC] & lmGRADE_MASK);
788
 
    #endif
789
 
 
790
 
                    set_pitch(pNoteCur, fpNew);
791
 
 
792
 
                    // assing pitch to non-chord notes between previous on-chord one
793
 
                    // and this one
794
 
                    sDbg += _T(",c");
795
 
                    if (nCount != 0)
796
 
                    {
797
 
                        pOnChord2 = pNoteCur;
798
 
                        AssignNonChordNotes(nCount, pOnChord1, pOnChord2,
799
 
                                            pNonChord, scale);
800
 
                    }
801
 
 
802
 
                    // Prepare data for next pair processing
803
 
                    nCount = 0;
804
 
                    pOnChord1 = pNoteCur;
805
 
                }
806
 
 
807
 
                else
808
 
                {
809
 
                    // non-chord note. Save it to be processed later
810
 
                    if (pNoteCur->get_fpitch() == k_undefined_fpitch)
811
 
                    {
812
 
                        pNonChord[nCount++] = pNoteCur;
813
 
                        sDbg += _T(",nc");
814
 
                    }
815
 
                    else
816
 
                        sDbg += _T(",(nc)");
817
 
                }
818
 
                pNotePrev = pNoteCur;
819
 
            }
820
 
        }
821
 
 
822
 
        else if (pImo->is_barline())
823
 
        {
824
 
            // End of measure: choose the next chord in progression
825
 
            iC++;
826
 
            nRootNote = GenerateInChordList(nKey, nChords[iC % 8], aOnChordPitch);
827
 
        }
828
 
 
829
 
        cursor2.move_next();
830
 
    }
831
 
 
832
 
    #if (TRACE_PITCH == 1)
833
 
    wxLogMessage(_T("[Composer::InstantiateNotes] %s"), sDbg.c_str());
834
 
    #endif
835
 
 
836
 
    return false;       // no error
837
 
}
838
 
 
839
 
//---------------------------------------------------------------------------------------
840
 
void Composer::InstantiateNotesRandom(ImoScore* pScore)
841
 
{
842
 
    ColStaffObjs* pColStaffObjs = pScore->get_staffobjs_table();
843
 
    ColStaffObjs::iterator it = pColStaffObjs->begin();
844
 
    while(it != pColStaffObjs->end())
845
 
    {
846
 
        ImoObj* pImo = (*it)->imo_object();
847
 
        if (pImo->is_note())
848
 
        {
849
 
            FPitch fp = RandomPitch();
850
 
            ImoNote* pNote = static_cast<ImoNote*>(pImo);
851
 
            set_pitch(pNote, fp);
852
 
        }
853
 
        ++it;
854
 
    }
855
 
}
856
 
 
857
 
//---------------------------------------------------------------------------------------
858
 
FPitch Composer::RandomPitch()
859
 
{
860
 
    int nMinPitch = (int)m_fpMinPitch.to_diatonic_pitch();
861
 
    int nMaxPitch = (int)m_fpMaxPitch.to_diatonic_pitch();
862
 
    static int nLastPitch = 0;
863
 
 
864
 
    if (nLastPitch == 0)
865
 
        nLastPitch = (nMinPitch + nMaxPitch) / 2;
866
 
 
867
 
    int nRange = m_pConstrains->GetMaxInterval();
868
 
    int nLowLimit = wxMax(nLastPitch - nRange, nMinPitch);
869
 
    int nUpperLimit = wxMin(nLastPitch + nRange, nMaxPitch);
870
 
    int nNewPitch;
871
 
    if (nUpperLimit - nLowLimit < 2)
872
 
        nNewPitch = nLowLimit;
873
 
    else
874
 
        nNewPitch = RandomGenerator::random_number(nLowLimit, nUpperLimit);
875
 
 
876
 
    // save value
877
 
    nLastPitch = nNewPitch;
878
 
 
879
 
    DiatonicPitch dp(nNewPitch);
880
 
    return FPitch(dp.step(), dp.octave(), 0);
881
 
}
882
 
 
883
 
//---------------------------------------------------------------------------------------
884
 
void Composer::GetRandomHarmony(int nFunctions, std::vector<long>& aFunction)
885
 
{
886
 
    //Fills array 'pFunction' with an ordered set of harmonic functions to
887
 
    //build a melody. i.e.: I,V,I,IV,II,III,IV,I
888
 
 
889
 
    int nNumProgs = sizeof(m_aProgression) / (8 * sizeof(long));
890
 
    int iP = RandomGenerator::random_number(0, nNumProgs-1);
891
 
    for(int i=0; i < 8; i++)
892
 
        aFunction[i] = m_aProgression[iP][i];
893
 
}
894
 
 
895
 
//---------------------------------------------------------------------------------------
896
 
void Composer::FunctionToChordNotes(EKeySignature nKey, long nFunction,
897
 
                                    FPitch notes[4])
898
 
{
899
 
    //Given a key signature and an harmonic function returns the notes to build the
900
 
    //chord (four notes per chord). The first chord note is always in octave 4
901
 
    //i.e.:
902
 
    //C Major, II --> d4, f4, a4
903
 
    //D Major, I  --> d4, +f4, a4
904
 
 
905
 
    //generate natural scale for key signature
906
 
    int nAcc[7];
907
 
    get_accidentals_for_key(nKey, nAcc);
908
 
    int step = get_step_for_root_note(nKey);
909
 
    FPitch scale[15];
910
 
    int octave = k_octave_4;
911
 
    for (int iN=0; iN < 15; iN++)
912
 
    {
913
 
        scale[iN] = FPitch(step, octave, nAcc[step]);
914
 
        if(++step == 7)
915
 
        {
916
 
            step = 0;
917
 
            octave++;
918
 
        }
919
 
    }
920
 
 
921
 
 
922
 
    // Compute the triad
923
 
    long iF = (nFunction & lmGRADE_MASK) - 1L;
924
 
    notes[0] = FPitch( scale[iF] );
925
 
    notes[1] = FPitch( scale[iF+2] );
926
 
    notes[2] = FPitch( scale[iF+4] );
927
 
 
928
 
//    #if (TRACE_COMPOSER == 1)
929
 
//    wxLogMessage(_T("[Composer::FunctionToChordNotes] Function %d, Key=%d, note0 %d (%s), note1 %d (%s), note2 %d (%s)."),
930
 
//        iF, nKey,
931
 
//        notes[0].to_diatonic_pitch(), notes[0].to_abs_ldp_name().c_str(),
932
 
//        notes[1].to_diatonic_pitch(), notes[1].to_abs_ldp_name().c_str(),
933
 
//        notes[2].to_diatonic_pitch(), notes[2].to_abs_ldp_name().c_str() );
934
 
//    #endif
935
 
}
936
 
 
937
 
//---------------------------------------------------------------------------------------
938
 
FPitch Composer::MoveByStep(bool fUpStep, FPitch nPitch, FPitch scale[7])
939
 
{
940
 
    // Generates a new note by moving up/down one step in the scale
941
 
    // The new pitch must be on the key signature natural scale
942
 
 
943
 
    // extract pitch components
944
 
    int nStep = nPitch.step();
945
 
    int nOctave = nPitch.octave();
946
 
 
947
 
    // find current note in scale
948
 
    int i;
949
 
    for (i=0; i < 7; i++) {
950
 
        if (scale[i].step() == nStep) break;
951
 
    }
952
 
    wxASSERT(i < 7);
953
 
 
954
 
    if (fUpStep) {
955
 
        // increment note
956
 
        if (++i == 7) i = 0;
957
 
        if (nStep == k_step_B) nOctave++;
958
 
    }
959
 
    else {
960
 
        // decrement note
961
 
        if (--i == -1) i = 6;
962
 
        if (nStep == k_step_C) nOctave--;
963
 
    }
964
 
 
965
 
    nStep = scale[i].step();
966
 
    int nAcc = scale[i].accidentals();
967
 
    return FPitch(nStep, nOctave, nAcc);
968
 
 
969
 
}
970
 
 
971
 
//---------------------------------------------------------------------------------------
972
 
FPitch Composer::MoveByChromaticStep(bool fUpStep, FPitch pitch)
973
 
{
974
 
    // Generates a new note by moving up/down one chromatic step in the scale
975
 
 
976
 
    // extract pitch accidentals
977
 
    int acc = pitch.accidentals();
978
 
 
979
 
    if (fUpStep)
980
 
        acc++;
981
 
    else
982
 
        acc--;
983
 
 
984
 
    return FPitch(pitch.step(), pitch.octave(), acc);
985
 
}
986
 
 
987
 
//---------------------------------------------------------------------------------------
988
 
void Composer::GenerateScale(EKeySignature nKey, FPitch notes[7])
989
 
{
990
 
    int acc[7];
991
 
    get_accidentals_for_key(nKey, acc);
992
 
    int step = get_step_for_root_note(nKey);
993
 
    for (int i=0; i < 7; ++i)
994
 
    {
995
 
        notes[i] = FPitch(step, k_octave_4, acc[step]);
996
 
        if(++step == 7)
997
 
            step = 0;
998
 
    }
999
 
}
1000
 
 
1001
 
//---------------------------------------------------------------------------------------
1002
 
FPitch Composer::GenerateInChordList(EKeySignature nKey, long nChord,
1003
 
                                     std::vector<FPitch>& fpValidPitch)
1004
 
{
1005
 
    // Returns the root note in octave 4
1006
 
    // Generates a list with all allowed notes in the chord, satisfying the
1007
 
    // constraints for notes range. For instance:
1008
 
    // D Major chord: d4, +f4, a4
1009
 
    // notes range: a3 to a5
1010
 
    // returns: a3, d4, +f4, a4, d5, +f5, a5
1011
 
 
1012
 
    // allocate an array for notes in chord (basic chord, octave 4)
1013
 
    FPitch notes[4];                          // notes in current chord
1014
 
    FunctionToChordNotes(nKey, nChord, notes);
1015
 
 
1016
 
    // extract valid steps, to simplify
1017
 
    //TODO: review logic. Value NO_DPITCH is never returned.
1018
 
    int nValidStep[4];
1019
 
    for (int i=0; i < 4; i++)
1020
 
    {
1021
 
        if (notes[i].to_diatonic_pitch() == NO_DPITCH)
1022
 
            nValidStep[i] = -1;        //you can assign any non valid value for a step
1023
 
        else
1024
 
            nValidStep[i] = notes[i].step();
1025
 
    }
1026
 
 
1027
 
    // empty valid pitches array
1028
 
    fpValidPitch.clear();
1029
 
 
1030
 
    // scan notes range and select those in chord
1031
 
    DiatonicPitch dnMinPitch = m_fpMinPitch.to_diatonic_pitch();
1032
 
    DiatonicPitch dnMaxPitch = m_fpMaxPitch.to_diatonic_pitch();
1033
 
    for (int i=int(dnMinPitch); i <= int(dnMaxPitch); i++)
1034
 
    {
1035
 
        DiatonicPitch dp(i);
1036
 
        int nStep = dp.step();
1037
 
        for (int j=0; j < 4; j++)
1038
 
        {
1039
 
            if (nStep == nValidStep[j])
1040
 
            {
1041
 
                // Note in chord. Add it to the list
1042
 
                FPitch nPitch(nStep, dp.octave(), notes[j].num_accidentals());
1043
 
                fpValidPitch.push_back(nPitch);
1044
 
            }
1045
 
        }
1046
 
    }
1047
 
 
1048
 
    return notes[0];
1049
 
}
1050
 
 
1051
 
//---------------------------------------------------------------------------------------
1052
 
void Composer::GenerateContour(int nNumPoints, std::vector<DiatonicPitch>& aContour)
1053
 
{
1054
 
    // In this method we choose at random a contour curve and compute its points
1055
 
    // The curve will always start and finish in the root note. Its amplitude will
1056
 
    // be adjusted to satisfy the constrains (min and max pitch), and will be
1057
 
    // a value between one and two octaves, depending on the valid notes range and
1058
 
    // the type of contour.
1059
 
    // In case the valid notes range is lower than one octave, arch like curves will
1060
 
    // be forced
1061
 
 
1062
 
    // First, we will determine the root note
1063
 
    int nRootStep = get_step_for_root_note(m_nKey);
1064
 
 
1065
 
    // Now lets do some computations to determine a suitable octave
1066
 
    DiatonicPitch dnMinPitch = m_fpMinPitch.to_diatonic_pitch();
1067
 
    DiatonicPitch dnMaxPitch = m_fpMaxPitch.to_diatonic_pitch();
1068
 
    int nAmplitude = int(dnMaxPitch) - int(dnMinPitch) + 1;
1069
 
//    #if (TRACE_COMPOSER == 1)
1070
 
//    wxLogMessage(_T("[Composer::GenerateContour] minPitch %d  (%s), max pitch %d (%s), amplitude %d"),
1071
 
//        dnMinPitch, dnMinPitch.get_ldp_name().c_str(),
1072
 
//        dnMaxPitch, dnMaxPitch.get_ldp_name().c_str(),
1073
 
//        nAmplitude );
1074
 
//    #endif
1075
 
 
1076
 
 
1077
 
        // determine minimum root pitch
1078
 
    int nOctave = dnMinPitch.octave();
1079
 
    if (dnMinPitch.step() > nRootStep)
1080
 
        nOctave++;
1081
 
    DiatonicPitch dnMinRoot(nRootStep, nOctave);
1082
 
 
1083
 
        // determine maximum root pitch
1084
 
    DiatonicPitch dnMaxRoot = dnMinRoot;
1085
 
    while (dnMaxRoot+7 <= dnMaxPitch)
1086
 
        dnMaxRoot+=7;
1087
 
 
1088
 
        // if range greater than two octaves reduce it and reposition
1089
 
    if (int(dnMaxRoot)-int(dnMinRoot) > 14)
1090
 
    {
1091
 
        int nRange = (int(dnMaxRoot)-int(dnMinRoot)) / 7;
1092
 
        int nShift = RandomGenerator::random_number(0, nRange-2);
1093
 
        dnMinRoot += 7*nShift;
1094
 
        dnMaxRoot = dnMinRoot + 14;
1095
 
    }
1096
 
 
1097
 
 
1098
 
//    #if (TRACE_COMPOSER == 1)
1099
 
//    wxLogMessage(_T("[Composer::GenerateContour] min root %d  (%s), max root %d (%s)"),
1100
 
//        dnMinRoot, DiatonicPitch_ToLDPName(dnMinRoot).c_str(),
1101
 
//        dnMaxRoot, DiatonicPitch_ToLDPName(dnMaxRoot).c_str() );
1102
 
//    #endif
1103
 
 
1104
 
 
1105
 
 
1106
 
    // Choose a contour curve
1107
 
    enum
1108
 
    {
1109
 
        lmCONTOUR_TRIANGLE = 0,
1110
 
        lmCONTOUR_TRIANGLE_RAMP,
1111
 
        lmCONTOUR_RAMP_TRIANGLE,
1112
 
        lmSTART_RESTRICTED_CONTOURS,
1113
 
        lmCONTOUR_ZIG_ZAG = lmSTART_RESTRICTED_CONTOURS,
1114
 
        lmCONTOUR_RAMP,
1115
 
        lmMAX_CONTOUR,
1116
 
        lmCONTOUR_ARCH,             //Bad results. Top is very flat so the sensation of
1117
 
                                    //raeching a peak is very poor
1118
 
    };
1119
 
 
1120
 
 
1121
 
 
1122
 
    // Choose a contour curve. If range is not at least an octave, do not allow
1123
 
    // ramp curves
1124
 
    int nCurve;
1125
 
    bool fUp;
1126
 
    if (dnMaxRoot == dnMinRoot)
1127
 
    {
1128
 
        // Pitch range is not at least an octave. Only some curves allowed.
1129
 
        nCurve = RandomGenerator::random_number(0, lmSTART_RESTRICTED_CONTOURS-1);
1130
 
        // Lets force curve direction to better use the avalable notes range
1131
 
        if (dnMaxPitch < dnMinRoot)
1132
 
        {
1133
 
            // allowed notes does not include root note. Use all notes range.
1134
 
            // Curve direction doesn't matter
1135
 
            dnMinRoot = dnMinPitch;
1136
 
            dnMaxRoot = dnMaxPitch;
1137
 
            fUp = RandomGenerator::flip_coin();
1138
 
        }
1139
 
        else if (int(dnMinRoot)-int(dnMinPitch) < int(dnMaxPitch)-int(dnMinRoot))
1140
 
        {
1141
 
            // Use upper part of range. Curve going up
1142
 
            fUp = true;
1143
 
            dnMaxRoot = dnMaxPitch;
1144
 
        }
1145
 
        else
1146
 
        {
1147
 
            // use lower part of range. Curve going down.
1148
 
            fUp = false;
1149
 
            dnMinRoot = dnMinPitch;
1150
 
        }
1151
 
    }
1152
 
    else
1153
 
    {
1154
 
        // range is grater than one octave. Any curve and direction allowed
1155
 
        nCurve = RandomGenerator::random_number(0, lmMAX_CONTOUR-1);
1156
 
        fUp = RandomGenerator::flip_coin();
1157
 
    }
1158
 
 
1159
 
    #if (TRACE_PITCH == 1)
1160
 
    wxLogMessage(_T("[Composer::GenerateContour] type=%d, nNumPoints=%d, up=%s"),
1161
 
        nCurve, nNumPoints, (fUp ? _T("Yes") : _T("No")) );
1162
 
    #endif
1163
 
 
1164
 
    // prepare curve parameters and compute the curve points
1165
 
    DiatonicPitch dnLowPitch, dnHighPitch, dnStartRamp, dnEndRamp;
1166
 
    switch (nCurve)
1167
 
    {
1168
 
        case lmCONTOUR_ARCH:
1169
 
            //----------------------------------------------------------------------------
1170
 
            // Arch. An arch will be defined by the amplitude, the center beat, and
1171
 
            //the direction
1172
 
            //
1173
 
            if (fUp)
1174
 
            {
1175
 
                dnLowPitch = dnMinRoot;
1176
 
                nAmplitude = dnMaxPitch - dnLowPitch;
1177
 
                if (nAmplitude > 14)
1178
 
                    nAmplitude = 14;
1179
 
                dnHighPitch = dnLowPitch + nAmplitude;
1180
 
            }
1181
 
            else
1182
 
            {
1183
 
                dnHighPitch = dnMaxRoot;
1184
 
                nAmplitude = dnHighPitch - dnMinPitch;
1185
 
                if (nAmplitude > 14)
1186
 
                    nAmplitude = 14;
1187
 
                dnLowPitch = dnHighPitch - nAmplitude;
1188
 
            }
1189
 
            ComputeArch(fUp, 0, nNumPoints, dnLowPitch, dnHighPitch, aContour);
1190
 
            break;
1191
 
 
1192
 
        case lmCONTOUR_TRIANGLE:
1193
 
            //----------------------------------------------------------------------------
1194
 
            // Triangle. Defined the amplitude, the center beat, and the direction
1195
 
            if (fUp)
1196
 
            {
1197
 
                dnLowPitch = dnMinRoot;
1198
 
                nAmplitude = dnMaxPitch - dnLowPitch;
1199
 
                if (nAmplitude > 14)
1200
 
                    nAmplitude = 14;
1201
 
                dnHighPitch = dnLowPitch + nAmplitude;
1202
 
            }
1203
 
            else
1204
 
            {
1205
 
                dnHighPitch = dnMaxRoot;
1206
 
                nAmplitude = dnHighPitch - dnMinPitch;
1207
 
                if (nAmplitude > 14)
1208
 
                    nAmplitude = 14;
1209
 
                dnLowPitch = dnHighPitch - nAmplitude;
1210
 
            }
1211
 
            ComputeTriangle(fUp, 0, nNumPoints, dnLowPitch, dnHighPitch, aContour);
1212
 
            break;
1213
 
 
1214
 
        case lmCONTOUR_RAMP:
1215
 
            //----------------------------------------------------------------------------
1216
 
            // Ramp.
1217
 
            // Amplitude will move two octaves from root to root note unless not possible
1218
 
            if (fUp)
1219
 
                ComputeRamp(0, nNumPoints, dnMinRoot, dnMaxRoot, aContour);
1220
 
            else
1221
 
                ComputeRamp(0, nNumPoints, dnMaxRoot, dnMinRoot, aContour);
1222
 
            break;
1223
 
 
1224
 
        case lmCONTOUR_TRIANGLE_RAMP:
1225
 
            //----------------------------------------------------------------------------
1226
 
            // Triangle+Ramp
1227
 
            // Triangle occupies two thirds and ramp one third
1228
 
        {
1229
 
            // Triangle: set up amplitude and last point
1230
 
            int nPoints = 2* nNumPoints / 3;
1231
 
            if (fUp)
1232
 
            {
1233
 
                dnLowPitch = dnMinRoot;
1234
 
                nAmplitude = dnMaxPitch - dnLowPitch;
1235
 
                if (nAmplitude > 14)
1236
 
                    nAmplitude = 14;
1237
 
                dnHighPitch = dnLowPitch + nAmplitude;
1238
 
                dnStartRamp = dnHighPitch;
1239
 
                dnEndRamp = dnLowPitch;
1240
 
            }
1241
 
            else
1242
 
            {
1243
 
                dnHighPitch = dnMaxRoot;
1244
 
                nAmplitude = dnHighPitch - dnMinPitch;
1245
 
                if (nAmplitude > 14)
1246
 
                    nAmplitude = 14;
1247
 
                dnLowPitch = dnHighPitch - nAmplitude;
1248
 
                dnStartRamp = dnLowPitch;
1249
 
                dnEndRamp = dnHighPitch;
1250
 
            }
1251
 
            ComputeTriangle(fUp, 0, nPoints, dnLowPitch, dnHighPitch, aContour);
1252
 
            ComputeRamp(nPoints, nNumPoints, dnStartRamp, dnEndRamp, aContour);
1253
 
            break;
1254
 
        }
1255
 
 
1256
 
        case lmCONTOUR_ZIG_ZAG:
1257
 
            //----------------------------------------------------------------------------
1258
 
            // Triangle+Ramp
1259
 
            // Triangle occupies two thirds and ramp one third
1260
 
        {
1261
 
            // Triangle: set up amplitude and last point
1262
 
            int nPoints = 2* nNumPoints / 3;
1263
 
            if (fUp)
1264
 
            {
1265
 
                dnLowPitch = dnMinRoot;
1266
 
                nAmplitude = dnMaxPitch - dnLowPitch;
1267
 
                if (nAmplitude > 14)
1268
 
                    nAmplitude = 14;
1269
 
                dnHighPitch = dnLowPitch + nAmplitude;
1270
 
                dnStartRamp = dnLowPitch;
1271
 
                dnEndRamp = dnMaxRoot;
1272
 
            }
1273
 
            else
1274
 
            {
1275
 
                dnHighPitch = dnMaxRoot;
1276
 
                nAmplitude = dnHighPitch - dnMinPitch;
1277
 
                if (nAmplitude > 14)
1278
 
                    nAmplitude = 14;
1279
 
                dnLowPitch = dnHighPitch - nAmplitude;
1280
 
                dnStartRamp = dnHighPitch;
1281
 
                dnEndRamp = dnMinRoot;
1282
 
            }
1283
 
            ComputeTriangle(fUp, 0, nPoints, dnLowPitch, dnHighPitch, aContour);
1284
 
            ComputeRamp(nPoints, nNumPoints, dnStartRamp, dnEndRamp, aContour);
1285
 
            break;
1286
 
        }
1287
 
 
1288
 
        case lmCONTOUR_RAMP_TRIANGLE:
1289
 
            //----------------------------------------------------------------------------
1290
 
            // Ramp+Triangle
1291
 
        {
1292
 
            // Ramp: num points
1293
 
            int nPoints = nNumPoints / 3;
1294
 
 
1295
 
            // Triangle: set up amplitude and last point
1296
 
            if (fUp)
1297
 
            {
1298
 
                dnLowPitch = dnMinRoot;
1299
 
                nAmplitude = dnMaxPitch - dnLowPitch;
1300
 
                if (nAmplitude > 14)
1301
 
                    nAmplitude = 14;
1302
 
                dnHighPitch = dnLowPitch + nAmplitude;
1303
 
                dnStartRamp = dnLowPitch;
1304
 
                dnEndRamp = dnMaxRoot;
1305
 
            }
1306
 
            else
1307
 
            {
1308
 
                dnHighPitch = dnMaxRoot;
1309
 
                nAmplitude = dnHighPitch - dnMinPitch;
1310
 
                if (nAmplitude > 14)
1311
 
                    nAmplitude = 14;
1312
 
                dnLowPitch = dnHighPitch - nAmplitude;
1313
 
                dnStartRamp = dnHighPitch;
1314
 
                dnEndRamp = dnMinRoot;
1315
 
            }
1316
 
 
1317
 
            ComputeRamp(0, nPoints, dnStartRamp, dnEndRamp, aContour);
1318
 
            ComputeTriangle(fUp, nPoints, nNumPoints, dnLowPitch, dnHighPitch, aContour);
1319
 
            break;
1320
 
        }
1321
 
    }
1322
 
 
1323
 
//    #if (TRACE_COMPOSER == 1)
1324
 
//    for (int i=0; i < nNumPoints; i++)
1325
 
//        wxLogMessage(_T("[Composer::GenerateContour] point[%d] = %d"), i, aContour[i]);
1326
 
//    #endif
1327
 
}
1328
 
 
1329
 
//---------------------------------------------------------------------------------------
1330
 
void Composer::ComputeTriangle(bool fUp, int iStart, int nPoints, DiatonicPitch dnLowPitch,
1331
 
                                  DiatonicPitch dnHighPitch, std::vector<DiatonicPitch>& aPoints)
1332
 
{
1333
 
    // Triangle. Defined the amplitude, the center beat, and the direction
1334
 
    // (up/down). Also by start pitch, top/bottom pitch, and end pitch
1335
 
 
1336
 
    // first ramp
1337
 
    float rNumPoints = (float)((nPoints-iStart)/2);
1338
 
    float rStep = (float)(dnHighPitch - dnLowPitch) / rNumPoints;
1339
 
    if (!fUp) rStep = -rStep;
1340
 
    float yValue = (float)(fUp ? dnLowPitch : dnHighPitch);
1341
 
//    #if (TRACE_COMPOSER == 1)
1342
 
//    wxLogMessage(_T("[Composer::ComputeTriangle] fUp=%s, iStart=%d, nPoints=%d, dnLowPitch=%d, dnHighPitch=%d, rStep=%.5f"),
1343
 
//        (fUp ? _T("Yes") : _T("No")), iStart, nPoints, dnLowPitch, dnHighPitch, rStep);
1344
 
//    #endif
1345
 
    int i = iStart;
1346
 
    int nCenter = (nPoints+iStart)/2;
1347
 
    for (; i < nCenter; i++)
1348
 
    {
1349
 
        aPoints[i] = (int)floor(yValue+0.5);
1350
 
        yValue += rStep;
1351
 
    }
1352
 
    yValue -= rStep;
1353
 
 
1354
 
    // second ramp
1355
 
    rStep = (float)(dnHighPitch - dnLowPitch) / ((float)(nPoints-iStart) - rNumPoints);
1356
 
    if (fUp)
1357
 
        rStep = -rStep;
1358
 
//    #if (TRACE_COMPOSER == 1)
1359
 
//    wxLogMessage(_T("[Composer::ComputeTriangle] fUp=%s, iStart=%d, nPoints=%d, dnLowPitch=%d, dnHighPitch=%d, rStep=%.5f"),
1360
 
//        (fUp ? _T("Yes") : _T("No")), i, nPoints, dnLowPitch, dnHighPitch, rStep);
1361
 
//    #endif
1362
 
    for (; i < nPoints; i++)
1363
 
    {
1364
 
        aPoints[i] = (int)floor(yValue+0.5);
1365
 
        yValue += rStep;
1366
 
    }
1367
 
 
1368
 
    //force last point to be a root note
1369
 
    aPoints[nPoints-1] = (fUp ? dnLowPitch : dnHighPitch);
1370
 
 
1371
 
}
1372
 
 
1373
 
//---------------------------------------------------------------------------------------
1374
 
void Composer::ComputeRamp(int iStart, int nPoints, DiatonicPitch dnStartPitch,
1375
 
                              DiatonicPitch dnEndPitch, std::vector<DiatonicPitch>& aPoints)
1376
 
{
1377
 
    // Ramp
1378
 
    float rNumPoints = (float)(nPoints-iStart);
1379
 
    float rStep = (float)(dnEndPitch - dnStartPitch) / rNumPoints;
1380
 
    float yValue = (float)dnStartPitch;
1381
 
//    #if (TRACE_COMPOSER == 1)
1382
 
//    wxLogMessage(_T("[Composer::ComputeRamp] iStart=%d, nPoints=%d, dnStartPitch=%d, dnEndPitch=%d, rStep=%.5f"),
1383
 
//        iStart, nPoints, dnStartPitch, dnEndPitch, rStep);
1384
 
//    #endif
1385
 
    for (int i=iStart; i < nPoints; i++)
1386
 
    {
1387
 
        aPoints[i] = (int)floor(yValue+0.5);
1388
 
        yValue += rStep;
1389
 
    }
1390
 
    //force last point to be a root note
1391
 
    aPoints[nPoints-1] = dnEndPitch;
1392
 
 
1393
 
}
1394
 
 
1395
 
//---------------------------------------------------------------------------------------
1396
 
void Composer::ComputeArch(bool fUp, int iStart, int nPoints, DiatonicPitch dnLowPitch,
1397
 
                              DiatonicPitch dnHighPitch, std::vector<DiatonicPitch>& aPoints)
1398
 
{
1399
 
    // Arch. An arch will be defined the amplitude, the center beat, and the direction
1400
 
    // (up/down). Also by start pitch, top/bottom pitch, and end pitch
1401
 
    // I will use a second degree polinimio. I approximate it by using the Lagrange method.
1402
 
    // The resulting polinom is
1403
 
    //      P(x) = RootPitch + ((4 * maxPitch * x * (numPoints - x))/numPoints**2)
1404
 
    //           = a3 + ((a1 * x * (numPoints - x))/ a2)
1405
 
    //      a1 = 4 * maxPitch
1406
 
    //      a2 = numPoints**2
1407
 
    //      a3 = RootPitch
1408
 
    //      a4 = numPoints
1409
 
 
1410
 
//    #if (TRACE_COMPOSER == 1)
1411
 
//    wxLogMessage(_T("[Composer::ComputeArch] fUp=%s, iStart=%d, nPoints=%d, dnLowPitch=%d, dnHighPitch=%d"),
1412
 
//        (fUp ? _T("Yes") : _T("No")), iStart, nPoints, dnLowPitch, dnHighPitch);
1413
 
//    #endif
1414
 
    float a1 = 4.0 * (float)(dnHighPitch-dnLowPitch);
1415
 
    float a2 = (float)(nPoints * nPoints);
1416
 
    float a3 = (float)dnLowPitch;
1417
 
    float a4 = (float)nPoints;
1418
 
    float x = 0.0;
1419
 
    for (int i=iStart; i < iStart+nPoints; i++, x+=1.0)
1420
 
    {
1421
 
        float y = a3 + ((a1 * x * (a4 - x)) / a2);
1422
 
        aPoints[i] = (int)floor(y + 0.5);
1423
 
    }
1424
 
    //force last point to be a root note
1425
 
    aPoints[iStart+nPoints-1] = (fUp ? dnLowPitch : dnHighPitch);
1426
 
 
1427
 
}
1428
 
 
1429
 
//---------------------------------------------------------------------------------------
1430
 
FPitch Composer::NearestNoteOnChord(DiatonicPitch nPoint, ImoNote* pNotePrev,
1431
 
                                    ImoNote* pNoteCur,
1432
 
                                    std::vector<FPitch>& aOnChordPitch)
1433
 
{
1434
 
//    #if (TRACE_COMPOSER == 1)
1435
 
//    wxLogMessage(_T("[Composer::NearestNoteOnChord] nPoint=%d"), nPoint );
1436
 
//    #endif
1437
 
//
1438
 
    // if note is tied to previous one, return previous note pitch
1439
 
    if (pNotePrev && pNotePrev->is_tied_next() && pNotePrev->is_pitch_defined())
1440
 
    {
1441
 
//    #if (TRACE_COMPOSER == 1)
1442
 
//        wxLogMessage(_T("[Composer::NearestNoteOnChord] Previous note = %s"), (pNotePrev->get_fpitch()).to_abs_ldp_name().c_str());
1443
 
//    #endif
1444
 
        return pNotePrev->get_fpitch();
1445
 
    }
1446
 
 
1447
 
    for (int i=0; i < (int)aOnChordPitch.size(); i++)
1448
 
    {
1449
 
        DiatonicPitch dnCur = aOnChordPitch[i].to_diatonic_pitch();
1450
 
        if (nPoint == dnCur)
1451
 
            return aOnChordPitch[i];
1452
 
        else if (nPoint < dnCur)
1453
 
        {
1454
 
            // The nearest one is this one or the previous one
1455
 
            // If no previous one, return this one
1456
 
            if (i == 0)
1457
 
                return aOnChordPitch[i];
1458
 
            // there is a 'previous one'. So lets compute differences
1459
 
            DiatonicPitch dnPrev = aOnChordPitch[i-1].to_diatonic_pitch();
1460
 
            if (nPoint - dnPrev < dnCur - nPoint)
1461
 
                return aOnChordPitch[i-1];
1462
 
            else
1463
 
                return aOnChordPitch[i];
1464
 
        }
1465
 
    }
1466
 
 
1467
 
    //requested note is out of range. Return maximum allowed one
1468
 
    //return aOnChordPitch[aOnChordPitch.size()-1];
1469
 
    return aOnChordPitch[aOnChordPitch.size()-1];
1470
 
 
1471
 
    return FPitch("c4");
1472
 
}
1473
 
 
1474
 
//---------------------------------------------------------------------------------------
1475
 
void Composer::InstantiateWithNote(ImoScore* pScore, FPitch fp)
1476
 
{
1477
 
    // This method is used only to generate images for documentation.
1478
 
    // The idea is to instantiate the score with the same pitch for all
1479
 
    // notes, to create scores with the rhymth pattern
1480
 
 
1481
 
    // Loop to instantiate notes
1482
 
    ColStaffObjs* pColStaffObjs = pScore->get_staffobjs_table();
1483
 
    ColStaffObjs::iterator it = pColStaffObjs->begin();
1484
 
    while(it != pColStaffObjs->end())
1485
 
    {
1486
 
        ImoObj* pImo = (*it)->imo_object();
1487
 
        if (pImo->is_note())
1488
 
        {
1489
 
            // It is a note. Instantiate it
1490
 
            ImoNote* pNote = static_cast<ImoNote*>(pImo);
1491
 
            set_pitch(pNote, fp);
1492
 
        }
1493
 
        ++it;
1494
 
    }
1495
 
}
1496
 
 
1497
 
//---------------------------------------------------------------------------------------
1498
 
void Composer::AssignNonChordNotes(int nNumNotes, ImoNote* pOnChord1, ImoNote* pOnChord2,
1499
 
                                      ImoNote* pNonChord[], FPitch scale[7])
1500
 
{
1501
 
    // Receives the two on-chord notes and the non-chord notes between them, and assign
1502
 
    // the pitch to all notes
1503
 
    // The first on-chord note can be NULL (first anacruxis measure)
1504
 
    // The number of non-chord notes received is in 'nNumNotes'
1505
 
 
1506
 
 
1507
 
    //case: no non-chord notes. Nothing to do
1508
 
    if (nNumNotes == 0)
1509
 
    {
1510
 
//    #if (TRACE_PITCH == 1)
1511
 
//        wxLogMessage(_T("[Composer::AssignNonChordNotes] No non-chord notes. Nothing to do."));
1512
 
//    #endif
1513
 
        return;
1514
 
    }
1515
 
 
1516
 
    //case: anacruxis measure
1517
 
    if (!pOnChord1)
1518
 
    {
1519
 
//    #if (TRACE_PITCH == 1)
1520
 
//        wxLogMessage(_T("[Composer::AssignNonChordNotes] Anacruxis measure"));
1521
 
//    #endif
1522
 
 
1523
 
        //we are going to assing an ascending sequence, by step, to finish in the root
1524
 
        //note (the first on chord note)
1525
 
        if (nNumNotes == 1)
1526
 
        {
1527
 
            //assign root pitch
1528
 
            set_pitch(pNonChord[0], pOnChord2->get_fpitch());
1529
 
        }
1530
 
        else
1531
 
        {
1532
 
            // ascending sequence of steps
1533
 
            FPitch nPitch = pOnChord2->get_fpitch();
1534
 
            for(int i=nNumNotes-1; i >= 0; i--)
1535
 
            {
1536
 
                if (!pNonChord[i]->is_pitch_defined())
1537
 
                {
1538
 
                    nPitch = MoveByStep(k_down, nPitch, scale);
1539
 
                    set_pitch(pNonChord[i], nPitch);
1540
 
                }
1541
 
            }
1542
 
        }
1543
 
        return;
1544
 
    }
1545
 
 
1546
 
    // Compute inteval formed by on-chord notes
1547
 
    FPitch ap1 = pOnChord1->get_fpitch();
1548
 
    FPitch ap2 = pOnChord2->get_fpitch();
1549
 
    //FPitch fp1 = FPitch(ap1);
1550
 
    //FPitch fp2 = FPitch(ap2);
1551
 
    int nDIntval = ap2.to_diatonic_pitch() - ap1.to_diatonic_pitch();
1552
 
    //int nFIntval = abs(fp2 - fp1);
1553
 
    int nAbsIntval = abs(nDIntval) + 1;
1554
 
 
1555
 
    //Choose non-chord notes type depending on interval formed by on-chord notes
1556
 
    if (nAbsIntval == 1)
1557
 
    {
1558
 
        //unison
1559
 
        //If one or two notes use neighboring notes. If more notes
1560
 
        //choose between neighboring notes or on-chord arpege
1561
 
        if (nNumNotes < 3)
1562
 
            NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1563
 
        else
1564
 
            NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1565
 
        return;
1566
 
    }
1567
 
 
1568
 
    else if (nAbsIntval == 2)
1569
 
    {
1570
 
        //second
1571
 
        //If one note there are several possibilities (anticipation / suspension /
1572
 
        //retardation / appogiatura) but I will just use a on-chord tone (a third apart)
1573
 
        if (nNumNotes == 1)
1574
 
            ThirdFifthNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1575
 
        else
1576
 
            NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1577
 
        return;
1578
 
    }
1579
 
 
1580
 
    else if (nAbsIntval == 3)
1581
 
    {
1582
 
        //third
1583
 
        //If one note use a passing note, else we could use two passing notes by
1584
 
        //chromatic step, but chromatic accidentals could not be appropriate for
1585
 
        //lower music reading levels; therefore I will use a neighboring tone
1586
 
        if (nNumNotes == 1)
1587
 
            PassingNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1588
 
        else
1589
 
            NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1590
 
        return;
1591
 
    }
1592
 
 
1593
 
    else if (nAbsIntval == 4)
1594
 
    {
1595
 
        //fourth
1596
 
        //interval 1:   Third / Appoggiatura
1597
 
        //interval 2:   Two passing tones (by step) / Appoggiatura
1598
 
        if (nNumNotes == 1)
1599
 
            ThirdFifthNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1600
 
        else if (nNumNotes == 1)
1601
 
            PassingNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1602
 
        else
1603
 
            NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1604
 
        return;
1605
 
    }
1606
 
 
1607
 
    else if (nAbsIntval == 5)
1608
 
    {
1609
 
        //fifth
1610
 
        //interval 1:   Third / Appoggiatura
1611
 
        //interval 2:   Third+Fifth / Double appoggiatura
1612
 
        //interval 3:   three passing tones (by step)
1613
 
        if (nNumNotes < 3)
1614
 
            ThirdFifthNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1615
 
        else if (nNumNotes == 3)
1616
 
            PassingNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1617
 
        else
1618
 
            NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1619
 
        return;
1620
 
    }
1621
 
 
1622
 
    else if (nAbsIntval == 6)
1623
 
    {
1624
 
        //sixth
1625
 
        //interval 1:   Appoggiatura
1626
 
        //interval 2:   Double appoggiatura
1627
 
        //interval 4:   four passing tones (by step)
1628
 
        if (nNumNotes < 3)
1629
 
            ThirdFifthNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1630
 
        else if (nNumNotes == 4)
1631
 
            PassingNotes((nDIntval > 0), nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1632
 
        else
1633
 
            NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1634
 
        return;
1635
 
    }
1636
 
 
1637
 
    else
1638
 
    {
1639
 
        wxLogMessage(_T("[Composer::AssignNonChordNotes] Program error: ")
1640
 
            _T("case not defined: Intval %d, num.notes %d"),
1641
 
            nAbsIntval, nNumNotes );
1642
 
        NeightboringNotes(nNumNotes, pOnChord1, pOnChord2, pNonChord, scale);
1643
 
        return;
1644
 
    }
1645
 
}
1646
 
 
1647
 
//---------------------------------------------------------------------------------------
1648
 
void Composer::NeightboringNotes(int nNumNotes, ImoNote* pOnChord1, ImoNote* pOnChord2,
1649
 
                                    ImoNote* pNonChord[], FPitch scale[7])
1650
 
{
1651
 
    // Receives the two on-chord notes and the non-chord notes between them, and assign
1652
 
    // the pitch to all notes. The number of non-chord notes received is in 'nNumNotes'
1653
 
 
1654
 
    //wxASSERT(nNumNotes > 0 && nNumNotes < 4);
1655
 
    //// 1, 2 or 3 neightboring notes
1656
 
 
1657
 
    bool fUpStep = RandomGenerator::flip_coin();
1658
 
    FPitch ap = pOnChord1->get_fpitch();
1659
 
    //wxASSERT(ap.to_diatonic_pitch() != lmNO_NOTE);
1660
 
    FPitch nFirstPitch = MoveByStep(fUpStep, ap, scale);
1661
 
    set_pitch(pNonChord[0], nFirstPitch);
1662
 
    if (nNumNotes == 1) return;
1663
 
    set_pitch(pNonChord[1], MoveByStep(!fUpStep, ap, scale));
1664
 
    if (nNumNotes == 2) return;
1665
 
    set_pitch(pNonChord[2], nFirstPitch);
1666
 
}
1667
 
 
1668
 
//---------------------------------------------------------------------------------------
1669
 
void Composer::PassingNotes(bool fUp, int nNumNotes, ImoNote* pOnChord1, ImoNote* pOnChord2,
1670
 
                               ImoNote* pNonChord[], FPitch scale[7])
1671
 
{
1672
 
    // Receives the two on-chord notes and the non-chord notes between them, and assign
1673
 
    // the pitch to all notes. The number of non-chord notes received is in 'nNumNotes'
1674
 
 
1675
 
    wxASSERT(nNumNotes > 0);
1676
 
 
1677
 
    // passing note
1678
 
    FPitch apNewPitch = pOnChord1->get_fpitch();
1679
 
    set_pitch(pNonChord[0], MoveByStep(fUp, apNewPitch, scale));
1680
 
 
1681
 
    // two passing notes
1682
 
    for (int i=1; i < nNumNotes; i++)
1683
 
    {
1684
 
        apNewPitch = MoveByStep(fUp, apNewPitch, scale);
1685
 
        set_pitch(pNonChord[i], apNewPitch);
1686
 
    }
1687
 
}
1688
 
 
1689
 
//---------------------------------------------------------------------------------------
1690
 
void Composer::ThirdFifthNotes(bool fUp, int nNumNotes, ImoNote* pOnChord1,
1691
 
                               ImoNote* pOnChord2,
1692
 
                               ImoNote* pNonChord[], FPitch scale[7])
1693
 
{
1694
 
    // Receives the two on-chord notes and the non-chord notes between them, and assign
1695
 
    // the pitch to all notes. The number of non-chord notes received is in 'nNumNotes'
1696
 
 
1697
 
    wxASSERT(nNumNotes == 1 || nNumNotes == 2);
1698
 
 
1699
 
    // third
1700
 
    FPitch pitch = MoveByStep(fUp, pOnChord1->get_fpitch(), scale);  //second
1701
 
    pitch = MoveByStep(fUp, pitch, scale);     //third
1702
 
    set_pitch(pNonChord[0], pitch);
1703
 
    if (nNumNotes == 1) return;
1704
 
    // fifth
1705
 
    pitch = MoveByStep(fUp, pitch, scale);     //fourth
1706
 
    pitch = MoveByStep(fUp, pitch, scale);     //fifth
1707
 
    set_pitch(pNonChord[1], pitch);
1708
 
}
1709
 
 
1710
 
//---------------------------------------------------------------------------------------
1711
 
int Composer::get_metronome_pulses_for(ETimeSignature nTimeSign)
1712
 
{
1713
 
    //returns the number of pulses (metronome pulses) implied by the received
1714
 
    //time signature.
1715
 
 
1716
 
    switch (nTimeSign) {
1717
 
        case k_time_2_4:
1718
 
            return 2;
1719
 
        case k_time_3_4:
1720
 
            return 3;
1721
 
        case k_time_4_4:
1722
 
            return 4;
1723
 
        case k_time_2_8:
1724
 
            return 2;
1725
 
        case k_time_3_8:
1726
 
            return 3;
1727
 
        case k_time_2_2:
1728
 
            return 2;
1729
 
        case k_time_3_2:
1730
 
            return 3;
1731
 
        case k_time_6_8:
1732
 
            return 2;
1733
 
        case k_time_9_8:
1734
 
            return 3;
1735
 
        case k_time_12_8:
1736
 
            return 4;
1737
 
        default:
1738
 
            wxASSERT(false);
1739
 
            return 4;
1740
 
    }
1741
 
}
1742
 
 
1743
 
//---------------------------------------------------------------------------------------
1744
 
int Composer::get_top_number_for(ETimeSignature nTimeSign)
1745
 
{
1746
 
    //returns the numerator of time signature fraction
1747
 
 
1748
 
    switch (nTimeSign) {
1749
 
        case k_time_2_4:
1750
 
            return 2;
1751
 
        case k_time_3_4:
1752
 
            return 3;
1753
 
        case k_time_4_4:
1754
 
            return 4;
1755
 
        case k_time_2_8:
1756
 
            return 2;
1757
 
        case k_time_3_8:
1758
 
            return 3;
1759
 
        case k_time_2_2:
1760
 
            return 2;
1761
 
        case k_time_3_2:
1762
 
            return 3;
1763
 
        case k_time_6_8:
1764
 
            return 6;
1765
 
        case k_time_9_8:
1766
 
            return 9;
1767
 
        case k_time_12_8:
1768
 
            return 12;
1769
 
        default:
1770
 
            wxASSERT(false);
1771
 
            return 4;
1772
 
    }
1773
 
}
1774
 
 
1775
 
//---------------------------------------------------------------------------------------
1776
 
int Composer::get_bottom_number_for(ETimeSignature nTimeSign)
1777
 
{
1778
 
    switch (nTimeSign) {
1779
 
        case k_time_2_4:
1780
 
        case k_time_3_4:
1781
 
        case k_time_4_4:
1782
 
            return 4;
1783
 
 
1784
 
        case k_time_2_8:
1785
 
        case k_time_3_8:
1786
 
        case k_time_6_8:
1787
 
        case k_time_9_8:
1788
 
        case k_time_12_8:
1789
 
            return 8;
1790
 
 
1791
 
        case k_time_2_2:
1792
 
        case k_time_3_2:
1793
 
            return 2;
1794
 
 
1795
 
        default:
1796
 
            wxASSERT(false);
1797
 
            return 4;
1798
 
    }
1799
 
}
1800
 
 
1801
 
//---------------------------------------------------------------------------------------
1802
 
int Composer::get_num_ref_notes_per_pulse_for(ETimeSignature nTimeSign)
1803
 
{
1804
 
    switch (nTimeSign) {
1805
 
        case k_time_2_4:
1806
 
        case k_time_3_4:
1807
 
        case k_time_4_4:
1808
 
            return 1;
1809
 
 
1810
 
        case k_time_2_8:
1811
 
        case k_time_3_8:
1812
 
        case k_time_6_8:
1813
 
        case k_time_9_8:
1814
 
        case k_time_12_8:
1815
 
            return 3;
1816
 
 
1817
 
        case k_time_2_2:
1818
 
        case k_time_3_2:
1819
 
            return 1;
1820
 
 
1821
 
        default:
1822
 
            wxASSERT(false);
1823
 
            return 1;
1824
 
    }
1825
 
}
1826
 
 
1827
 
//---------------------------------------------------------------------------------------
1828
 
float Composer::get_ref_note_duration_for(ETimeSignature nTimeSign)
1829
 
{
1830
 
    // returns beat duration (in LDP notes duration units)
1831
 
 
1832
 
    int nBeatType = get_bottom_number_for(nTimeSign);
1833
 
    return lomse::get_duration_for_ref_note(nBeatType);
1834
 
}
1835
 
 
1836
 
//---------------------------------------------------------------------------------------
1837
 
float Composer::get_measure_duration_for(ETimeSignature nTimeSign)
1838
 
{
1839
 
    // Returns the required duration for a measure in the received time signature
1840
 
 
1841
 
    float rNumBeats = (float)get_top_number_for(nTimeSign);
1842
 
    return rNumBeats * get_ref_note_duration_for(nTimeSign);
1843
 
}
1844
 
 
1845
 
//---------------------------------------------------------------------------------------
1846
 
void Composer::set_pitch(ImoNote* pNote, FPitch fp)
1847
 
{
1848
 
    int nAccidentals[7];
1849
 
    lomse::get_accidentals_for_key(m_nKey, nAccidentals);
1850
 
    EAccidentals acc = EAccidentals( nAccidentals[fp.step()] );
1851
 
    if (!pNote->is_pitch_defined())
1852
 
    {
1853
 
        pNote->set_notated_pitch(fp.step(), fp.octave(), k_no_accidentals);
1854
 
        pNote->set_actual_accidentals(acc);
1855
 
    }
1856
 
 
1857
 
    if (pNote->is_tied_next())
1858
 
    {
1859
 
        ImoTie* pTie = pNote->get_tie_next();
1860
 
        pNote = pTie->get_end_note();
1861
 
        pNote->set_notated_pitch(fp.step(), fp.octave(), k_no_accidentals);
1862
 
        pNote->set_actual_accidentals(acc);
1863
 
    }
1864
 
}
1865
 
 
1866
 
 
1867
 
 
1868
 
}   //namespace lenmus