package ONO::Lib::Audio::Pro::Guitar::FretBoard;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################
use strict;
use ONO::Lib::Audio::Pro::Music::LangKit;
use ONO::Lib::Audio::Pro::Music::Theory;
###############################################################################
# guitar
###############################################################################
sub display {
my (
$self,
$script_url,
$lang,
$BLK_ref,
$vars_ref,
$switches,
) = @_;
my %vars = %$vars_ref;
#: ONO Guitar Fretboard
#:
#: -C hide controls
#: -E empty fretboard (can then be filled using vars)
#: -T hide title
#:
#: how to activate notes manually:
#:
#: guitar_display_note_0 display the first note of the current scale
#: guitar_display_note_C display all 'C' notes
#:
#: guitar_white_note_0 display the first note of the current scale, force it white
#: guitar_yellow_note_0 display the first note of the current scale, force it yellow
my $MLK_ref = ONO::Lib::Audio::Pro::Music::LangKit->get($lang);
my %MLK = %$MLK_ref;
my %BLK = %$BLK_ref;
my ($hide_controls,$hide_title);
if ($switches =~ /C/) {
$hide_controls = " hide";
}
if ($switches =~ /T/) {
$hide_title = " hide";
}
if ($script_url !~ /\?/) {
$script_url .= "?";
}
if ($vars{'guitar_frets'} < 9) {
$vars{'guitar_frets'} = 12;
}
if ($vars{'guitar_key'} < 1) {
$vars{'guitar_key'} = 0;
}
if (!$vars{'guitar_minmaj'}) {
$vars{'guitar_minmaj'} = "major";
}
if (!$vars{'guitar_scale'}) {
$vars{'guitar_scale'} = "all";
}
if (!$vars{'guitar_align'}) {
$vars{'guitar_align'} = "default";
}
my @tuning = (4,11,7,2,9,4);
my $fretwidth = substr((100/$vars{'guitar_frets'}),0,5);
my @notes = ONO::Lib::Audio::Pro::Music::Theory->notes;
my @notes_double = (@notes,@notes);
my $scalenotes = ONO::Lib::Audio::Pro::Music::Theory->scalenotes($vars{'guitar_key'},$vars{'guitar_minmaj'});
my @first_note_is_used;
my $TITLE = $MLK{ucfirst $vars{'guitar_minmaj'}};
my $TITLE = qq~$notes[$vars{'guitar_key'}] $TITLE: <span class="cola">$scalenotes</span>~;
my ($FRETS,$MAX_WIDTH,$KEYS,$SCALES,%chk,@usenote,@usenote_full);
if ($switches !~ /E/) {
if ($vars{'guitar_scale'} ne "empty") {
@usenote = ONO::Lib::Audio::Pro::Music::Theory->scale_major_rotate($vars{'guitar_key'},$vars{'guitar_scale'});
if ($vars{'guitar_minmaj'} eq "minor") {
@usenote = ONO::Lib::Audio::Pro::Music::Theory->scale_minor_rotate($vars{'guitar_key'},$vars{'guitar_scale'});
}
}
if ($vars{'guitar_scale'} =~ /^(pentatonic|blues|flamenco)$/) {
@usenote_full = ONO::Lib::Audio::Pro::Music::Theory->scale_major_rotate($vars{'guitar_key'},"all");
if ($vars{'guitar_minmaj'} eq "minor") {
@usenote_full = ONO::Lib::Audio::Pro::Music::Theory->scale_minor_rotate($vars{'guitar_key'},"all");
}
}
}
my $LINK = &link("",$script_url,$vars_ref,"guitar_frets");
foreach my $opt (9,12,15,21,24) {
my $sel;
if ($opt eq $vars{'guitar_frets'}) {
$sel = "_dark";
}
$FRETS .= qq~<a href="$LINK&guitar_frets=$opt" class="button$sel">$opt</a>~;
}
if ($vars{'guitar_frets'} < 13) {
$MAX_WIDTH = $vars{'guitar_frets'} * 80;
$MAX_WIDTH = ";max-width:${MAX_WIDTH}px";
}
$LINK = &link("",$script_url,$vars_ref,"guitar_minmaj");
if ($vars{'guitar_minmaj'} eq "minor") {
$chk{'minmaj_minor'} = "_yellow";
} else {
$chk{'minmaj_major'} = "_green";
}
$KEYS .= qq~<a href="$LINK&guitar_minmaj=major" class="button$chk{'minmaj_major'} button_left">$MLK{'major'}</a>
<a href="$LINK&guitar_minmaj=minor" class="button$chk{'minmaj_minor'} button_right mr10">$MLK{'minor'}</a>
~;
$LINK = &link("",$script_url,$vars_ref,"guitar_key");
my $count = 0;
$chk{"key_$vars{'guitar_key'}"} = "_dark";
foreach my $key (@notes) {
my $align = "middle";
if ($count == 0) {
$align = "left";
}
if ($count == 11) {
$align = "right";
}
$KEYS .= qq~<a href="$LINK&guitar_key=$count" class="button$chk{"key_$count"} button_$align mb10">$key</a>~;
$count++;
}
$LINK = &link("",$script_url,$vars_ref,"guitar_scale");
$chk{"scale_$vars{'guitar_scale'}"} = "_dark";
if ($vars{'guitar_scale'} eq "blues") {
$chk{"scale_$vars{'guitar_scale'}"} = "_blue";
}
if ($vars{'guitar_scale'} eq "flamenco") {
$chk{"scale_$vars{'guitar_scale'}"} = "_red";
}
$SCALES .= qq~<a href="$LINK&guitar_scale=all" class="button$chk{'scale_all'} mb10">$MLK{'all_notes'}</a>
<a href="$LINK&guitar_scale=chords" class="button button mb10">$MLK{'chords'}</a>
<a href="$LINK&guitar_scale=pentatonic" class="button$chk{'scale_pentatonic'} button_left mb10">$MLK{'pentatonic'}</a>
<a href="$LINK&guitar_scale=blues" class="button$chk{'scale_blues'} button_middle mb10">$MLK{'blues'}</a>
<a href="$LINK&guitar_scale=flamenco" class="button$chk{'scale_flamenco'} button_right mb10">$MLK{'flamenco'} (5+)</a>
<a href="$LINK&guitar_scale=empty" class="button$chk{'scale_empty'} mb10">$BLK{'empty'}</a>
~;
if ($vars{'guitar_scale'} eq "chords") {
my @chords = ONO::Lib::Audio::Pro::Music::Theory->chords($vars{'guitar_minmaj'});
my @chord_types = ONO::Lib::Audio::Pro::Music::Theory->chord_types();
$SCALES = qq~<a href="$LINK&guitar_scale=all" class="button$chk{'scale_all'} mb10">all notes</a>~;
my $counter;
for (my $c = 0; $c < 13; $c++) {
if ($chords[$c]) {
$counter++;
my $but = "middle";
if ($c == 0) {
$but = "left";
}
if ($c > 9) {
$but = "right";
}
my $TYPE = $chord_types[$chords[$c]];
my $sel;
if ($vars{'guitar_chord'} == $c) {
$sel = "_dark";
my $c0 = $c+$vars{'guitar_key'};
my $c1 = $c0+4;
my $c2 = $c0+7;
if ($TYPE eq "min") {
$c1 = $c0+3;
}
if ($TYPE eq "dim") {
$c1 = $c0+3;
$c2 = $c0+6;
}
if ($c1 > 11) {
$c1 = $c1-12;
}
if ($c2 > 11) {
$c2 = $c2-12;
}
$vars{"guitar_white_note_$c0"} = 1;
$vars{"guitar_white_note_$c1"} = 1;
$vars{"guitar_white_note_$c2"} = 1;
}
$TYPE =~ s~maj~Maj~;
$SCALES .= qq~<a href="$LINK&guitar_scale=chords&guitar_chord=$c"
class="button$sel button_$but mb10">$notes_double[$c+$vars{'guitar_key'}] $TYPE<span class="small col9"> ($counter)</span></a>
~;
}
}
$SCALES .= qq~<a href="$LINK" class="button button mb10">scales & more...</a>~;
}
$LINK = &link("",$script_url,$vars_ref,"guitar_align");
$chk{"align_$vars{'guitar_align'}"} = "_green";
# $SCALES .= qq~<a href="$script_url&guitar_align=default" class="button$chk{'align_default'} button_left mb10 ml10">default</a>
# <a href="$script_url&guitar_align=diagonal" class="button$chk{'align_diagonal'} button_right mb10">diagonal</a>
# ~;
my @opts;
if ($vars{'guitar_scale'} =~ /^(pentatonic|blues)$/) {
# @opts = (1,2,3,4,5);
}
if ($vars{'guitar_scale'} =~ /flamenco/) {
# @opts = (1,2,3,4,5,6,7);
}
if ($vars{'guitar_scale'} =~ /modes/) {
@opts = ('ionian','phrygian','mixolydian','locrian','dorian','lydian','arolian');
}
foreach my $opt (@opts) {
$SCALES .= qq~<a href="$script_url&guitar_key=" class="button button_middle mb10">$opt</a>~;
}
my $FRET = qq~<table class="wide_table mb10">
<tr>
<td class="p0">
<div class="fr$hide_controls">$FRETS</div>
<div class="xlarge bold$hide_title">$TITLE</div>
</td>
</tr>
</table>
<div class="p2" style="$MAX_WIDTH">
<table class="wide_table" style="background-image:url('/ono/osr/images/textures/var/wood/default.jpg');background-size:50%;">
~;
for (my $s = 0; $s < 6; $s++) {
my ($DOT,$TRANS);
if ($usenote[$tuning[$s]]) {
my $color = "highlight col3";
if ($vars{'guitar_scale'} eq "chords") {
$color = "dark";
}
if ($vars{"guitar_white_note_$tuning[$s]"}) {
$color = "white col3";
}
if ($vars{"guitar_yellow_note_$tuning[$s]"}) {
$color = "highlight col3";
}
if ($vars{'guitar_scale'} eq "chords" && $vars{"guitar_white_note_$tuning[$s]"}) {
$first_note_is_used[$s]++;
$color = "highlight col3";
}
$DOT = qq~<div class="abs bg_$color radius16 center" style="top:-4px;left:-5px;height:24px;width:24px;z-index:999;border:2px solid #333333">
<div class="rel" style="top:1px;left:0px">$notes[$tuning[$s]]</div>
</div>
~;
} else {
$TRANS = " trans30";
}
$FRET .= qq~ <tr>
<td class="pad5_2 center bold cole" style="background-color:#666666;background-image:url('/ono/osr/images/textures/tissue_black.jpg');">
<div class="rel$TRANS">$notes[$tuning[$s]]$DOT</div>
</td>
~;
my $dots_per_string;
for (my $f = 0; $f < $vars{'guitar_frets'}; $f++) {
my $thick1 = int(1+($s/1.2));
my $thick2 = 18-int($thick1/2);
my $DOT;
if (($s == 3 && $f =~ /^(2|4|6|8|14|16|18|20)$/) || ($f == 11 && ($s == 1 || $s == 5))) {
$DOT = qq~<div class="abs box_paper radius10 trans70" style="top:-11px;left:33%;width:0px"></div>~;
}
my $note = $tuning[$s]+$f+1;
my $keynote = $note-$vars{'guitar_key'};
my $whilecounter;
while ($note > 11 && $whilecounter < 1024) {
$whilecounter++;
$note = $note-12;
}
while ($keynote > 11 && $whilecounter < 1024) {
$whilecounter++;
$keynote = $keynote-12;
}
my $notename = $notes[$note];
$notename =~ s~#~<span class="xsmall">#</span>~;
if ($vars{"guitar_display_note_$notename"} || $vars{"guitar_display_note_$keynote"}) {
$usenote[$note] = 1;
}
if (($usenote[$note] || $usenote_full[$note]) && ($vars{'guitar_align'} eq "default" || $dots_per_string < 3)) {
my $color = "highlight";
if ($usenote[$note] == 0 || $vars{'guitar_scale'} eq "chords") {
$color = "dark colf trans70";
}
if ($usenote[$note] == 1) {
$dots_per_string++;
}
if ($usenote[$note] == 2) {
$color = "blue";
if ($vars{'guitar_scale'} eq "flamenco") {
$color = "darkred";
}
}
if ($vars{"guitar_white_note_$note"}) {
$color = "white";
}
if ($vars{"guitar_yellow_note_$note"}) {
$color = "highlight";
}
if ($vars{'guitar_scale'} eq "chords" && $vars{"guitar_white_note_$note"} && !$first_note_is_used[$s]) {
$first_note_is_used[$s]++;
$color = "highlight";
}
$DOT .= qq~<div class="abs bg_$color radius16 center" style="top:4px;left:30%;height:24px;width:24px;z-index:999;border:2px solid #333333">
<div class="rel" style="top:1px;left:0px">$notename</div>
</div>
~;
}
my $ADD;
if ($f == 11 || $f == 23) {
$ADD .= qq~;background-image:url('/ono/osr/images/textures/var/wood/dark.jpg');background-size:50%;~;
}
$FRET .= qq~ <td class="p0" style="width:${fretwidth}%;border-right:4px #999999 solid$ADD">
<div class="rel" style="padding:${thick2}px 0px ${thick2}px 0px">
<div style="width:110%;height:${thick1}px;background-color:#cccccc;background-image:url('/ono/osr/images/textures/fabric_grey_light.jpg');"></div>
$DOT
</div>
</td>
~;
}
$FRET .= qq~ </tr>~;
}
$FRET .= qq~ <tr style="background-color:#ffffff">
<td></td>~;
for (my $i = 1; $i < $vars{'guitar_frets'}+1; $i++) {
if (($i%2 != 0 || $i == 12) && $i > 2 && $i != 11 && $i != 13) {
$FRET .= qq~ <td class="center small bold col3">$i</td>~;
} else {
$FRET .= qq~ <td></td>~;
}
}
$FRET .= qq~ <td></td>
</tr>
</table>
</div>
<table class="wide_table mt10$hide_controls">
<tr>
<td class="p0">
$KEYS
</td>
</tr>
<tr>
<td class="p0">
$SCALES
</td>
</tr>
</table>
~;
return $FRET;
}
sub link {
#: Return link code.
my (
$self,
$LINK,
$vars_ref,
$exclude,
) = @_;
my %vars = %$vars_ref;
foreach my $key (keys %vars) {
if ($key =~ /^guitar_/ && $vars{$key} && $key ne $exclude) {
$LINK .= qq~&$key=$vars{$key}~;
}
}
return $LINK;
}
###############################################################################
# end of script
###############################################################################
1;
__END__