Saiten stimmen

9

Aufgabe

Schreiben Sie ein Programm, um die Note einer Saite zu bestimmen, die auf eine bestimmte Frequenz gestimmt und an einem bestimmten Punkt gedrückt wurde.

Nehmen Sie der Einfachheit halber an, dass die Frequenz des erzeugten Klangs und die Länge der Saite rechts von der Stelle, an der sie gedrückt wird, umgekehrt proportional sind.

Hinweis: Diese Aufgabe befasst sich ausschließlich mit dem Grundton und nicht mit Obertönen / anderen Harmonischen.

Eingang

Ihr Programm erhält zwei Daten:

  • Eine Zeichenfolge beliebiger Länge, die die betreffende Zeichenfolge darstellt. Diese Zeichenfolge wird mit einem X markiert, an dem die Zeichenfolge gedrückt werden soll.

    [-----] is a string divided in six sections (five divisions).
    [--X--] is a string pressed at the exact center of the string.
    [X----] is a string pressed at 1/6 the length of the string. (Length used is 5/6)
    [-X--] is a string pressed at 2/5 of the length of the string. (Length used is 3/5)
    

    Angenommen, die Note ertönt mit dem Teil der Saite rechts von X.

  • Eine Zahl (nicht unbedingt eine Ganzzahl), die die Frequenz angibt, mit der die Zeichenfolge gestimmt wird. Die Genauigkeit dieser Zahl beträgt höchstens vier Nachkommastellen.

Es kann angenommen werden, dass die durchgelassenen Frequenzen zwischen 10 Hzund liegen 40000 Hz.

Die Eingabe kann in einem Format Ihrer Wahl übergeben werden. Bitte geben Sie in Ihrer Antwort an, wie Eingaben in Ihr Programm akzeptiert werden.

Ausgabe

Ihr Programm muss sowohl die nächste Note * im Zwölfton-Stimmsystem für gleiches Temperament als auch die Anzahl der Cent von der nächsten Note entfernt ausgeben, die der durch die Saite angegebene Klang sein würde (auf den nächsten Cent gerundet).

+nCent sollte verwendet werden, um nCent scharf / über der Note und -nCent für flach / unter der Note zu bezeichnen.

Die Note sollte in wissenschaftlicher Tonhöhennotation ausgegeben werden. Angenommen, A4 ist auf abgestimmt 440Hz. Verwenden Sie b und # für flache / scharfe Noten. Hinweis: Es kann entweder scharf oder flach verwendet werden. Für die Note bei 466.16Hzkann entweder A#oder Bbfür die Note ausgegeben werden.

Das Format der Ausgabe liegt bei Ihnen, solange die Ausgabe nur die beiden zuvor angegebenen Informationen enthält (dh das Drucken jeder einzelnen möglichen Ausgabe ist nicht zulässig).

* Die nächste Note bezieht sich auf die Note, die dem durch die Eingabe angegebenen Ton am nächsten kommt, gemessen in Cent (daher die Note, die sich innerhalb 50 centsdes Tons befindet). Wenn der Ton 50 centsvon zwei verschiedenen Noten entfernt ist (nach dem Runden), kann eine der beiden Noten ausgegeben werden.

Beispiele

Ihr Programm sollte für alle Fälle funktionieren, nicht nur für die folgenden Beispiele.

Output             Input Frequency   Input String
A4,  +0  cents     220               [-----X-----]
A5,  +0  cents     220               [--------X--]
D5,  -2  cents     440               [--X--------]
B4,  -49 cents     440               [X----------]
A#4, +19 cents*    314.1592          [X-]
Eb9, +8  cents*    400               [-----------------------X]
Eb11,+8  cents*    100               [--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------X]
D#1, +49 cents*    10                [--X]
A0,  -11 cents     11.7103           [---X--]

* Es könnte entweder scharf oder flach ausgegeben worden sein.

Potenziell hilfreiche Links

Dies ist so dass die kürzeste Antwort gewinnt.

es1024
quelle
Ich denke, Ihre Beispiele sind etwas inkonsistent: Gemäß dem ersten wird [--X--]die Zeichenfolge in der Mitte der Abteilung gedrückt, in der die xplatziert ist, während die letzte [-X--]bei 3/8 (nicht 2/5) liegen würde, wenn Sie dieser Logik folgen. Oder verstehe ich etwas falsch?
Fehler
@flawr für die letzte [-X--], die Zeichenfolge wird an 4 Stellen (und daher in 5 Teile) geteilt und an der zweiten dieser Unterteilungen gedrückt. Somit wird auf gedrückt 2/5und die verwendete Länge ist 3/5.
Es1024
Ah ok, jetzt verstehe ich, also -repräsentiert jeder im Grunde die Position der Abteilungen, danke für die Erklärung!
Fehler

Antworten:

1

BBC Basic, 161 #

  REM get frequency and string. Store length of string in n for later.
  INPUT f,s$
  n=LEN(s$)

  REM store floating-point value of note in semitones above (C0-0.5). Store integer value in n% (offset effectively means it will be rounded not truncated.)
  n=LN(f*(n-1)/(n-INSTR(s$,"X"))/15.8861)/LN(2)*12
  n%=n

  REM format printing to whole numbers only
  @%=2^17

  REM Output note name and octave. Output cents (discrepancy between n and n%, minus the offset of 0.5)
  PRINT MID$("C C#D D#E F F#G G#A A#B ",n%MOD12*2+1,2);INT(n/12)'(n-0.5-n%)*100'

Punktzahl schließt Kommentare aus. Noch nicht Golf gespielt.

Ausgabe

Funktioniert in allen Testfällen mit Ausnahme der beiden langen korrekt. Denn Eb9es scheint, dass im Testfall ein Strich fehlt: Es gibt 22 -und einen X, der die Saite in 24 gleiche Teile teilt. Nach meinen manuellen Berechnungen sind dies 9600 Hz, was 37 Cent über einem D9 liegt. Genau das gibt mein Programm aus. Wenn ich einen weiteren Strich hinzufüge, bekomme ich Eb9 + 8 Cent. Leider kann BBC Basic keine Zeichenfolgen mit mehr als 255 Zeichen verarbeiten, daher Eb11gibt der Fall einen Fehler aus.

Geben Sie hier die Bildbeschreibung ein

Level River St.
quelle
3

C, 179

main(n,d){float f;scanf("%*[^X]%nX%*[-]%n]%f",&n,&d,&f);f=log(f*d/(d-n))*17.3123-57.376;n=d=f+.5;n=n%12*7+784;printf("%c%d%c,%+2.0f cents\n",n/12,(d+9)/12,n%12/7*3+32,(f-d)*100);}

Empfängt das ASCII-Bild in einer Zeile für sich und die Frequenz in einer separaten Zeile.

Einige Zeichen können entfernt werden, indem die Genauigkeit der magischen Zahlen 17.3123und verringert wird 57.376.

Ohne das Golfen sieht das Programm folgendermaßen aus:

main(n,d)
{
    float f; // 'float' and '%f' better than 'double' and '%lf'

    scanf("%*[^X]%nX%*[-]%n]%f", &n, &d, &f);
    // n is the number of chars before 'X'
    // d is the number of chars before ']'
    // f is the frequency

    // Calculate the tuned frequency
    f = f * d / (d - n);

    // Convert the frequency to logarithmic scale, relative to pitch A0
    f=log(f)*17.3123-57.376;
    // alternatively: f = log2(f / (440 / 16)) * 12;

    // Round to nearest integer
    n=d=f+.5;

    // Calculate the note name ('A', 'B', etc), multipled by 12 for convenience
    n=n%12*7+784;

    printf("%c%d%c,%+2.0f cents\n", // output example: B4 ,-49 cents
        n/12,        // note name
        (d+9)/12,    // octave number
        n%12/7*3+32, // if n%12 is large enough, it's '#' else ' ' (natural)
        (f-d)*100);  // number of cents; stdio rounds it to integer
}
Anatolyg
quelle
2
+1 für die fantastische scanfFormatzeichenfolge. Ich hatte keine Ahnung, dass du das tun könntest. Ich werde Ihren Ausgabecode später überprüfen (ich habe darüber nachgedacht, dies in C zu tun, und obwohl mir bei der Ausgabe etwas Ähnliches eingefallen ist, konnte ich keine Möglichkeit finden, das Ganze wettbewerbsfähig zu machen.) Ich gehe davon aus, d+9dass Sie indiziert sind Note A, also müssen Sie die Oktavzahl an den Index in Note C anpassen: Ich frage mich, ob es einen Weg gibt, das zu umgehen.
Level River St
Ja, die +9 kompensiert die Tatsache, dass Oktaven bei C beginnen. Entweder das oder eine ähnliche Korrektur für die Berechnung des Notennamens. Für Notennamen könnte die Kreisverschiebung durch eine LUT implementiert werden, aber ich mag die "mathematischere" Art, sie zu berechnen.
Anatolyg
1

JavaScript (199)

Nennen Sie es zB als t('[X-]',314.1592)

t=(s,f)=>{l=s.length-1;p='C C# D D# E F F# G G# A B H'.split(' ');n=12*Math.log2(f*l/(l-s.indexOf('X'))/16.3515978);m=n+.5|0;return p[m%12]+(n/12|0)+' '+((n-m)*100+.5|0)}

Fest. (Da ich in Europa lebe, habe ich B anstelle von Bb und H anstelle von B = verwendet.)

fehlerhaft
quelle
Flawr, bist du Deutscher? Ich habe B und H immer als deutsche Notation gesehen, nicht als europäische Notation. Großbritannien und Irland verwenden Bb und B. Spanien und Italien verwenden SIb und SI (wie in DO RE MI FA SOL LA SI.). Auf jeden Fall ist es nur eine Rettung eines Charakters.
Level River St
Ja, ich bin ein deutschsprachiges Land. Mir war nicht bewusst, dass andere europäische Länder dieses Doremi-System verwenden (ich habe nur gehört, dass Menschen es in der Kindererziehung verwenden). Wie auch immer, es war in erster Linie ein Witz, da es, wie Sie sagten, nur 1 Zeichen spart und die Anforderungen nicht wirklich erfüllt =)
Fehler
Dies scheint die Anzahl der Cent falsch zu runden, wenn die Anzahl der Cent negativ ist (zum Beispiel t('[---X--]',11.7103)(letztes Beispiel) -10anstelle von-11
es1024
Mit p="C0C#0D0D#0E0F0F#0G0G#0A0B0H".split(0)spart Sie zusätzlich 2 Zeichen.
Sean Latham
@ es1024 Oh, ich hätte es wissen müssen: Weil ich die Rundungsfunktion implementiert habe, mit round(x) = x+.5|0der nur positive Zahlen korrekt sind, werde ich das später beheben. @ipi danke!
Fehler
1

Python 3: 175

import math
def t(b,s):l=len(s)-1;n=12*math.log2(b*l/(l-s.index("X"))/16.35);m=round(n);return"%s%s%+d"%(("C C# D D# E F F# G G# A A# B".split()*99)[m],m//12,round(100*(n-m)))

Ungolfed:

import math

c0 = 16.35

def tuning (base_frequency, string):
    return formatted (note_number (frequency (base_frequency, string)))

def formatted (note_number):
    return "{name}{octave:d}{cents:+d}".format (name=note_name (note_number),
                             octave=octave (note_number),
                             cents=cents_out (note_number))

def note_name (note_number):
    return ("C C# D D# E F F# G G# A A# B".split() * 99)[round (note_number)]

def note_number (frequency):
    return 12 * math.log2 (frequency / c0)

def octave (note_number):
    return round (note_number) // 12

def cents_out (note_number):
    return round (100 * (note_number - round (note_number)))

def frequency (base_frequency, string):
    string_length = len (string) - 1
    held_length = string_length - string.index ("X")
    return base_frequency * string_length / held_length

if "__main__" == __name__:

    print ("Testing functions against known values...")
    assert "A4+0"     == tuning (220,      "[-----X-----]")
    assert "A5+0"     == tuning (220,      "[--------X--]")
    assert "D5-2"     == tuning (440,      "[--X--------]")
    assert "B4-49"    == tuning (440,      "[X----------]")
    assert "A#4+19"   == tuning (314.1592, "[X-]")
    assert "D#9+8"    == tuning (400,      "[-----------------------X]")
    assert "D#11+8"   == tuning (100,      "[--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------X]")
    assert "D#1+49"   == tuning (10,       "[--X]")
    assert "A0-11"    == tuning (11.7103,  "[---X--]")
    print ("Tests passed.")
kompetent
quelle