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     This file contains code from
9     Other copyrights also apply to some parts of this work.  Please
10     see the AUTHORS file and individual file headers for details.
11 
12     This program is free software; you can redistribute it and/or
13     modify it under the terms of the GNU General Public License as
14     published by the Free Software Foundation; either version 2 of the
15     License, or (at your option) any later version.  See the file
16     COPYING included with this distribution for more information.
17 */
18 
19 #include "NoteSymbols.h"
20 #include "Fingering.h"
21 #include "misc/Debug.h"
22 
23 namespace Rosegarden
24 {
25 
26 namespace Guitar
27 {
28 NoteSymbols::posPair
getX(int imgWidth,unsigned int stringNb,unsigned int nbOfStrings) const29 NoteSymbols::getX ( int imgWidth, unsigned int stringNb, unsigned int nbOfStrings ) const
30 {
31     /*
32             std::cout << "NoteSymbols::getX - input values" << std::endl
33             << "  position: " << position << std::endl
34             << "  string #: " << string_num << std::endl
35             << "  scale:    " << scale << std::endl;
36     */
37     unsigned int lBorder = getLeftBorder( imgWidth );
38     unsigned int guitarChordWidth = getGuitarChordWidth( imgWidth );
39     unsigned int columnWidth = guitarChordWidth / nbOfStrings;
40     return std::make_pair( ( stringNb * columnWidth + lBorder ), columnWidth );
41 }
42 
43 NoteSymbols::posPair
getY(int imgHeight,unsigned int fretNb,unsigned int nbOfFrets) const44 NoteSymbols::getY ( int imgHeight, unsigned int fretNb, unsigned int nbOfFrets ) const
45 {
46     /*
47             std::cout << "NoteSymbols::getY - input values" << std::endl
48             << "  position: " << fret_pos << std::endl
49             << "  max frets:   " << maxFretNum << std::endl
50             << "  scale:    " << scale << std::endl;
51     */
52     unsigned int tBorder = getTopBorder( imgHeight );
53     unsigned int guitarChordHeight = getGuitarChordHeight( imgHeight );
54     unsigned int rowHeight = guitarChordHeight / nbOfFrets;
55     return std::make_pair( ( ( fretNb * rowHeight ) + tBorder ), rowHeight );
56 }
57 
58 void
drawMuteSymbol(bool big,QPainter * p,unsigned int position) const59 NoteSymbols::drawMuteSymbol ( bool big,
60                               QPainter* p,
61                               unsigned int position ) const
62 {
63     RG_DEBUG << "NoteSymbols::drawMuteSymbol()";
64 
65     QRect v = p->viewport();
66 
67     posPair x_pos = getX ( v.width(), position, m_nbOfStrings );
68     unsigned int y_pos = (getTopBorder( v.height() ) / 2) + 2;
69     double columnWidth = x_pos.second;
70     unsigned int width = static_cast<unsigned int>( columnWidth * 0.7 );
71     unsigned int height = static_cast<unsigned int>( columnWidth * 0.7 );
72 
73     //std::cout << "NoteSymbols::drawMuteSymbol - drawing Mute symbol at string #" << position
74     //<< std::endl;
75 
76     QPen pen(Qt::black);
77     if (big) pen.setWidth(2);
78 
79     p->save();
80 
81     p->setPen(pen);
82 
83     p->drawLine ( x_pos.first - ( width / 2 ),
84                   y_pos - ( height / 2 ),
85                   ( x_pos.first + ( width / 2 ) ),
86                   y_pos + ( height / 2 ) );
87 
88     p->drawLine( x_pos.first + ( width / 2 ),
89                  y_pos - ( height / 2 ),
90                  ( x_pos.first - ( width / 2 ) ),
91                  y_pos + ( height / 2 ) );
92 
93     p->restore();
94 }
95 
96 void
drawOpenSymbol(bool big,QPainter * p,unsigned int position) const97 NoteSymbols::drawOpenSymbol ( bool big,
98                               QPainter* p,
99                               unsigned int position ) const
100 {
101     RG_DEBUG << "NoteSymbols::drawOpenSymbol()";
102 
103     QRect v = p->viewport();
104     posPair x_pos = getX ( v.width(), position, m_nbOfStrings );
105     unsigned int y_pos = (getTopBorder(v.height()) / 2) + 2;
106     double columnWidth = x_pos.second;
107     unsigned int radius = static_cast<unsigned int>( columnWidth * 0.7 );
108 
109     //std::cout << "NoteSymbols::drawOpenSymbol - drawing Open symbol at string #" << position
110     //<< std::endl;
111 
112     QPen stylus(Qt::black);
113     if (big) stylus.setWidth(2);
114 
115     p->save();
116 
117     p->setPen(stylus);
118     p->drawEllipse( x_pos.first - ( radius / 2 ),
119                     y_pos - ( radius / 2 ),
120                     radius,
121                     radius );
122 
123     // little hack here to try to fix a problem I don't quite understand with
124     // brute force
125     p->setBrush(Qt::white);
126     if (big) {
127         p->drawEllipse( x_pos.first - ( radius / 2 ) + 1,
128                         y_pos - ( radius / 2 ) + 1,
129                         radius - 2,
130                         radius - 2);
131     }/* else {
132         p->drawEllipse( x_pos.first - ( radius / 2 ) + 1,
133                         y_pos - ( radius / 2 ) + 1,
134                         radius - 3,
135                         radius - 3);
136     }*/
137 
138     p->restore();
139 }
140 
141 void
drawNoteSymbol(bool,QPainter * p,unsigned int stringNb,int fretNb,bool transient) const142 NoteSymbols::drawNoteSymbol ( bool /* big */,
143                               QPainter* p,
144                               unsigned int stringNb,
145                               int fretNb,
146                               bool transient ) const
147 {
148 //    NOTATION_DEBUG << "NoteSymbols::drawNoteSymbol - string: " << stringNb << ", fret:" << fretNb;
149     RG_DEBUG << "NoteSymbols::drawNoteSymbol()";
150 
151     QRect v = p->viewport();
152     posPair x_pos = getX ( v.width(), stringNb, m_nbOfStrings );
153     posPair y_pos = getY ( v.height(), fretNb, m_nbOfFrets );
154     double columnWidth = x_pos.second;
155     unsigned int radius;
156 
157     p->save();
158 
159     if (transient) {
160         radius =  static_cast<unsigned int>( columnWidth /* * 0.9 */ );
161         p->setPen(QColor(0, 0x10, 0xFF, 0xAA));
162     } else {
163         radius =  static_cast<unsigned int>( columnWidth * 0.7 );
164         p->setBrush(Qt::black);
165     }
166 
167     int x = x_pos.first - ( radius / 2 ),
168         y = y_pos.first + ( (y_pos.second - radius) / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN;
169 
170 //        y = y_pos.first - (radius / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN;
171 
172 //    RG_DEBUG << "NoteSymbols::drawNoteSymbol : rect = " << QRect(x,y, radius, radius);
173 
174     p->drawEllipse( x,
175                     y,
176                     radius,
177                     radius );
178 
179     p->restore();
180 }
181 
182 void
drawBarreSymbol(QPainter * p,int fretNb,unsigned int start,unsigned int end) const183 NoteSymbols::drawBarreSymbol ( QPainter* p,
184                                int fretNb,
185                                unsigned int start,
186                                unsigned int end ) const
187 {
188 
189     //std::cout << "NoteSymbols::drawBarreSymbol - start: " << start << ", end:" << end << std::endl;
190 
191     drawNoteSymbol (false, p, start, fretNb );
192 
193     if ( ( end - start ) >= 1 ) {
194         QRect v = p->viewport();
195         posPair startXPos = getX ( v.width(), start, m_nbOfStrings );
196         posPair endXPos = getX ( v.width(), end, m_nbOfStrings );
197         posPair y_pos = getY ( v.height(), fretNb, m_nbOfFrets );
198         double columnWidth = startXPos.second;
199         unsigned int thickness = static_cast<unsigned int>( columnWidth * 0.7 );
200 
201         QPen pen(Qt::red); // to see if this is ever used
202 
203         p->save();
204 
205         p->setPen(pen);
206 
207         p->drawRect( startXPos.first,
208                      y_pos.first + ( y_pos.second / 4 ) + TOP_GUITAR_CHORD_MARGIN,
209                      endXPos.first - startXPos.first,
210                      thickness );
211 
212         p->restore();
213     }
214 
215     drawNoteSymbol (false, p, end, fretNb );
216 }
217 
218 void
drawFretNumber(QPainter * p,unsigned int fret_num) const219 NoteSymbols::drawFretNumber ( QPainter* p,
220                               unsigned int fret_num ) const
221 {
222     if ( fret_num > 1 ) {
223         QRect v = p->viewport();
224         unsigned int imgWidth = v.width();
225         unsigned int imgHeight = v.height();
226 
227         p->save();
228         QFont font;
229         font.setPixelSize(getFontPixelSize(v.width(), v.height()));
230         p->setFont(font);
231 
232         QString tmp;
233         tmp.setNum( fret_num );
234 
235         // Get the Y coord of the first fret.
236         posPair y_pos = getY( imgHeight, 1, m_nbOfFrets );
237 
238         // Compute the "true" center.
239         int y = y_pos.first + TOP_GUITAR_CHORD_MARGIN;
240 
241         // Make a rect around the center.  Don't worry about the size as
242         // boundingRect() will give us the required bounding rect.
243         QRect rect(getLeftBorder( imgWidth ) / 4, y - 10, 20, 20);
244 
245         p->setPen(Qt::black);
246 
247         // Using AlignVCenter ends up slightly high as the descent is probably
248         // also included.  Looks OK, though.
249 
250         // Get the required bounding rect.
251         QRect requiredRect = p->boundingRect(
252             rect, Qt::AlignVCenter | Qt::AlignLeft, tmp);
253 
254         // Use the required bounding rect to draw the text.
255         p->drawText(requiredRect, Qt::AlignVCenter | Qt::AlignLeft, tmp);
256 
257         p->restore();
258     }
259 }
260 
261 void
drawFrets(QPainter * p) const262 NoteSymbols::drawFrets ( QPainter* p ) const
263 {
264     /*
265             std::cout << "NoteSymbols::drawFretHorizontalLines" << std::endl
266             << "  scale: " << scale << std::endl
267             << "  frets: " << fretsDisplayed << std::endl
268             << "  max string: " << maxStringNum << std::endl;
269     */
270 
271     QRect v = p->viewport();
272     unsigned int imgWidth = v.width();
273     unsigned int imgHeight = v.height();
274     //unsigned int endXPos = getGuitarChordWidth(imgWidth) + getLeftBorder(imgWidth);
275     posPair endXPos = getX ( imgWidth, m_nbOfStrings - 1, m_nbOfStrings );
276 
277     unsigned int yGuitarChord = getGuitarChordHeight( imgHeight );
278     unsigned int rowHeight = yGuitarChord / m_nbOfFrets;
279 
280     QPen pen(p->pen());
281     pen.setWidth(imgHeight >= 100 ? FRET_PEN_WIDTH : FRET_PEN_WIDTH / 2);
282     pen.setColor(Qt::black);
283     p->save();
284     p->setPen(pen);
285     unsigned int y_pos = (getY ( imgHeight, 0, m_nbOfFrets )).first + TOP_GUITAR_CHORD_MARGIN;
286 
287 //    NOTATION_DEBUG << "NoteSymbols::drawFrets : " << m_nbOfFrets;
288 
289     // Horizontal lines
290     for ( unsigned int i = 0; i <= m_nbOfFrets; ++i ) {
291 
292         /* This code borrowed from KGuitar 0.5 */
293         p->drawLine( getLeftBorder( imgWidth ),
294                      y_pos,
295                      endXPos.first,
296                      y_pos);
297 //        NOTATION_DEBUG << "NoteSymbols::drawFrets : " << QPoint(getLeftBorder(imgWidth), y_pos)
298 //                       << " to " << QPoint(endXPos.first, y_pos) << endl;
299 
300 
301        y_pos += rowHeight;
302     }
303 
304     p->restore();
305 
306 }
307 
308 void
drawStrings(QPainter * p) const309 NoteSymbols::drawStrings ( QPainter* p ) const
310 {
311     // Vertical lines
312     QRect v = p->viewport();
313     int imgHeight = v.height();
314     int imgWidth = v.width();
315 
316     unsigned int startPos = getTopBorder( imgHeight ) + TOP_GUITAR_CHORD_MARGIN;
317     unsigned int endPos = (getY ( imgHeight, m_nbOfFrets, m_nbOfFrets )).first + TOP_GUITAR_CHORD_MARGIN;
318 
319     unsigned int guitarChordWidth = getGuitarChordWidth( imgWidth );
320     unsigned int columnWidth = guitarChordWidth / m_nbOfStrings;
321 
322     unsigned int x_pos = (getX ( imgWidth, 0, m_nbOfStrings )).first;
323 
324     QPen pen(p->pen());
325     pen.setWidth(imgWidth >= 100 ? STRING_PEN_WIDTH : STRING_PEN_WIDTH / 2);
326     pen.setColor(Qt::black);
327     p->save();
328     p->setPen(pen);
329 
330     for ( unsigned int i = 0; i < m_nbOfStrings; ++i ) {
331 
332         /* This code borrowed from KGuitar 0.5 */
333         p->drawLine( x_pos,
334                      startPos,
335                      x_pos,
336                      endPos );
337 
338        x_pos += columnWidth;
339     }
340 
341     p->restore();
342 
343 }
344 
getTransientNoteSymbolRect(QSize guitarChordSize,unsigned int stringNb,int fretNb) const345 QRect NoteSymbols::getTransientNoteSymbolRect(QSize guitarChordSize,
346                                               unsigned int stringNb,
347                                               int fretNb) const
348 {
349     posPair x_pos = getX ( guitarChordSize.width(), stringNb, m_nbOfStrings );
350     posPair y_pos = getY ( guitarChordSize.height(), fretNb, m_nbOfFrets );
351     double columnWidth = x_pos.second;
352     unsigned int radius =  static_cast<unsigned int>( columnWidth /* * 0.9 */ );
353 
354     int x = x_pos.first - ( radius / 2 ),
355         y = y_pos.first + ( (y_pos.second - radius) / 2) - y_pos.second + TOP_GUITAR_CHORD_MARGIN;
356 
357     return QRect(x, y, radius, radius);
358 }
359 
360 unsigned int
getTopBorder(unsigned int imgHeight) const361 NoteSymbols::getTopBorder ( unsigned int imgHeight ) const
362 {
363     return static_cast<unsigned int>( TOP_BORDER_PERCENTAGE * imgHeight );
364 }
365 
366 unsigned int
getBottomBorder(unsigned int imgHeight) const367 NoteSymbols::getBottomBorder ( unsigned int imgHeight ) const
368 {
369     return static_cast<unsigned int>( imgHeight * BOTTOM_BORDER_PERCENTAGE );
370 }
371 
372 unsigned int
getLeftBorder(unsigned int imgWidth) const373 NoteSymbols::getLeftBorder ( unsigned int imgWidth ) const
374 {
375     unsigned int left = static_cast<unsigned int>( imgWidth * LEFT_BORDER_PERCENTAGE );
376     if ( left < 15 ) {
377         left = 15;
378     }
379     return left;
380 }
381 
382 unsigned int
getRightBorder(unsigned int imgWidth) const383 NoteSymbols::getRightBorder ( unsigned int imgWidth ) const
384 {
385     return static_cast<unsigned int>( imgWidth * RIGHT_BORDER_PERCENTAGE );
386 }
387 
388 unsigned int
getGuitarChordWidth(int imgWidth) const389 NoteSymbols::getGuitarChordWidth ( int imgWidth ) const
390 {
391     return static_cast<unsigned int>( imgWidth * GUITAR_CHORD_WIDTH_PERCENTAGE );
392 }
393 
394 unsigned int
getGuitarChordHeight(int imgHeight) const395 NoteSymbols::getGuitarChordHeight ( int imgHeight ) const
396 {
397     return static_cast<unsigned int>( imgHeight * GUITAR_CHORD_HEIGHT_PERCENTAGE );
398 }
399 
400 unsigned int
getFontPixelSize(int,int imgHeight) const401 NoteSymbols::getFontPixelSize ( int /* imgWidth */, int imgHeight ) const
402 {
403     return std::max(8, imgHeight / 10);
404 }
405 
406 std::pair<bool, unsigned int>
getStringNumber(int imgWidth,unsigned int x_pos,unsigned int maxStringNum) const407 NoteSymbols::getStringNumber ( int imgWidth,
408                                unsigned int x_pos,
409                                unsigned int maxStringNum ) const
410 {
411     /*
412         std::cout << "NoteSymbols::getNumberOfStrings - input values" << std::endl
413         << "  X position: " << x_pos << std::endl
414         << "  string #: " << maxStringNum << std::endl
415         << "  image width:    " << imgWidth << std::endl;
416     */
417     bool valueOk = false;
418 
419     posPair xPairPos;
420     unsigned int min = 0;
421     unsigned int max = 0;
422     unsigned int result = 0;
423 
424     for ( unsigned int i = 0; i < maxStringNum; ++i ) {
425         xPairPos = getX ( imgWidth, i, maxStringNum );
426 
427         // If the counter equals zero then we are at the first
428         // string to the left
429         if ( i == 0 ) {
430             // Add 10 pixel buffer to range comparison
431             min = xPairPos.first - 10;
432         } else {
433             min = xPairPos.first - xPairPos.second / 2;
434         }
435 
436         // If the counter equals the maxString number -1 then we are at the last
437         // string to the right
438         if ( i == ( maxStringNum - 1 ) ) {
439             // Add 10 pixel buffer to range comparison
440             max = xPairPos.first + 10;
441         } else {
442             max = xPairPos.first + xPairPos.second / 2;
443         }
444 
445         if ( ( x_pos >= min ) && ( x_pos <= max ) ) {
446             result = i;
447             valueOk = true;
448             break;
449         }
450     }
451 
452     //std::cout << "NoteSymbols::getNumberOfStrings - string: #" << result << std::endl;
453     return std::make_pair( valueOk, result );
454 }
455 
456 std::pair<bool, unsigned int>
getFretNumber(int imgHeight,unsigned int y_pos,unsigned int maxFretNum) const457 NoteSymbols::getFretNumber ( int imgHeight,
458                              unsigned int y_pos,
459                              unsigned int maxFretNum ) const
460 {
461     /*
462         std::cout << "NoteSymbols::getNumberOfFrets - input values" << std::endl
463         << "  Y position: " << y_pos << std::endl
464         << "  max frets:   " << maxFretNum << std::endl
465         << "  image height:    " << imgHeight << std::endl;
466     */
467 
468     bool valueOk = false;
469     unsigned int tBorder = getTopBorder( imgHeight );
470     unsigned int result = 0;
471 
472     if ( y_pos < tBorder ) {
473         // User pressing above the guitar chord to mark line muted or opened
474         valueOk = true;
475     } else {
476         posPair min_pos;
477         posPair max_pos;
478 
479         for ( unsigned int i = 0; i < maxFretNum; ++i ) {
480             min_pos = getY ( imgHeight, i, maxFretNum );
481             max_pos = getY ( imgHeight, i + 1, maxFretNum );
482 
483             if ( ( y_pos >= min_pos.first ) && y_pos <= max_pos.first - 1 ) {
484                 result = i + 1;
485                 valueOk = true;
486                 break;
487             }
488         }
489     }
490     //    std::cout << "  fret #: " << result << std::endl;
491     return std::make_pair( valueOk, result );
492 }
493 
494 void
drawFingeringPixmap(const Guitar::Fingering & fingering,const Guitar::NoteSymbols & noteSymbols,QPainter * p)495 NoteSymbols::drawFingeringPixmap(const Guitar::Fingering& fingering, const Guitar::NoteSymbols& noteSymbols, QPainter *p)
496 {
497     unsigned int startFret = fingering.getStartFret();
498     unsigned int stringNb = 0;
499 
500     for (Fingering::const_iterator pos = fingering.begin();
501          pos != fingering.end();
502          ++pos, ++stringNb) {
503 
504         switch (*pos) {
505         case Fingering::OPEN:
506                 noteSymbols.drawOpenSymbol(false, p, stringNb);
507                 break;
508 
509         case Fingering::MUTED:
510                 noteSymbols.drawMuteSymbol(false, p, stringNb);
511                 break;
512 
513         default:
514                 noteSymbols.drawNoteSymbol(false, p, stringNb, *pos - (startFret - 1), false);
515                 break;
516         }
517     }
518 
519     // draw frets last, so the sharp lines don't get broken by the fuzzy
520     // outlines of the new antialiased note symbols
521     noteSymbols.drawFretNumber(p, startFret);
522     noteSymbols.drawFrets(p);
523     noteSymbols.drawStrings(p);
524 }
525 
526 
527 float const NoteSymbols::LEFT_BORDER_PERCENTAGE = 0.2;
528 float const NoteSymbols::RIGHT_BORDER_PERCENTAGE = 0.1;
529 float const NoteSymbols::GUITAR_CHORD_WIDTH_PERCENTAGE = 0.8;
530 float const NoteSymbols::TOP_BORDER_PERCENTAGE = 0.1;
531 float const NoteSymbols::BOTTOM_BORDER_PERCENTAGE = 0.1;
532 float const NoteSymbols::GUITAR_CHORD_HEIGHT_PERCENTAGE = 0.8;
533 int   const NoteSymbols::TOP_GUITAR_CHORD_MARGIN = 5;
534 int   const NoteSymbols::FRET_PEN_WIDTH = 2;
535 int   const NoteSymbols::STRING_PEN_WIDTH = 2;
536 
537 } /* namespace Guitar */
538 
539 }
540 
541