1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #include "Fingering.h"
19 
20 #include "misc/Debug.h"
21 
22 #include <QStringList>
23 #include <sstream>
24 #include <algorithm>
25 
26 namespace Rosegarden
27 {
28 
29 namespace Guitar
30 {
31 
Fingering(unsigned int nbStrings)32 Fingering::Fingering(unsigned int nbStrings) :
33     m_strings(nbStrings, MUTED)
34 {
35 }
36 
Fingering(QString s)37 Fingering::Fingering(QString s)
38 {
39     QString errString;
40     Fingering t = parseFingering(s, errString);
41     m_strings = t.m_strings;
42 }
43 
44 unsigned int
getStartFret() const45 Fingering::getStartFret() const
46 {
47     int min = 999, max = 0;
48     for(std::vector<int>::const_iterator i = m_strings.begin(); i != m_strings.end(); ++i) {
49         if (*i < min && *i > 0)
50             min = *i;
51         if (*i > max)
52             max = *i;
53     }
54 
55     if (max < 4)
56         min = 1;
57 
58     return min == 999 ? 1 : min;
59 }
60 
61 bool
hasBarre() const62 Fingering::hasBarre() const
63 {
64     int lastStringStatus = m_strings[getNbStrings() - 1];
65 
66     return ((m_strings[0] > OPEN && m_strings[0] == lastStringStatus) ||
67             (m_strings[1] > OPEN && m_strings[1] == lastStringStatus) ||
68             (m_strings[2] > OPEN && m_strings[2] == lastStringStatus));
69 }
70 
71 Fingering::Barre
getBarre() const72 Fingering::getBarre() const
73 {
74     // ??? This routine needs review and testing.  If guitar chords in
75     //     lilypond look strange, this is likely the reason.
76 
77     // Fret on the last string.  (6th string for standard guitar.)
78     int lastStringStatus = m_strings[getNbStrings() - 1];
79 
80     Barre res;
81 
82     res.fret = lastStringStatus;
83 
84     // For each string from first (0) to third (2).
85     for(unsigned int i = 0; i < 3; ++i) {
86         // If this string is not open (0) and it's at the same fret
87         // as the last (6th) string...
88         // ??? We can drop the check for OPEN if we check for
89         //     lastStringStatus == OPEN and bail before we get in here.
90         if (m_strings[i] > OPEN && m_strings[i] == lastStringStatus) {
91             res.start = i;
92 
93             // ??? Is this the way this is supposed to be?
94             //break;
95         }
96 
97         // ??? Based on indentation in previous versions, it seems like this
98         //     belongs in the if above.  Reformatting this to fix a compiler
99         //     warning, but don't have time to figure out a regression test.
100         //     As it is, this seems wrong.  It will only check the first
101         //     string.  If it's not at the same fret as the 6th string,
102         //     it will indicate that the barre goes from string 0 (or
103         //     perhaps garbage) to string 6 at string 6's fret.
104         break;
105     }
106 
107     res.end = 5;
108 
109     return res;
110 }
111 
112 Fingering
parseFingering(const QString & ch,QString & errorString)113 Fingering::parseFingering(const QString& ch, QString& errorString)
114 {
115 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
116     QStringList tokens = ch.split(' ', Qt::SkipEmptyParts);
117 #else
118     QStringList tokens = ch.split(' ', QString::SkipEmptyParts);
119 #endif
120 
121     unsigned int idx = 0;
122     Fingering fingering;
123 
124     for(QStringList::iterator i = tokens.begin(); i != tokens.end() && idx < fingering.getNbStrings(); ++i, ++idx) {
125         QString t = *i;
126         bool b = false;
127         unsigned int fn = t.toUInt(&b);
128         if (b) {
129 //            NOTATION_DEBUG << "Fingering::parseFingering : '" << t << "' = " << fn;
130             fingering[idx] = fn;
131         } else if (t.toLower() == "x") {
132 //            NOTATION_DEBUG << "Fingering::parseFingering : '" << t << "' = MUTED\n";
133             fingering[idx] = MUTED;
134         } else {
135             errorString = tr("couldn't parse fingering '%1' in '%2'").arg(t).arg(ch);
136         }
137     }
138 
139     return fingering;
140 }
141 
142 
toString() const143 std::string Fingering::toString() const
144 {
145     std::stringstream s;
146 
147     for(std::vector<int>::const_iterator i = m_strings.begin(); i != m_strings.end(); ++i) {
148         if (*i >= 0)
149             s << *i << ' ';
150         else
151             s << "x ";
152     }
153 
154     return s.str();
155 }
156 
operator <(const Fingering & a,const Fingering & b)157 bool operator<(const Fingering& a, const Fingering& b)
158 {
159     for(unsigned int i = 0; i < Fingering::DEFAULT_NB_STRINGS; ++i) {
160         if (a.getStringStatus(i) != b.getStringStatus(i)) {
161             return a.getStringStatus(i) < b.getStringStatus(i);
162         }
163     }
164     return false;
165 }
166 
167 }
168 
169 }
170