public void AddLyrics_Syllabics_StartAndEndCorrect(string input)
        {
            var  sut   = new LyricsProcessor(input);
            note note1 = new note();
            note note2 = new note();
            note note3 = new note();

            sut.AddLyrics(note1);
            sut.AddLyrics(note2);
            sut.AddLyrics(note3);

            using (new AssertionScope())
            {
                note1.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "1",
                    Items  = new dynamic[] { syllabic.begin, new { Value = "Ly" } }
                });

                note2.lyric.Should().BeNullOrEmpty();

                note3.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "1",
                    Items  = new dynamic[] { syllabic.end, new { Value = "rics" } }
                });
            }
        }
        public void AddLyrics_Syllabics_MiddleCorrect(string input)
        {
            var  sut   = new LyricsProcessor(input);
            note note1 = new note();
            note note2 = new note();
            note note3 = new note();

            sut.AddLyrics(note1);
            sut.AddLyrics(note2);
            sut.AddLyrics(note3);

            using (new AssertionScope())
            {
                note1.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "1",
                    Items  = new dynamic[] { syllabic.begin, new { Value = "Mu" } }
                });

                note2.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "1",
                    Items  = new dynamic[] { syllabic.middle, new { Value = "sic" } }
                });

                note3.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "1",
                    Items  = new dynamic[] { syllabic.end, new { Value = "xml" } }
                });
            }
        }
        public void AddLyrics_MultipleStaves_TextCorrectlyAssigned()
        {
            var  sut   = new LyricsProcessor("Ly-#-rics\r\nJust three words");
            note note1 = new note();
            note note2 = new note();
            note note3 = new note();

            sut.AddLyrics(note1);
            sut.AddLyrics(note2);
            sut.AddLyrics(note3);

            using (new AssertionScope())
            {
                note1.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "1",
                    Items  = new dynamic[] { syllabic.begin, new { Value = "Ly" } }
                },
                    new
                {
                    number = "2",
                    Items  = new dynamic[] { new { Value = "Just" } }
                });

                note2.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "2",
                    Items  = new dynamic[] { new { Value = "three" } }
                });

                note3.lyric.Should().BeEquivalentTo(
                    new
                {
                    number = "1",
                    Items  = new dynamic[] { syllabic.end, new { Value = "rics" } }
                },
                    new
                {
                    number = "2",
                    Items  = new dynamic[] { new { Value = "words" } }
                });
            }
        }
Example #4
0
        private void ProcessAccords(
            int staffNumber, int measureIndex, int voiceNumber, BYTE[] accords, INT[] accordIndexes, string text)
        {
            Console.WriteLine($"Processing staff {staffNumber}, measure {measureIndex}, voice {voiceNumber}");

            var lyricsProcessor = new LyricsProcessor(text);

            // iterate data
            int    index               = 0;
            int    accordIndex         = 0;
            int    currentMeasureTicks = 0;
            double tripletRests        = 0;

            while (index < accords.Length)
            {
                scorepartwisePartMeasure currentMeasure = this.measures[staffNumber][measureIndex];
                if (accordIndexes[accordIndex] != index)
                {
                    throw new InvalidOperationException(
                              $"The current accord should start at index {accordIndexes[accordIndex]} but is {index}.");
                }

                accordIndex++;
                byte value = accords[index++];

                // C2FORMAT.TXT line 252ff
                //5. Daten der Akkordinformationen
                //--------------------------------
                //
                //  +---+---+---+---+---+---+---+---+
                //  |5              |4  |3  |2  |1  |
                //  +---+---+---+---+---+---+---+---+
                //  (1) BIT      Extras
                //  (2) BIT      fester_Taktstrich_oder_Verschiebung
                //  (3) BIT      Moduswechsel
                //  (4) BIT      Triole_etc
                //  (5) BIT[4]   Anzahl_Noten
                bool hasExtras     = (value & 0x01) > 0;
                bool hasMovement   = (value & 0x02) > 0;
                bool hasModeChange = (value & 0x04) > 0;
                bool hasTriplet    = (value & 0x08) > 0;
                int  notes         = (value & 0xF0) >> 4;

                if (hasModeChange)
                {
                    // C2FORMAT.TXT line 264ff
                    //  if Moduswechsel
                    //    +---+---+---+---+---+---+---+---+
                    //    |3      |2          |1          |
                    //    +---+---+---+---+---+---+---+---+
                    //    BIT[3] Schluesselform (0=G, 1=C, 2=F, 3=Schlagz. 4=kein, 5=unveraendert)
                    //    BIT[3] keyByte-Linie (0 = oberste Linie, ... , 4 = unterste Linie)
                    //    BIT[2] keyByte-Oktavierung (0=nach oben, 1=keine, 2= nach unten)
                    //    +---+---+---+---+---+---+---+---+
                    //    |2              |1              |
                    //    +---+---+---+---+---+---+---+---+
                    //    (1) BIT[4] Tonart (0=7b ... 7=c-Dur ... 14=7#, 15=unveraendert)
                    //    (2) BIT[4] Taktnenner (0=ganze, 1=halbe, ..., 6=1/64, 15=kein Takt)
                    //    BYTE   Taktzaehler  (255=unveraendert)
                    //  endif
                    byte mb1           = accords[index++];
                    int  keyForm       = mb1 & 0x07;
                    int  keyLine       = (mb1 & 0x38) >> 3;
                    int  keyOctavation = (mb1 & 0xC0) >> 6;
                    if (keyForm != 5)
                    {
                        // key has changed ("5" is unchanged)
                        this.currentKeyForm[staffNumber]       = keyForm;
                        this.currentKeyLine[staffNumber]       = keyLine;
                        this.currentKeyOctavation[staffNumber] = keyOctavation;
                        // TODO Anzeigen
                    }

                    byte mb2       = accords[index++];
                    int  signature = mb2 & 0x0F;
                    if (signature != 15)
                    {
                        // signature has changed ("15" is unchanged)
                        var currentSignature = this.GetCurrentSignature(staffNumber, voiceNumber);
                        if (currentSignature != signature)
                        {
                            Console.WriteLine($"changing signature from {currentSignature} to {signature}");
                            //this.currentSignature[staffNumber] = signature;
                            this.SetCurrentSignature(staffNumber, voiceNumber, signature);
                            currentMeasure.AddSignature(signature);
                        }
                    }

                    int  nenner  = (mb2 & 0xF0) >> 4;
                    byte zaehler = accords[index++];
                    if (zaehler != 255)
                    {
                        int newBeats = zaehler;
                        int newBeatType;
                        switch (nenner)
                        {
                        case 0:
                            newBeatType = 1;
                            break;

                        case 1:
                            newBeatType = 2;
                            break;

                        case 2:
                            newBeatType = 4;
                            break;

                        case 3:
                            newBeatType = 8;
                            break;

                        case 4:
                            newBeatType = 16;
                            break;

                        case 5:
                            newBeatType = 32;
                            break;

                        case 6:
                            newBeatType = 64;
                            break;

                        default:
                            throw new NotImplementedException();
                        }

                        // check if beat type is really changed
                        // capella is writing type with every system
                        if (this.currentBeats[staffNumber] != newBeats ||
                            this.currentBeatType[staffNumber] != newBeatType)
                        {
                            this.currentBeats[staffNumber]    = newBeats;
                            this.currentBeatType[staffNumber] = newBeatType;

                            attributes att     = currentMeasure.GetAttributes();
                            var        theTime = new time();
                            att.time      = ArrayExtensions.ArrayAppend(att.time, theTime);
                            theTime.Items = ArrayExtensions.ArrayAppend(
                                theTime.Items,
                                this.currentBeats[staffNumber].ToString(CultureInfo.InvariantCulture));
                            theTime.ItemsElementName = ArrayExtensions.ArrayAppend(
                                theTime.ItemsElementName,
                                ItemsChoiceType10.beats);
                            theTime.Items            = ArrayExtensions.ArrayAppend(theTime.Items, this.currentBeatType[staffNumber].ToString(CultureInfo.InvariantCulture));
                            theTime.ItemsElementName = ArrayExtensions.ArrayAppend(theTime.ItemsElementName, ItemsChoiceType10.beattype);
                        }
                    }
                }

                if (hasMovement)
                {
                    // C2FORMAT.TXT line 281ff
                    //  if fester_Taktstrich_oder_Verschiebung
                    //    +---+---+---+---+---+---+---+---+
                    //    |2              |1              |
                    //    +---+---+---+---+---+---+---+---+
                    //    BIT[4] fester_Taktstrich (0..6=einfach, doppelt, Schluss Wdh-Ende,
                    //                              Wdh.Anf., Wdh-Ende/Anf)
                    //    BIT[4] Horizontalverschiebung
                    //  endif
                    byte move = accords[index++];
                }

                int  tripletCounter = 0;
                bool tripartit;
                if (hasTriplet)
                {
                    // C2FORMAT.TXT line 290ff
                    //  if Triole_etc (Beispiele: Triole / Duole)
                    //    +---+---+---+---+---+---+---+---+
                    //    |3          |2  |1              |
                    //    +---+---+---+---+---+---+---+---+
                    //    (1) BIT[4] Zaehler           ( 3  /  2 )
                    //    (2) BIT[1] tripartit        ( 0  /  1 )
                    //    (3) Bit[3] reserviert (=0)
                    //  endif
                    byte triole = accords[index++];
                    tripletCounter = triole & 0x0F;
                    tripartit      = (triole & 0x10) > 0;
                }

                if (hasExtras)
                {
                    // C2FORMAT.TXT line 299ff
                    //  if Extras
                    //    BYTE[3] reserviert
                    //    BYTE    Anzahl_GrafikObjekte
                    //    GRAFIKOBJEKT[Anzahl_GrafikObjekte]
                    //  endif
                    byte r1 = accords[index++];
                    byte r2 = accords[index++];
                    byte r3 = accords[index++];
                    byte graphObjectCount = accords[index++];

                    for (int i = 0; i < graphObjectCount; i++)
                    {
                        // C2FORMAT.TXT line 342ff
                        //GRAFIKOBJEKT
                        //  BYTE Typ
                        //  BYTE Laenge_des_Notenbereichs
                        //  case Typ
                        //    0: BINDEBOGEN
                        //    1: N_OLENKLAMMER
                        //    2: DE_CRESCENDO
                        //    3: NOTENLINIEN
                        //    4: VOLTENKLAMMER
                        //    5: TRILLERSCHLANGE
                        //    6: TEXTOBJEKT
                        //    7: LINIE
                        //    8: RECHTECK
                        //    9: ELLIPSE
                        //  endcase
                        //end
                        byte type   = accords[index++];
                        byte length = accords[index++];
                        switch (type)
                        {
                        case 0:     // BINDEBOGEN
                            index += 16;
                            break;

                        case 1:     // N_OLENKLAMMER
                            index += 10;
                            break;

                        case 2:     // DE_CRESCENDO
                            index += 8;
                            break;

                        case 3:     // NOTENLINIEN
                            index += 6;
                            break;

                        case 4:     // VOLTENKLAMMER
                            index += 7;
                            break;

                        case 5:     // TRILLERSCHLANGE
                            index += 7;
                            break;

                        case 6:     // TEXTOBJEKT
                            int x = (byte)accords[index] + (byte)accords[index + 1] * 256;
                            index += 2;
                            int y = (byte)accords[index] + (byte)accords[index + 1] * 256;
                            index += 2;
                            byte fontInfo  = accords[index++];
                            int  fontIndex = fontInfo & 0x07;
                            if (fontIndex == 2)
                            {
                                index += 56;
                            }

                            while (accords[index] != 0)
                            {
                                index++;
                            }

                            // terminator
                            index++;
                            break;

                        case 7:     // LINIE
                            index += 10;
                            break;

                        case 8:     // RECHTECK
                            index += 10;
                            break;

                        case 9:     // ELLIPSE
                            index += 10;
                            break;
                        }
                    }
                }

                // C2FORMAT.TXT line 305ff
                //  +---+---+---+---+---+---+---+---+
                //  |4  |3  |2      |1              |
                //  +---+---+---+---+---+---+---+---+
                //  (1) BIT[4] Notenwert
                //  (2) BIT[2] Punktierung (0..3 = ohne, einfach doppelt, Zeilenende)
                //  (3) BIT    halbe_Groesse
                //  (4) BIT    ohne_Wert
                //  +---+---+---+---+---+---+---+---+
                //  |6          |5  |4  |3  |2  |1  |              |
                //  +---+---+---+---+---+---+---+---+
                //  (1) BIT    Balken trennen
                //  (2) BIT    Balken verbinden
                //  (3) BIT    unsichtbar
                //  (4) BIT    Haltebogen
                //  (5) BIT    expl_Vorzeichen
                //  (6) BIT[3] Kopfform
                //  +---+---+---+---+---+---+---+---+
                //  |4  |3  |2      |1              |
                //  +---+---+---+---+---+---+---+---+
                //  (1) BIT[4] Artikulationszeichen
                //  (2) BIT[2] Hals
                //  (3) BIT[1] Pause auch in zweistimmiger Zeile zentriert (ab V2.1)
                //  (4) BIT[1] Atemzeichen (neu in V2.2)
                //  NOTE[Anzahl_Noten]
                byte b1             = accords[index++];
                int  noteDuration   = b1 & 0x0F;
                int  notePunctation = (b1 & 0x30) >> 4;
                bool noteHalfSize   = (b1 & 0x40) > 0;
                bool noteNoValue    = (b1 & 0x80) > 0;

                byte b2          = accords[index++];
                bool splitBar    = (b2 & 0x01) > 0;
                bool combineBar  = (b2 & 0x02) > 0;
                bool invisible   = (b2 & 0x04) > 0;
                bool connectNext = (b2 & 0x08) > 0;
                bool showSign    = (b2 & 0x10) > 0;
                int  headFormat  = (b2 & 0xE0) >> 5;

                byte b3 = accords[index++];

                if (noteNoValue && invisible)
                {
                    // current note is just a placeholder
                    // skip and continue
                    for (int i = 0; i < notes; i++)
                    {
                        index++;
                    }

                    continue;
                }

                if (notePunctation == 3)
                {
                    // "3" in notePuncation marks end of line
                    // just leave accords loop
                    if (index != accords.Length)
                    {
                        throw new InvalidOperationException(
                                  "Current member identifies end of line, but there are pending elements.");
                    }

                    break;
                }

                var noteType = new notetype();
                int noteTicks;
                switch (noteDuration)
                {
                case 0:
                    noteType.Value = notetypevalue.whole;
                    noteTicks      = 128;
                    if (notes == 0)
                    {
                        // whole rest -> whole measure
                        noteTicks = this.currentBeats[staffNumber] * (128 / this.currentBeatType[staffNumber]);
                    }

                    break;

                case 1:
                    noteType.Value = notetypevalue.half;
                    noteTicks      = 64;
                    break;

                case 2:
                    noteType.Value = notetypevalue.quarter;
                    noteTicks      = 32;
                    break;

                case 3:
                    noteType.Value = notetypevalue.eighth;
                    noteTicks      = 16;
                    break;

                case 4:
                    noteType.Value = notetypevalue.Item16th;
                    noteTicks      = 8;
                    break;

                case 5:
                    noteType.Value = notetypevalue.Item32nd;
                    noteTicks      = 4;
                    break;

                case 6:
                    noteType.Value = notetypevalue.Item64th;
                    noteTicks      = 2;
                    break;

                case 7:
                    noteType.Value = notetypevalue.Item128th;
                    noteTicks      = 1;
                    break;

                default:
                    throw new NotImplementedException();
                }

                emptyplacement[] dots = null;
                if (notePunctation == 1)
                {
                    noteTicks = (noteTicks * 3) / 2;
                    dots      = new[] { new emptyplacement() };
                }
                else if (notePunctation == 2)
                {
                    noteTicks = (noteTicks * 5) / 3;
                    dots      = new[] { new emptyplacement(), new emptyplacement() };
                }

                if (notes == 0)
                {
                    // rest
                    var newNote = new note();
                    var theRest = newNote.AddRest();

                    // the rest sign will be painted on step A by default in octave 4
                    theRest.displaystep = step.B;
                    int octave = 4;
                    if (voiceNumber == 1)
                    {
                        theRest.displaystep = step.F;
                        octave = 5;
                    }
                    else if (voiceNumber == 2)
                    {
                        theRest.displaystep = step.E;
                    }

                    if (this.currentKeyOctavation[staffNumber] == 0)
                    {
                        // in case of upper octavation we have to increase octave number
                        octave++;
                    }
                    else if (this.currentKeyOctavation[staffNumber] == 2)
                    {
                        // in case of lower octavation we have to decrease octave number
                        octave--;
                    }

                    // in case of F clef we use different octave and step for rest sign
                    if (this.currentKeyForm[staffNumber] == 2)
                    {
                        octave--;
                        theRest.displaystep = step.D;
                        if (voiceNumber == 1)
                        {
                            theRest.displaystep = step.A;
                        }
                        else if (voiceNumber == 2)
                        {
                            theRest.displaystep = step.G;
                            octave--;
                        }
                    }

                    theRest.displayoctave = octave.ToString(CultureInfo.InvariantCulture);

                    newNote.AddDuration(noteTicks);

                    if (noteDuration > 0)
                    {
                        newNote.type = noteType;
                    }

                    newNote.dot = dots;

                    if (voiceNumber > 0)
                    {
                        newNote.voice = voiceNumber.ToString(CultureInfo.InvariantCulture);
                    }

                    if (invisible)
                    {
                        // TODO printobject is yet generated from xsd2code
                        // newNote.printobject = no;
                    }

                    currentMeasure.AddNote(newNote);
                }

                for (int i = 0; i < notes; i++)
                {
                    // C2FORMAT.TXT line 334ff
                    //NOTE
                    //  +---+---+---+---+---+---+---+---+
                    //  |2      |1                      |
                    //  +---+---+---+---+---+---+---+---+
                    //  (1) BIT[6] relative diatonische Hoehe + 32
                    //  (2) BIT[2] Alteration + 2
                    //end
                    byte noteValue     = accords[index++];
                    int  relativePitch = (noteValue & 0x3F) - 32;
                    int  alteration    = ((noteValue & 0xC0) >> 6) - 2;

                    var newNote = new note();

                    if (i > 0)
                    {
                        newNote.SetChord();
                    }

                    var p = pitch.CreatePitch(
                        relativePitch,
                        alteration,
                        this.currentKeyForm[staffNumber],
                        this.currentKeyOctavation[staffNumber],
                        this.GetCurrentSignature(staffNumber, voiceNumber));
                    newNote.AddPitch(p);

                    newNote.type = noteType;
                    newNote.dot  = dots;

                    if (voiceNumber > 0)
                    {
                        newNote.voice = voiceNumber.ToString(CultureInfo.InvariantCulture);
                    }

                    // TRIPLETS
                    if (hasTriplet && tripletCounter > 0)
                    {
                        newNote.timemodification = new timemodification
                        {
                            actualnotes = tripletCounter.ToString(CultureInfo.InvariantCulture),
                            normalnotes = "2"
                        };
                        // TODO How to remove normal-type?
                        // TODO :
                        //newNote.notations = new notations[1]
                        //{
                        //    new notations { Items = new object[1]
                        //    {
                        //        new tuplet { type = startstop.start } }
                        //    }
                        //};

                        double accurateNoteTicks = noteTicks * 2;
                        accurateNoteTicks /= tripletCounter;
                        noteTicks          = (int)accurateNoteTicks;
                        tripletRests      += accurateNoteTicks - noteTicks;
                        if (tripletRests > 0.5)
                        {
                            noteTicks    += 1;
                            tripletRests -= 1;
                        }
                    }

                    // the duration values corresponds with the divisions value set in readLAYOUT()
                    newNote.AddDuration(noteTicks);

                    if (connectNext && this.notesConnecting[staffNumber] == false)
                    {
                        newNote.AddTieStart();
                    }
                    else if (connectNext == false && this.notesConnecting[staffNumber])
                    {
                        newNote.AddTieStop();
                    }

                    this.notesConnecting[staffNumber] = connectNext;

                    lyricsProcessor.AddLyrics(newNote);

                    currentMeasure.AddNote(newNote);
                }

                currentMeasureTicks += noteTicks;
                if (currentMeasureTicks >= this.currentBeats[staffNumber] * (128 / this.currentBeatType[staffNumber]))
                {
                    currentMeasure.AddBackup(currentMeasureTicks);

                    measureIndex++;
                    if (measureIndex >= this.measures[staffNumber].Count)
                    {
                        var newMeasure = new scorepartwisePartMeasure();
                        this.measures[staffNumber].Add(newMeasure);
                        newMeasure.number = (measureIndex + 1).ToString(CultureInfo.InvariantCulture);
                    }

                    currentMeasureTicks = 0;
                }
            }
        }