Spiele einen Song für mich


23

Herausforderung

Bei gegebener Gitarrentabulatur müssen Sie das durch die Registerkarte dargestellte Lied ausgeben. Dies kann an den Lautsprechern Ihres Computers oder an einer Audiodatei (.wav, .mp3, .midi, .aiff usw.) liegen. Es wird auch einen zweiten Eingang für das Timing geben.

Die Registerkarten können über eine Datei oder direkt in STDIN eingegeben werden. Die Registerkarte wird in ASCII-Form sein .

Spec

Alle Registerkarten sind für 6 Gitarren mit sechs Saiten mit Standard-E-Stimmung: E2 (82,41 Hz), A2 (110,00 Hz), D3 (146,83 Hz), G3 (196,00 Hz), B3 (246,94 Hz), E4 (329,63 Hz).

Die einzigen Techniken (neben dem normalen Pflücken), für die Sie sorgen müssen, sind:

  • Biegen (dies wird immer eine Halbtonbiegung sein)
  • Hämmern auf
  • Abziehen
  • Hoch / runter gleiten

Da Sie den Klang einer gedämpften Saite nicht synthetisieren können, behandeln Sie sie xals- .

Geben Sie beim Biegen den vollständigen Übergang von ungebogen zu Saite zu gebogen zu ungebogen wieder aus.

Die zweite Eingabe ist die Zeit, die jedes Symbol auf der Registerkarte in Sekunden darstellt. Beispielsweise:

Für die Eingabe:

e|---
B|---
G|---
D|---
A|---
E|---

Bei Timing 0.5ist 3die ausgegebene Audiodatei ( 3*0.5=1.5) , da es Spalten mit Symbolen (aber keine Noten) gibt.1.5 Sekunden lang still .

Beispiel-Registerkarten

1 - Das Gewicht (Jack White, Jimmy Page + The Edge Edition)

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

2 - Riecht nach Teen Spirit

e|--------------|---------------|-------------|-------------|
B|--------------|---------------|-------------|-------------|
G|-----8h10-----|-8-8b----6--5--|-6--5--------|-------------|
D|-10--------6--|---------------|-------8-6-8-|-8b----6--5--|
A|--------------|---------------|-------------|-------------|
E|--------------|---------------|-------------|-------------|

3 - Sterne Spangled Banner

e|---0-------2-5---9-7-5-----------9-7-5-4-2-4-5------|
B|-----2---2-------------2-4-5---5---------------5-2--|
G|-------2-------------------------------------------2|
D|----------------------------------------------------|
A|----------------------------------------------------|
E|----------------------------------------------------|

3
Ich habe Ihren Frequenzen ein paar Nachkommastellen hinzugefügt. Vorausgesetzt, ein Halbton = 1 Bund entspricht einem Verhältnis von 1,059463: 1 (dh einer Differenz von etwa 6%), ist die Abstimmung auf 1 Hz nicht präzise genug, um einen guten abgestimmten Klang zu erzielen. Da es sich natürlich um einen Beliebtheitswettbewerb handelt, ist eine schlechte Abstimmung zwar zulässig, aber sie wird nicht gewinnen.
Level River St

Sehr kreativer Wettbewerb! Nachdem ich mir den Link zur ASCII-Form angesehen hatte, konnte ich Beispiel 2 verstehen (seit ich den Song gehört habe), aber da ich keine Gitarre kenne, denke ich, dass die Herausforderung eine hohe Lernkurve hat. Ich habe auch wenig Erfahrung mit Audio-Manipulationen außer der grundlegenden Verwendung von Audacity.
mbomb007

Zählt MIDI als Audiodatei?
Orlp

@orlp Ja, das tut es
Beta Decay

1
Gut für zukünftige Referenz: v * (2 ^ (f / 12)) = x; v = Häufigkeit der Saite; f = Bund (die Nummer auf der Registerkarte); x = gespielte Frequenz; Tabulatoren geben auch nicht die Länge einer Notiz an. Ihr Programm muss schlau sein.
Grant Davis

Antworten:


7

MATLAB

Das ist irgendwie unvollendet. Ich habe eine schnelle und schmutzige Methode angewendet, um das Audio so einfach wie möglich zu machen. Die Methode, die ich benutzte, machte es schwierig, das Biegen / Hämmern durchzuführen (ich hatte diese Wörter in diesem Zusammenhang auch noch nie zuvor gehört).

Abgesehen davon liest dieses Skript eine Textdatei mit dem Namen "inputs.txt" ein, die die ASCII-Registerkarte enthält, und spielt das Lied ab.

%zeitliche Koordinierung
t = 0,25; % natürlich könnte diese Zeile 't = input (' timing: ') sein;
        %, wenn Sie einen Wonky-Wert so festlegen, dass t * 8192 keine ganze Zahl ist
        % stuff wird fehlschlagen
% Frequenzen und zusätzliche Variablen, um später etwas Faulheit zu ermöglichen
e = 329,63; eN = 1;
B = 246,94; BN = 2;
G = 196,00; GN = 3;
D = 146,83; DN = 4;
A = 110,00; AN = 5;
E = 82,41; EN = 6;
% Dadurch wird der Titel computerfreundlicher gespeichert
Lied = Nullen (1,6);
% -Funktion zum Abrufen der Frequenz aus v = Frequenz und f = Bund
w = @ (v, f) v * (2 ^ (f / 12));
% get input und starte die große Schleife
file = fopen ('input.txt');
line = fgetl (Datei);
während ischar (Linie)
    % Das erste Zeichen der Zeile gibt die Zeilenfrequenz an
    lfreqv = eval (Zeile (1)); %Frequenz
    lfreqN = eval ([Zeile (1), 'N']); % horizontaler Frequenzindex
    % starte die kleine Schleife über jeder Zeile
    für k = 3: (numel (line)) - 1
        if (strcmp (Zeile (k), '-')) || (strcmp (Zeile (k), '|')) || (strcmp (Zeile (k), 'h')) || (strcmp (Zeile (k), 'b'))
            Lied (k-2, lfreqN) = 0;
        sonst
            Lied (k-2, lfreqN) = w (lfreqv, double (Linie (k)));
        Ende
    Ende
    line = fgetl (Datei);
Ende
fclose (Datei);
% Dies wird das Lied halten
melodie = [];
vols = Nullen (1,6);
playf = Nullen (1,6);
für songIndex = 1: Größe (song, 1)
    ctune = [];
    für k = 1: 6
        if song (songIndex, k) == 0
            vols (k) = 2 · vols (k) / 4;
        sonst
            vols (k) = 1;
            playf (k) = song (songIndex, k);
        Ende
        ctune (k, 1: t · 8192) = vols (k) · sin (0,5 · pi · playf (k) · (1: (t · 8192)) / 8192);
    Ende
    tune = [tune ctune];
Ende
soundsc (summe (melodie));

Hier ist ein Link zum Sound des ersten Testeingangs.

Hier ist ein Link zum Sound des dritten Testeingangs. (Star Spangled Banner oder Ice Cream Truck?)

Die zweite Testeingabe klang für mich ziemlich schlecht, aber das mag daran liegen, dass sie viele bs und hs verwendet, die vom Skript ignoriert werden.

Wie Sie hören können, hat die Ausgabe nicht ganz die gleiche Qualität wie das Original. Es hört sich so an, als würde im Hintergrund ein Metronom spielen. Ich denke, diese Stücke haben Charakter.


Wow, das klingt wie eine Spieluhr ... Wirklich schön!
Beta Decay

5

Python 3

Ich musste es versuchen.

Dies wandelt einen Tab in eine Midi-Datei um, wie sie von einem Klavier gespielt wird. Ich habe keine Ahnung, wie man eine Saite an einem Klavier biegt, also kann es das nicht, aber Hammer-Ons und Pull-Offs sind unkompliziert.

Ich habe die Testdateien folgendermaßen generiert: $ python3 tab.py The-weight.txt 0.14Wo 0.14ist die Länge einer einzelnen Notiz in Sekunden?

from midiutil.MidiFile3 import MIDIFile
import sys

# Read the relevant lines of the file
lines = []
if len(sys.argv) > 1:
    filename = sys.argv[1]
    try:
        beats_per_minute = 60 / float(sys.argv[2])
    except:
        beats_per_minute = 756
else:
    filename = 'mattys-tune.txt'
    beats_per_minute = 756
with open(filename) as f:
    for line in f:
        if len(line) > 3 and (line[1] == '|' or line[2] == '|'):
            line = line.replace('\n', '')
            lines.append(line)
assert len(lines) % 6 == 0

# Initialize the MIDIFile object (with 1 track)
time = 0
duration = 10
volume = 100
song = MIDIFile(1)
song.addTrackName(0, time, "pianized_guitar_tab.")
song.addTempo(0, time, beats_per_minute)

# The root-pitches of the guitar
guitar = list(reversed([52, 57, 62, 67, 71, 76])) # Assume EADGBe tuning
def add_note(string, fret):
    song.addNote(0, string, guitar[string] + fret, time, duration, volume)

# Process the entire tab
for current in range(0, len(lines), 6):  # The current base string
    for i in range(len(lines[current])): # The position in the current string
        time += 1
        for s in range(6):               # The number of the string
            c = lines[current + s][i]
            try: next_char = lines[current + s][i + 1]
            except: next_char = ''
            if c in '-x\\/bhp':
                # Ignore these characters for now
                continue
            elif c.isdigit():
                # Special case for fret 10 and higher
                if next_char.isdigit():
                    c += next_char
                    lines[current + s] = lines[current + s][:i+1] + '-' + lines[current + s][i+2:]
                # It's a note, play it!
                add_note(s, int(c))
            else:
                # Awww
                time -= 1
                break

# And write it to disk.
def save():
    binfile = open('song.mid', 'wb')
    song.writeFile(binfile)
    binfile.close()
    print('Done')
try:
    save()
except:
    print('Error writing to song.mid, try again.')
    input()
    try:
        save()
    except:
        print('Failed!')

Der Code ist auch auf github, https://github.com/Mattias1/ascii-tab , wo ich auch das Ergebnis der vom OP bereitgestellten Beispiele hochgeladen habe. Ich habe es auch auf einigen meiner eigenen Registerkarten ausprobiert. Es ist ziemlich komisch, ein Klavier spielen zu hören, aber es ist nicht schlecht.

Beispiele:

Ich habe einige direkte Links hinzugefügt, bin mir aber nicht sicher, wie lange sie bleiben. Daher behalte ich auch die alten Download-Links.

  1. Das Gewicht oder Spiel
  2. Riecht nach jugendlichem Geist oder nach Spielen
  3. Sternschnuppenbanner oder spielen
  4. Mattys Melodie oder Spiel
  5. dm melodie oder spielen

Und der Tab von Mattys Melodie (mein Favorit) unten:

    Am/C        Am            F          G             Am/C        Am
e |------------------------|----------------0-------|------------------------|
B |-1--------1--1--------1-|-1--------1--3-----3----|-1--------1--1--------1-|
G |-2-----2-----2-----2----|-2-----2--------------0-|-2-----2-----2-----2----|
D |----2-----------2-------|----2-------------------|----2-----------2-------|
A |-3-----2-----0----------|-------0--------0--2----|-3-----------0----------|
E |-------------------3----|-1-----------3----------|------------------------|

    F        G               Am/C        Am           F           G
e |------------------------|------------------------|----------------0-------|
B |-1--------3-------------|-1--------1--1--------1-|-1--------1--3-----3----|
G |----------4-------------|-2-----2-----2-----2----|-2-----2--------------0-|
D |-------3--5-------------|----2-----------2-------|----2-------------------|
A |----3-----5--------0--2-|-3-----2-----0----------|-------0--------0--2----|
E |-1--------3-----3-------|-------------------3----|-1-----------3----------|

    Am/C        Am           F        G
e |------------------------|------------------------|
B |-1--------1--1--------1-|-1----------3-----------|
G |-2-----2-----2-----2----|------------4-----------|
D |----2-----------2-------|-------3---5------------|
A |-3-----------0----------|----3------5------------|
E |------------------------|-1--------3-------------|

1
Woah, 756 BPM ?! Ich hoffe, das ist nicht der letzte Schlag ...
Beta Decay

Haha, nun, ich betrüge ein bisschen. 2/3von diesen "Beats" sind in der Tat Striche.
Matty

Woah, Mattys Melodie klingt ziemlich cool. Wie ist es auf einer Gitarre?
Beta Decay

1
Danke @BetaDecay, es ist eine Melodie, die ich einmal gemacht habe (die Grundlinie), inspiriert von Tommy Emmanuels blauem Mond ( youtube.com/watch?v=v0IY3Ax2PkY ). Aber es klingt nicht halb so gut wie er es tut.
Matty

4

Java Script

Hinweis: Verwendet das Web Development Audio Kit. Dies ist ein Ausweg aus der IE-Liga. Getestet in Google Chrome

Sie können die Registerkarten in das Textfeld einfügen. IE Sie könnten Mattys Melodie von Mattys Post in den Textbereich stellen (mit den Buchstaben über den Notizen) und es wird immer noch korrekt analysiert.

Klicken Sie hier, um das Programm auszuführen

JavaScript:

context = new AudioContext;
gainNode = context.createGain();
gainNode.connect(context.destination);

gain= 2;

function getValue(i) {
    return document.getElementById(i).value;
}

function addValue(i, d) {
    document.getElementById(i).value += d;
}

function setValue(i, d) {
    document.getElementById(i).value = d;
}

document.getElementById("tada").onclick = updateLines;

function updateLines(){
    var e=getValue("ta").replace(/[^-0-9\n]/g,'').replace("\n\n","\n").split("\n");
    for(var l=0;l<e.length;l+=6){
        try{
        addValue("littleE",e[l]);
        addValue("B",e[l+1]);
        addValue("G",e[l+2]);
        addValue("D",e[l+3]);
        addValue("A",e[l+4]);
        addValue("E",e[l+5]);
        }catch(err){}
    }
    updateDash();
}

document.getElementById("littleE").oninput = updateDash;
document.getElementById("B").oninput = updateDash;
document.getElementById("G").oninput = updateDash;
document.getElementById("D").oninput = updateDash;
document.getElementById("A").oninput = updateDash;
document.getElementById("E").oninput = updateDash;


function updateDash() {
    max = 10;
    findDashMax("littleE");
    findDashMax("B");
    findDashMax("G");
    findDashMax("D");
    findDashMax("A");
    findDashMax("E");
    applyMax();
    i = "littleE";
    dash = new Array();
    for (var l = 0; l < getValue(i).length; l++) {
        if (getValue(i).charCodeAt(l) == 45) {
            dash[l] = true;
        } else {
            dash[l] = false;
        }
    }
    /*applyDash("B");
    applyDash("G");
    applyDash("D");
    applyDash("A");
    applyDash("E");*/
}

function findDashMax(i) {
    if (getValue(i).length > max) {
        max = getValue(i).length;
    }
}

function applyMax() {
    if (max < 50) {
        document.getElementById("stepe").size = 50;
        document.getElementById("littleE").size = 50;
        document.getElementById("B").size = 50;
        document.getElementById("G").size = 50;
        document.getElementById("D").size = 50;
        document.getElementById("A").size = 50;
        document.getElementById("E").size = 50;
    } else {
        document.getElementById("stepe").size = max + 1;
        document.getElementById("littleE").size = max + 1;
        document.getElementById("B").size = max + 1;
        document.getElementById("G").size = max + 1;
        document.getElementById("D").size = max + 1;
        document.getElementById("A").size = max + 1;
        document.getElementById("E").size = max + 1;
    }
}

function applyDash(i) {
    var old = getValue(i);
    setValue(i, "");
    for (var l = 0; l < old.length || dash[l] == true; l++) {
        if (dash[l] == true) {
            addValue(i, "-");
        } else {
            if (old.charCodeAt(l) != 45) {
                addValue(i, old.charAt(l));
            }
        }
    }
}
document.getElementById("next").onclick = begin;

function addDash(i) {
    while (getValue(i).length < max) {
        addValue(i, "-");
    }
}

function begin() {
    setValue("littleE",getValue("littleE").replace(/[^-0-9]/g,''));
    setValue("B",getValue("B").replace(/[^-0-9]/g,''));
    setValue("G",getValue("G").replace(/[^-0-9]/g,''));
    setValue("D",getValue("D").replace(/[^-0-9]/g,''));
    setValue("A",getValue("A").replace(/[^-0-9]/g,''));
    setValue("E",getValue("E").replace(/[^-0-9]/g,''));
    addDash("littleE");
    addDash("B");
    addDash("G");
    addDash("D");
    addDash("A");
    addDash("E");
    setValue("next", "Stop");
    //playing = true;
    findLength();
    document.getElementById("next").onclick = function () {
        clearInterval(playingID);
        oscillator["littleE"].stop(0);
        oscillator["B"].stop(0);
        oscillator["G"].stop(0);
        oscillator["D"].stop(0);
        oscillator["A"].stop(0);
        oscillator["E"].stop(0);
        setValue("next", "Play");
        document.getElementById("next").onclick = begin;
    }
    step = -1;
    playingID = setInterval(function () {
        step++;
        setValue("stepe", "");
        for (var l = 0; l < step; l++) {
            addValue("stepe", " ");
        }
        addValue("stepe", "V");
        if (lg[step]) {
            oscillator["littleE"].stop(0);
            oscillator["B"].stop(0);
            oscillator["G"].stop(0);
            oscillator["D"].stop(0);
            oscillator["A"].stop(0);
            oscillator["E"].stop(0);
        }
        qw=0
        doSound("littleE");
        doSound("B");
        doSound("G");
        doSound("D");
        doSound("A");
        doSound("E");

    }, getValue("s") * 1000);
}

function doSound(i) {
    switch (getValue(i).charAt(step)) {
        case ("-"):
        case ("x"):
        case (""):
        case (" "):
            break;
        default:
            qw++;
            makeSound(fretToHz(getHz(i), getValue(i).charAt(step)), i);

    }
    checkTop();
}

function checkTop(){
    switch(qw){
        case 0:
            break;
        case 1:
            gain=2;
            break;
        case 2:
            gain=1;
            break;
        case 3:
            gain=.5;
            break;
        default:
            gain=.3;
            break;
    }
}

function getHz(i) {
    switch (i) {
        case "littleE":
            return 329.63;
        case "B":
            return 246.94;
        case "G":
            return 196;
        case "D":
            return 146.83;
        case "A":
            return 110;
        case "E":
            return 82.41;
    }
}

function fretToHz(v, f) {
    return v * (Math.pow(2, (f / 12)));
}

/*function getTime() {
    var u = 1;
    while (lg[step + u] == false) {
        u++;
    }
    return u;
}*/

function findLength() {
    lg = new Array();
    for (var h = 0; h < getValue("littleE").length; h++) {
        lg[h] = false;
        fl(h, "littleE");
        fl(h, "B");
        fl(h, "G");
        fl(h, "D");
        fl(h, "A");
        fl(h, "E");
    }
    console.table(lg);
}

function fl(h, i) {
    var l = getValue(i).charAt(h);
    switch (l) {
        case "-":
        case "|":
            break;
        default:
            lg[h] = true;
    }
}

oscillator = new Array();

function makeSound(hz, i) {
    console.log("playing " + hz + " Hz" + i);
    oscillator[i] = context.createOscillator();
    oscillator[i].connect(gainNode);
    oscillator[i].frequency.value = hz;
    oscillator[i].start(0);
}

soundInit("littleE");
soundInit("B");
soundInit("G");
soundInit("D");
soundInit("A");
soundInit("E");

function soundInit(i) {
    makeSound(440, i);
    oscillator[i].stop(0);
}
setInterval(function () {
    gainNode.gain.value = .5 * getValue("v") * gain;
    document.getElementById("q").innerHTML = "Volume:" + Math.round(getValue("v") * 100) + "%";
}, 100);

Kannst du dieses Lied identifizieren?


1
Es stürzt ab bei Charakteren wie | / b h p. Warum nicht einfach ein paar Zeichenfolgen analysieren, um sie zu ersetzen -? Das hört sich ganz ok an und es funktioniert. (Und vielleicht mit einem Eingabefeld in Zeilenumbrüche aufteilen.) Das macht es Spaß, mit diesem Skript zu spielen.
Matty

Das, was ich vorhatte, bin ich einfach nie dazu gekommen.
Grant Davis

Ich stimme zu, die unterschiedliche Linie für jede Saite ist ein Schmerz, aber sonst klingt es gut
Beta Decay

Hoppla, habe vor dem Bearbeiten des Beitrags vergessen, mich anzumelden.
Grant Davis

Ich erkenne die Melodie, kann sie aber nicht benennen ... Klingt aber cool
Beta Decay

2

Java

Dieses Programm konvertiert eine Tabulatur in ein 16-Bit-WAV-Format.

Erstens habe ich eine ganze Reihe von Tabulatur-Parsing-Code geschrieben. Ich bin nicht sicher, ob meine Analyse vollständig korrekt ist, aber ich denke, es ist in Ordnung. Es könnte auch mehr Validierung für Daten gebrauchen.

Danach habe ich den Code erstellt, um das Audio zu generieren. Jeder String wird separat generiert. Das Programm verfolgt die aktuelle Frequenz, Amplitude und Phase. Es werden dann 10 Obertöne für die Frequenz mit erfundenen relativen Amplituden erzeugt und addiert. Schließlich werden die Zeichenfolgen kombiniert und das Ergebnis wird normalisiert. Das Ergebnis wird als WAV-Audio gespeichert, das ich für sein ultra-einfaches Format ausgewählt habe (keine Bibliotheken verwendet).

Es "unterstützt" das Hämmern ( h) und Ziehen ( p), indem es sie ignoriert, da ich wirklich keine Zeit hatte, sie zu unterschiedlich klingen zu lassen. Das Ergebnis klingt allerdings ein bisschen wie eine Gitarre (verbrachte einige Stunden damit, meine Gitarre in Audacity zu analysieren).

Es unterstützt auch das Biegen ( b), Loslassen ( r) und Gleiten ( /und \austauschbar). xwird als Muting des Strings implementiert.

Sie können versuchen, die Konstanten am Anfang des Codes zu optimieren. Besonders das Absenken silenceRateführt oft zu einer besseren Qualität.

Beispielergebnisse

Der Code

Ich möchte jeden Java-Anfänger warnen: Versuchen Sie nicht , etwas aus diesem Code zu lernen, er ist schrecklich geschrieben. Außerdem wurde es schnell und in 2 Sitzungen geschrieben und sollte nie wieder verwendet werden, sodass es keine Kommentare enthält. (Könnte später hinzugefügt werden: P)

import java.io.*;
import java.util.*;

public class TablatureSong {

    public static final int sampleRate = 44100;

    public static final double silenceRate = .4;

    public static final int harmonies = 10;
    public static final double harmonyMultiplier = 0.3;

    public static final double bendDuration = 0.25;

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("Output file:");
        String outfile = in.nextLine();
        System.out.println("Enter tablature:");
        Tab tab = parseTablature(in);
        System.out.println("Enter tempo:");
        int tempo = in.nextInt();
        in.close();

        int samples = (int) (60.0 / tempo * tab.length * sampleRate);
        double[][] strings = new double[6][];
        for (int i = 0; i < 6; i++) {
            System.out.printf("Generating string %d/6...\n", i + 1);
            strings[i] = generateString(tab.actions.get(i), tempo, samples);
        }

        System.out.println("Combining...");
        double[] combined = new double[samples];
        for (int i = 0; i < samples; i++)
            for (int j = 0; j < 6; j++)
                combined[i] += strings[j][i];

        System.out.println("Normalizing...");
        double max = 0;
        for (int i = 0; i < combined.length; i++)
            max = Math.max(max, combined[i]);
        for (int i = 0; i < combined.length; i++)
            combined[i] = Math.min(1, combined[i] / max);

        System.out.println("Writing file...");
        writeWaveFile(combined, outfile);
        System.out.println("Done");
    }

    private static double[] generateString(List<Action> actions, int tempo, int samples) {
        double[] harmonyPowers = new double[harmonies];
        for (int harmony = 0; harmony < harmonyPowers.length; harmony++) {
            if (Integer.toBinaryString(harmony).replaceAll("[^1]", "").length() == 1)
                harmonyPowers[harmony] = 2 * Math.pow(harmonyMultiplier, harmony);
            else
                harmonyPowers[harmony] = Math.pow(harmonyMultiplier, harmony);
        }
        double actualSilenceRate = Math.pow(silenceRate, 1.0 / sampleRate);

        double[] data = new double[samples];

        double phase = 0.0, amplitude = 0.0;
        double slidePos = 0.0, slideLength = 0.0;
        double startFreq = 0.0, endFreq = 0.0, thisFreq = 440.0;
        double bendModifier = 0.0;
        Iterator<Action> iterator = actions.iterator();
        Action next = iterator.hasNext() ? iterator.next() : new Action(Action.Type.NONE, Integer.MAX_VALUE);

        for (int sample = 0; sample < samples; sample++) {
            while (sample >= toSamples(next.startTime, tempo)) {
                switch (next.type) {
                case NONE:
                    break;
                case NOTE:
                    amplitude = 1.0;
                    startFreq = endFreq = thisFreq = next.value;
                    bendModifier = 0.0;
                    slidePos = 0.0;
                    slideLength = 0;
                    break;
                case BEND:
                    startFreq = addHalfSteps(thisFreq, bendModifier);
                    bendModifier = next.value;
                    slidePos = 0.0;
                    slideLength = toSamples(bendDuration);
                    endFreq = addHalfSteps(thisFreq, bendModifier);
                    break;
                case SLIDE:
                    slidePos = 0.0;
                    slideLength = toSamples(next.endTime - next.startTime, tempo);
                    startFreq = thisFreq;
                    endFreq = thisFreq = next.value;
                    break;
                case MUTE:
                    amplitude = 0.0;
                    break;
                }
                next = iterator.hasNext() ? iterator.next() : new Action(Action.Type.NONE, Integer.MAX_VALUE);
            }

            double currentFreq;
            if (slidePos >= slideLength || slideLength == 0)
                currentFreq = endFreq;
            else
                currentFreq = startFreq + (endFreq - startFreq) * (slidePos / slideLength);

            data[sample] = 0.0;
            for (int harmony = 1; harmony <= harmonyPowers.length; harmony++) {
                double phaseVolume = Math.sin(2 * Math.PI * phase * harmony);
                data[sample] += phaseVolume * harmonyPowers[harmony - 1];
            }

            data[sample] *= amplitude;
            amplitude *= actualSilenceRate;
            phase += currentFreq / sampleRate;
            slidePos++;
        }
        return data;
    }

    private static int toSamples(double seconds) {
        return (int) (sampleRate * seconds);
    }

    private static int toSamples(double beats, int tempo) {
        return (int) (sampleRate * beats * 60.0 / tempo);
    }

    private static void writeWaveFile(double[] data, String outfile) {
        try (OutputStream out = new FileOutputStream(new File(outfile))) {
            out.write(new byte[] { 0x52, 0x49, 0x46, 0x46 }); // Header: "RIFF"
            write32Bit(out, 44 + 2 * data.length, false); // Total size
            out.write(new byte[] { 0x57, 0x41, 0x56, 0x45 }); // Header: "WAVE"
            out.write(new byte[] { 0x66, 0x6d, 0x74, 0x20 }); // Header: "fmt "
            write32Bit(out, 16, false); // Subchunk1Size: 16
            write16Bit(out, 1, false); // Format: 1 (PCM)
            write16Bit(out, 1, false); // Channels: 1
            write32Bit(out, 44100, false); // Sample rate: 44100
            write32Bit(out, 44100 * 1 * 2, false); // Sample rate * channels *
                                                    // bytes per sample
            write16Bit(out, 1 * 2, false); // Channels * bytes per sample
            write16Bit(out, 16, false); // Bits per sample
            out.write(new byte[] { 0x64, 0x61, 0x74, 0x61 }); // Header: "data"
            write32Bit(out, 2 * data.length, false); // Data size
            for (int i = 0; i < data.length; i++) {
                write16Bit(out, (int) (data[i] * Short.MAX_VALUE), false); // Data
            }
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void write16Bit(OutputStream stream, int val, boolean bigEndian) throws IOException {
        int a = (val & 0xFF00) >> 8;
        int b = val & 0xFF;
        if (bigEndian) {
            stream.write(a);
            stream.write(b);
        } else {
            stream.write(b);
            stream.write(a);
        }
    }

    private static void write32Bit(OutputStream stream, int val, boolean bigEndian) throws IOException {
        int a = (val & 0xFF000000) >> 24;
        int b = (val & 0xFF0000) >> 16;
        int c = (val & 0xFF00) >> 8;
        int d = val & 0xFF;
        if (bigEndian) {
            stream.write(a);
            stream.write(b);
            stream.write(c);
            stream.write(d);
        } else {
            stream.write(d);
            stream.write(c);
            stream.write(b);
            stream.write(a);
        }
    }

    private static double[] strings = new double[] { 82.41, 110.00, 146.83, 196.00, 246.94, 329.63 };

    private static Tab parseTablature(Scanner in) {
        String[] lines = new String[6];
        List<List<Action>> result = new ArrayList<>();
        int longest = 0;
        for (int i = 0; i < 6; i++) {
            lines[i] = in.nextLine().trim().substring(2);
            longest = Math.max(longest, lines[i].length());
        }
        int skipped = 0;
        for (int i = 0; i < 6; i++) {
            StringIterator iterator = new StringIterator(lines[i]);
            List<Action> actions = new ArrayList<Action>();
            while (iterator.index() < longest) {
                if (iterator.get() < '0' || iterator.get() > '9') {
                    switch (iterator.get()) {
                    case 'b':
                        actions.add(new Action(Action.Type.BEND, 1, iterator.index(), iterator.index()));
                        iterator.next();
                        break;
                    case 'r':
                        actions.add(new Action(Action.Type.BEND, 0, iterator.index(), iterator.index()));
                        iterator.next();
                        break;
                    case '/':
                    case '\\':
                        int startTime = iterator.index();
                        iterator.findNumber();
                        int endTime = iterator.index();
                        int endFret = iterator.readNumber();
                        actions.add(new Action(Action.Type.SLIDE, addHalfSteps(strings[5 - i], endFret), startTime,
                                endTime));
                        break;
                    case 'x':
                        actions.add(new Action(Action.Type.MUTE, iterator.index()));
                        iterator.next();
                        break;
                    case '|':
                        iterator.skip(1);
                        iterator.next();
                        break;
                    case 'h':
                    case 'p':
                    case '-':
                        iterator.next();
                        break;
                    default:
                        throw new RuntimeException(String.format("Unrecognized character: '%c'", iterator.get()));
                    }
                } else {
                    StringBuilder number = new StringBuilder();
                    int startIndex = iterator.index();
                    while (iterator.get() >= '0' && iterator.get() <= '9') {
                        number.append(iterator.get());
                        iterator.next();
                    }
                    int fret = Integer.parseInt(number.toString());
                    double freq = addHalfSteps(strings[5 - i], fret);
                    actions.add(new Action(Action.Type.NOTE, freq, startIndex, startIndex));
                }
            }
            result.add(actions);
            skipped = iterator.skipped();
        }
        return new Tab(result, longest - skipped);
    }

    private static double addHalfSteps(double freq, double halfSteps) {
        return freq * Math.pow(2, halfSteps / 12.0);
    }

}

class StringIterator {
    private String string;
    private int index, skipped;

    public StringIterator(String string) {
        this.string = string;
        index = 0;
        skipped = 0;
    }

    public boolean hasNext() {
        return index < string.length() - 1;
    }

    public void next() {
        index++;
    }

    public void skip(int length) {
        skipped += length;
    }

    public char get() {
        if (index < string.length())
            return string.charAt(index);
        return '-';
    }

    public int index() {
        return index - skipped;
    }

    public int skipped() {
        return skipped;
    }

    public boolean findNumber() {
        while (hasNext() && (get() < '0' || get() > '9'))
            next();
        return get() >= '0' && get() <= '9';
    }

    public int readNumber() {
        StringBuilder number = new StringBuilder();
        while (get() >= '0' && get() <= '9') {
            number.append(get());
            next();
        }
        return Integer.parseInt(number.toString());
    }
}

class Action {
    public static enum Type {
        NONE, NOTE, BEND, SLIDE, MUTE;
    }

    public Type type;
    public double value;
    public int startTime, endTime;

    public Action(Type type, int time) {
        this(type, time, time);
    }

    public Action(Type type, int startTime, int endTime) {
        this(type, 0, startTime, endTime);
    }

    public Action(Type type, double value, int startTime, int endTime) {
        this.type = type;
        this.value = value;
        this.startTime = startTime;
        this.endTime = endTime;
    }
}

class Tab {
    public List<List<Action>> actions;
    public int length;

    public Tab(List<List<Action>> actions, int length) {
        this.actions = actions;
        this.length = length;
    }
}

Ich weiß, dass ich es nicht spezifiziert habe, aber könnten Sie einige Testfälle posten, die die Leute gerne in den anderen Antworten hören können?
Beta Decay

@BetaDecay Aktualisiert meine Antwort, hat jetzt eine Reihe von Tests
PurkkaKoodari

Diese Links funktionieren nicht: /
Beta Decay

@BetaDecay Ich habe im Inkognito-Modus eines Browsers, den ich nicht verwende, eine andere Verbindung doppelt überprüft. Sie arbeiten zumindest für mich.
PurkkaKoodari

Schön, ich mag deine Version von Mattys Musik sehr, obwohl die Basis manchmal schwer zu hören ist.
Matty
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.