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