我刚刚玩什么?将吉他指法转换为和弦


22

相关:音乐:这和弦里有什么?弹奏笔记生成吉他弹片将数字对转换为吉他音符

给定吉他指法,输出它代表的和弦。您可以使用标准输入和输出,也可以编写返回字符串的函数。

输入的指法将分类为以下和弦之一,表示如下(如果根音为C):

  • 大三合会: C
  • 小三合会: Cm
  • (主要)第七名: C7
  • 小七: Cm7

和弦可能颠倒了,因此您不能依靠最低音为根。您也不能依靠这是现实世界中的简单指法。更一般而言,程序的输出必须忽略音高的八度,并将与同一音符(即A)相对应的所有音高均视为相等。

这是,因此以字节为单位的最短代码获胜。

输入格式

输入是一系列由6个值组成的值,这些值针对标准调音(EADGBE)中的6弦吉他的每个弦指示将在哪个弦上弹奏。这也可能表明该字符串根本没有播放。“零”品格也称为打开位置,品格号从那里开始递增计数。假设吉他有21个品格位置,因此最高品位是20号。

例如,输入X 3 2 0 1 0意味着将一根手指放在吉他脖子顶部的以下位置:

(6th) |---|---|---|---|---
      |-X-|---|---|---|---
      |---|---|---|---|---
      |---|-X-|---|---|---
      |---|---|-X-|---|---
(1st) |---|---|---|---|---

并弹奏第二至第六弦。它对应于以下ASCII标签

e |-0-|
B |-1-|
G |-0-|
D |-2-|
A |-3-|
E |---|

您可以灵活选择所需的输入类型:每个品格位置可以表示为字符串或数字。未弹奏的吉他弦通常用指示X,但-1如果您更方便(例如,使用数字),则可以选择其他前哨值。可以将6个品格位置序列输入为任何列表,数组或序列类型,单个以空格分隔的字符串或作为标准输入-再次选择。

您可以依赖与上述4种和弦类型之一对应的输入。

请在您的帖子中说明您的解决方案采用哪种输入形式。

输出格式

您必须将描述指法所用的和弦的字符串返回或打印到标准输出。此字符串由串联在一起的两个部分组成。资本化问题。允许尾随空格。

第一部分表示根音的一个AA#/ BbBCC#/ DbDD#/ EbEFF#/ GbGG#/ Ab。(我使用#的,而不是和,b而不是,以避免需要的Unicode。),其可以在没有尖锐的或平坦的表达必须没有它们被表达根音音符(从未输出B#FbDbb); 那些不能用单个尖锐或扁平符号表示的字符(即C#Db,但永不B##)。换句话说,您必须最大程度地减少便笺名称中的意外(竖琴或扁琴)数量。

第二部分指示和弦的类型,m对于大三重奏,小三重奏,7占优势的七分之一或m7次要的七分之一为空。因此,G G主音的输出简单为,而D♯次音的第七个输出为D#m7Ebm7。最后的测试案例中可以找到更多示例。

理论与提示

音符

色度标度每个八度有12个音高。当调到相同的气质时,这些音调中的每个音调均与其相邻音调1相等。相距12 个半音(八度)的音高被视为同一音符。这意味着我们可以把笔记像整数模12,从0到11七这些被赋予字母名称2从A到G这是不够的,命名所有的12个音调,但增加记号修补程序:添加♯(尖锐)到音符会使它高半个半音,而加上♭(平整)会使它低半个半音。

和弦

和弦是2个或更多音符一起演奏。和弦的类型取决于音符之间的关系,这可以由音符之间的距离来确定。如前所述,和弦具有根音。在这些示例中,我们将根音符视为0,但这是任意的,在此挑战中,最重要的是模运算中音符之间的距离。答案总是会有一个独特的和弦类型,即三和弦或第七和弦。根音不会始终是最低音高。选择根音,以便可以将和弦描述为以下四种和弦类型之一:

  • 一个主要的三合会是带有音符的和弦0 4 7
  • 一个小调三是与音符的共鸣0 3 7
  • 一个占主导地位(或主/次)七和弦有笔记0 4 7 10
  • 一个未成年人(或次/次),七和弦有笔记0 3 7 103

吉他调音

6弦吉他的标准调音从最低弦开始以E开始,然后以5、5、5、4的间隔敲击音符,然后以5个半音上升。将最低的E设为0,这意味着将吉他的所有琴弦都弹奏会给您编号的音高0 5 10 15 19 24,其模12等于0 5 10 3 7 0或音符E A D G B E

工作的例子

如果输入为0 2 2 0 0 0,则对应于音符E B E G B E,因此仅对应E,B和G。它们形成和弦Em,可以通过将它们的根编号为E看到它们,从而得到和弦0 3 7。(对于X 2 X 0 X 0或,结果将相同12 14 14 12 12 12。)

如果输入为4 4 6 4 6 4,则以C♯的根编号为7 0 7 10 4 70 4 7 10,则答案为C#7(或Db7)。如果不是4 4 6 4 5 4,则编号将为7 0 7 10 3 70 3 7 10,即C#m7(或Dbm7)。

测试用例

X 3 2 0 1 0  --->  C
0 2 2 0 0 0  --->  Em
X 2 X 0 X 0  --->  Em
4 4 6 4 6 4  --->  C#7  (or Db7)
4 4 6 4 5 4  --->  C#m7 (or Dbm7)
0 2 2 1 0 0  --->  E
0 0 2 2 2 0  --->  A
X X 4 3 2 2  --->  F#   (or Gb)
3 2 0 0 0 1  --->  G7
X X 0 2 1 1  --->  Dm7
3 3 5 5 5 3  --->  C
4 6 6 5 4 4  --->  G#   (or Ab)
2 2 4 4 4 5  --->  B7
0 7 5 5 5 5  --->  Am7
7 6 4 4 X X  --->  B
8 6 1 X 1 3  --->  Cm
8 8 10 10 9 8 -->  Fm
0 19 5 16 8 7 -->  Em
6 20 0 3 11 6 -->  A#   (or Bb)
X 14 9 1 16 X -->  G#m  (or Abm)
12 14 14 12 12 12 --> Em
15 14 12 12 12 15 --> G
20 X 20 20 20 20  --> Cm7
X 13 18 10 11 10  --> A#7 (or Bb7)

1以其频率的对数

2或以solfège命名,如do,re,mi。在此挑战中,使用字母名称。

3这也可以称为大六和弦,带有不同的根音选择。在此挑战中,以它的次要第七名称命名。


3
巨大的挑战!
路易斯·门多

1
试图关闭作为我未来挑战的骗局:D(我脑子里也面临着非常相似的挑战,但您的速度明显更快。)
漏洞更大的

输出字符串中是否允许尾随空格?
路易斯·门多

@LuisMendo肯定; 没关系。
Dan Getz

1
@officialaimm不,您不需要处理任何其他情况。您可以假设它将始终是这4种和弦类型之一。换句话说,如果代码得到不同的和弦,则您的代码可以执行任何您想做的事情(包括错误或给出错误的答案)。
丹·盖茨

Answers:


9

MATL115个 114字节

[OAXICO]+tZN~)Y@!"@t1)XH- 12\XzXJK7hm?O.]JI7hm?'m'.]J[KCX]m?'7'.]J[ICX]m?'m7'.]]'FF#GG#AA#BCC#DD#E'l2741B~QY{HX)wh

输入格式为[N 3 2 0 1 0],其中N表示未使用的字符串。

输出字符串始终使用#,而不是b

在线尝试!或分两部分验证所有测试用例,以避免在线编译器超时:

说明

[OAXICO]            % Push [0 5 10 3 7 0]. This represents the pitch of each open
                    % string relative to the lowest string, modulo 12
+                   % Add to implicit input. May contain NaN's, for unused strings
tZN~)               % Remove NaN's
Y@!                 % Matrix of all permutations, each in a column
"                   % For each column
  @                 %   Push current column
  t1)               %   Duplicate and get first entry
  XH                %   Copy into clipboard H
  - 12\             %   Subtract. This amounts to considering that the first note
                    %   of the current permutation is the root, and computing
                    %   all intervals with respect to that
  12\               %   Modulo 12
  Xz                %   Remove zeros
  XJ                %   Copy into clipboard J
  K7hm?             %   Are all intervals 4 or 7? If so: it's a major chord
    O               %     Push 0 (will become space when converted to char)
    .               %     Break for loop
  ]                 %   End if
  J                 %   Push array of nonzero intervals again
  I7hm?             %   Are all intervals 3 or 7? If so: it's a minor chord
    'm'             %     Push this string
    .               %     Break for loop
  ]                 %   End if
  J                 %   Push array of nonzero intervals again
  [KCX]m?           %   Are all intervals 4, 7 or 10? If so: it's a dominant-7th
                    %   chord
    '7'             %     Push this string
    .               %     Break for loop
  ]                 %   End if
  J                 %   Push array of nonzero intervals again
  [ICX]m?           %   Are all intervals 3, 7 or 10? If so: it's a minor 7th chord
    'm7'            %     Push this string
    .               %     Break for loop
  ]                 %   End if
]                   % End for. The loop is always exited via one of the 'break'
                    % statements. When that happens, the stack contains 0, 'm',
                    % '7' or 'm7', indicating the type of chord; and clipboard H
                    % contains a number that tells the root note using the lowest 
                    % string as base (1 is F, 2 is F# etc)
'FF#GG#AA#BCC#DD#E' % Push this string. Will be split into strings of length 1 or 2
l                   % Push 1
2741B~Q             % Push [1 2 1 2 1 2 1 1 2 1 2 1] (obtained as 2741 in binary,
                    % negated, plus 1)
Y{                  % Split string using those lengths. Gives a cell array of
                    % strings: {'F', 'F#', ..., 'E'}
H                   % Push the identified root note
X)                  % Index into cell array of strings
wh                  % Swap and concatenate. Implicitly display

4

MS-DOS .COM文件(179字节)

文件(此处显示为十六进制):

fc be 81 00 bf 72 01 31 db b9 06 00 51 e8 73 00
59 e2 f9 b9 0c 00 be 48 01 ad 39 c3 74 0d 40 75
f8 d1 fb 73 03 80 c7 08 e2 ec c3 31 db 88 cb 8a
87 59 01 e8 42 00 8a 87 65 01 e8 3b 00 81 c6 08
00 ac e8 33 00 ac eb 30 91 00 89 00 91 04 89 04
ff ff 00 00 6d 00 37 00 6d 37 42 41 41 47 47 46
46 45 44 44 43 43 00 23 00 23 00 23 00 00 23 00
23 00 04 09 02 07 0b 04 84 c0 74 06 b4 02 88 c2
cd 21 c3 8a 0d 47 ac 3c 20 76 fb 30 ed 3c 41 73
22 2c 30 72 0b 86 c5 b4 0a f6 e4 00 c5 ac eb ed
88 e8 00 c8 30 e4 b1 0c f6 f1 88 e1 b8 01 00 d3
e0 09 c3

输入是通过命令行给出的。无效的输入将导致无效的程序行为!

汇编代码如下所示:

.text
.code16
ComFileStart:
    cld
    mov $0x81, %si
    mov $(TuneTable-ComFileStart+0x100), %di
    xor %bx, %bx
    # 6 strings: Build the mask of played tones
    mov $6, %cx
NextStringRead:
    push %cx
    call InsertIntoMask
    pop %cx
    loop NextStringRead

    # Check all base tones...
    mov $12, %cx
TestNextTone:
    mov $0x100+ChordTable-ComFileStart, %si
TestNextChord:
    lodsw
    # Is it the chord we are searching for?
    cmp %ax, %bx
    je FoundChord 
    # Is it the end of the table?
    inc %ax
    jnz TestNextChord
    # Transpose the chord we really play
    # and go to the next tone
    # This code rotates the low 12 bits of
    # BX one bit right
    sar $1, %bx
    jnc NoToneRotated
    add $8, %bh
NoToneRotated:
    loop TestNextTone
EndOfProgram:
    ret

FoundChord:
    # Get and print the tone name
    xor %bx, %bx
    mov %cl, %bl
    mov (ToneNamesTable+0x100-1-ComFileStart)(%bx),%al
    call printChar
    mov (ToneNamesTable+0x100+12-1-ComFileStart)(%bx),%al
    call printChar
    # Get the chord name suffix and print it
    add $(ChordNamesTable-ChordTable-2),%si
    lodsb
    call printChar
    lodsb
    # Note: Under MS-DOS 0x0000 is the first word on
    # the stack so the "RET" of printChar will jump
    # to address 0x0000 which contains an "INT $0x21"
    # (end of program) instruction
    jmp printChar

ChordTable:
    # Major, Minor, Major-7, Minor-7
    .word 0x91, 0x89, 0x491, 0x489, 0xFFFF
ChordNamesTable:
    .byte 0,0,'m',0,'7',0,'m','7'
ToneNamesTable:
    .ascii "BAAGGFFEDDCC"
    .byte 0,'#',0,'#',0,'#',0,0,'#',0,'#',0
TuneTable:
    .byte 4,9,2,7,11,4

#
# Subfunction: Print character AL;
#              Do nothing if AL=0
#
printChar:
    test %al, %al
    jz noPrint
    mov $2, %ah
    mov %al, %dl
    int $0x21
noPrint:
    ret

#
# Subfunction: Get one finger position
#              and insert it into a bit mask
#              of tones being played
#
# Input:
#
#   [DS:DI] = 
#        Tuning of current string (0=C, 1=C#, ..., 11=B)
#        Actually only 2=D, 4=E, 7=G, 9=A and 11=B are used
#
#   DS:SI = Next character to read
#
#   DF = Clear
#
# Input and Output:
#
#    BX = Bit mask
#    DI = Will be incremented
#
# Destroys nearly all registers but SI and BX
#
InsertIntoMask:
    mov (%di), %cl
    inc %di
SkipSpaces:
    lodsb
    cmp $' ', %al
    jbe SkipSpaces
# Now evaluate each digit
    xor %ch, %ch
GetNextDigit:
    # Number = 10*Number+Digit
    cmp $'A', %al
    jae DigitIsX
    sub $'0', %al
    jb DigitsDone
    xchg %al, %ch
    mov $10, %ah
    mul %ah
    add %al, %ch
    lodsb
    jmp GetNextDigit
DigitsDone:
    # Add the tune of the string
    # and perform modulus 12
    mov %ch, %al
    add %cl, %al
    xor %ah, %ah
    mov $12, %cl
    div %cl
    mov %ah, %cl
    mov $1, %ax
    shl %cl, %ax
    or %ax, %bx
DigitIsX:
    ret

测试用例:

6 20 0 3 11 6 -->  A#   (or Bb)

我已经看到两个钢琴演奏者一起演奏“四手”钢琴。

这个测试案例是我第一次阅读有关吉他演奏者的信息!

即使右手敲打,您也无法播放这样的电源线!


嗯,也许乌贼可以弹奏和弦?我认为这是我通过随机搜索发现的一种,因此可能会有一些“硬”测试用例。
Dan Getz

3

Ruby,129个字节

与以前的版本一样,但是使用单个循环,带有三元运算符,可以在解析步骤和输出步骤之间进行排序。要进行此工作,还需要进行一些其他小的修改。

->a{r=0
18.times{|j|j<6?a[j]&&r|=8194<<(6--~j%5+a[j]*7)%12:(r/=2)&11==3&&puts("CGDAEBF"[j%7]+?#*(j/13)+['',?m,?7,'m7'][r>>9&3])}}

Ruby,136个字节

Llamda函数接受6个数字组成的数组作为参数,并输出到stdout。未使用的字符串由伪造的值表示(ruby中唯一的伪造的值是nilfalse。)

->a{r=0
6.times{|j|a[j]&&r|=4097<<(6--~j%5+a[j]*7)%12}
12.times{|j|r&11==3&&puts("FCGDAEB"[j%7]+?#*(j/7)+['',?m,?7,'m7'][r>>9&3]);r/=2}}

说明

我使用基于五分之一圆的12个音高的表示形式。这意味着每个音高后跟的音高高7个半音(或低5个半音),给出音序F C G D A E B F# C# G# D# A#。这有两个优点。一是所有的利器一起出现。另一个是5弦贝司的开弦音符同时出现:GDAEB(吉他是相关的,但稍微复杂一些,请参见下文)。

第一个循环运行6次。表达式6--~j%5(等效于6-(j+1)%5)给出了打开字符串的注释值:E=5 A=4 D=3 G=2 B=6 E=5。为此,我们将品格号乘以7(如上图所示,添加一个半音会使我们在音序中向前移动7位。)然后我们将整个内容取模为12,并对存在的音符进行位图绘制(用于4097<<note value给出2个连续的八度。)

组成位图后,我们准备搜索和弦并输出它。

我们对以下注意事项感兴趣:

Note       position in      position in             Note      position in 
           semitone domain  circle of fifths                  circle of fifths 
Root       0                0                       Root      0
Minor 3rd  3                9                       Fifth     1
Major 3rd  4                4                       Sixth     3
Fifth      7                1                       Major 3rd 4
Sixth      9                3                       Minor 3rd 9
Minor 7th  10               10                      Minor 7th 10

从检查和弦F开始,我们测试是否存在根和第五根和弦:位0和1(从最低有效位开始:1和2的位开始计数)。要拒绝第六和弦,我们还需要检查第六和弦。缺少:3号位(8位)。因此我们检查一下r&&11==3,如果打印出此和弦。

我们忽略大三分音,而是完全依靠第9位(小三分)和第10位(小七分)来计算和弦类型。该表达式r>>9&3用于从数组中选择正确的和弦类型。

在循环的最后,我们将位图右移一位r/=2以依次测试可能的和弦根F C G D A E B F# C# G# D# A#

取消测试程序

f=->a{                            #Accept array of 6 numbers as argument.
  r=0                             #Setup an empty bitmap.

  6.times{|j|                     #For each string
    a[j]&&                        #if the fret value is truthy (not nil or false)
    r|=4097<<(6--~j%5+a[j]*7)%12  #calculate the note value in the circle of fifths and add to the bitmap.
  }

  12.times{|j|                    #For each possible root note
    r&11==3&&                     #if root and fifth are present (bits 0 and 1) and sixth is absent (bit 3) 
    puts("FCGDAEB"[j%7]+?#*(j/7)+ #output the note name and a sharp symbol if necessary, followed by
    ['',?m,?7,'m7'][r>>9&3])      #m and/or 7 as indicate by bits 9 and 10.
    r/=2
  }
}

print 1;f[[nil,3,2,0,1,0]]       #  C
print 2;f[[0,2,2,0,0,0]]         #  Em
print 3;f[[nil,2,nil,0,nil,0]]   #  Em
print 4;f[[4,4,6,4,6,4]]         #  C#7 
print 5;f[[4,4,6,4,5,4]]         #  C#m7 
print 6;f[[0,2,2,1,0,0]]         #  E
print 7;f[[0,0,2,2,2,0]]         #  A
print 8;f[[nil,nil,4,3,2,2]]     #  F#  
print 9;f[[3,2,0,0,0,1]]         #  G7
print 10;f[[nil,nil,0,2,1,1]]    #  Dm7
print 11;f[[3,3,5,5,5,3]]        #  C
print 12;f[[4,6,6,5,4,4]]        #  G#  
print 13;f[[2,2,4,4,4,5]]        #  B7
print 14;f[[0,7,5,5,5,5]]        #  Am7
print 15;f[[7,6,4,4,nil,nil]]    #  B
print 16;f[[8,6,1,nil,1,3]]      #  Cm
print 17;f[[8,8,10,10,9,8]]      #  Fm
print 18;f[[0,19,5,16,8,7]]      #  Em
print 19;f[[6,20,0,3,11,6]]      #  A#  
print 20;f[[nil,14,9,1,16,nil]]  #  G#m 
print 21;f[[12,14,14,12,12,12]]  #  Em
print 22;f[[15,14,12,12,12,15]]  #  G
print 23;f[[20,nil,20,20,20,20]] #  Cm7
print 24;f[[nil,13,18,10,11,10]] #  A#7

2

Javascript(ES6), 335个 333字节

喜欢这个挑战和PPCG SE!这是我的第一场高尔夫-欢迎提出建议,因为我敢肯定它会有所改善。(因为我在计数中包括了f =,所以剔除了两个字节)

函数f采用字符串数组(表示数字和'X'),例如,f(['X','3','2','0','1','0'])并返回和弦(自然或尖锐),例如E#m7。为了清楚起见添加了换行符(不包括在字节数中)

f=c=>[s=new Map([[435,''],[345,'m'],[4332,7],[3432,'m7']]),
n=[...new Set(c.map((e,i)=>e?(+e+[0,5,10,3,7,0][i])%12:-1)
.filter(e=>++e).sort((a,b)=>a>b))],d=[...n,n[0]+12].reduce(
(a,c,i)=>i?[...a,(c-n[i-1]+12)%12]:[],0).join``.repeat(2),
m=+d.match(/(34|43)(5|32)/g)[0],'E0F0F#0G0G#0A0A#0B0C0C#0D0D#'
.split(0)[n[d.indexOf(m)]]+s.get(m)][4]

用法示例:

console.log(f(['0','2','2','0','0','0'])); // Em

运行测试用例:

tests=`X 3 2 0 1 0 ---> C
0 2 2 0 0 0 ---> Em
X 2 X 0 X 0 ---> Em
4 4 6 4 6 4 ---> C#7 (or Db7)
4 4 6 4 5 4 ---> C#m7 (or Dbm7)`; // and so on...

tests.split`\n`.forEach(e=>{
    console.log(`Test: ${e}
      Result: ${f(e.split(' ').slice(0,6))}`)
})

非高尔夫版本,并附有说明:

f = (c) => {
    s = new Map([
        [435,''], [345,'m'], [4332,7], [3432,'m7'] 
    ]) /* Each key in s describes the intervals (semitones)
          between consecutive notes in a chord, when it is
          reduced to a single octave, including the interval
          from highest back to lowest. The values describe
          the corresponding chord suffix. E.g. C-E-G has
          intervals C-4-E-3-G-5-C. 435=major=no suffix. */

    n = [ ...new Set(
        c.map( 
         (e,i) => e ? ( +e + [0,5,10,3,7,0][i] )%12 : -1 
         ).filter( (e) => ++e ).sort( (a,b) => a>b )
        ) ] /* take the input array, c, and transform each fret
               position into a note. remove non-notes (-1), sort
               in tone order, remove duplicates. An input of
               positions X 13 18 10 11 10 becomes notes
               (-1) 6 4 1 6 10 then 1 4 6 10. */

    d = [ ...n, n[0] + 12 ].reduce(
        (a,c,i) => i ? [ ...a, (c - n[i-1] + 12)%12 ] : [], 0
    ).join``.repeat(2)
    /* convert the note array, n, into an interval string, d,
       including the lowest note repeated above it to capture
       all intervals. Repeat it twice so that, regardless of the
       inversion played, the intervals will appear in root order
       somewhere. E.g. notes 1-4-6-10 and 13 (1+12)
       become intervals 3 2 4 3, and string for searching
       32433243 */

    m = +d.match( /(34|43)(5|32)/g )[0];
      /* m is the matched chord pattern. In this case, 4332. */

    return 'E0F0F#0G0G#0A0A#0B0C0C#0D0D#'.split(0)[
    n[ d.indexOf(m) ]
    /* get the position in the interval string where the root
       interval first occurs. this corresponds to the position
       of the chord root note in the note array, n. convert this
       number 0-12 to a note name E - D# */
    ] + s.get(m)
       /* add the suffix corresponding to the matched
       chord interval pattern */
}

1
欢迎光临本站!很高兴听到您喜欢它。:)不幸的是,我不知道任何JS,所以我没有任何提示,但是您可以在这里
DJMcMayhem
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.