Riproduci una canzone per me


23

Sfida

Data la tablatura della chitarra, è necessario riprodurre il brano rappresentato dalla scheda. Ciò può riguardare gli altoparlanti del computer o un file audio (.wav, .mp3, .midi, .aiff, ecc.). Ci sarà anche un secondo input per il timing.

Le schede possono essere inserite tramite un file o direttamente su STDIN. La scheda sarà in formato ASCII .

Spec

Tutte le schede sono per 6 chitarre a sei corde con accordatura E standard: E2 (82,41 Hz), A2 (110,00 Hz), D3 (146,83 Hz), G3 (196,00 Hz), B3 (246,94 Hz), E4 (329,63 Hz).

Le uniche tecniche (oltre alla normale raccolta) che devi soddisfare sono:

  • Piegatura (questa sarà sempre una curva a mezzo tono)
  • Martellare
  • Staccare
  • Scorrimento su / giù

Poiché non è possibile sintetizzare il suono di una stringa disattivata, trattare xcome a -.

Durante la piegatura, emettere di nuovo la transizione completa da non piegato a corda a piegato a non piegato.

Il secondo input sarà il tempo che ciascun simbolo nella scheda rappresenta in secondi. Per esempio:

Per input:

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

Con i tempi 0.5, poiché ci sono 3colonne di simboli (ma nessuna nota), il file audio emesso è ( 3*0.5=1.5) 1.5secondi di silenzio.

Schede di esempio

1 - The Weight (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 - Odori come Teen Spirit

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

3 - Star 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
Ho aggiunto qualche altro decimale alle tue frequenze. Dato che un semitono = 1 tasto è un rapporto di 1,059463: 1 (cioè una differenza di circa il 6%) l'accordatura al 1Hz più vicino non è abbastanza precisa per ottenere un buon suono in sintonia. Naturalmente essendo un concorso di popolarità, la scarsa messa a punto può essere ammissibile ma non vincerà.
Level River St

Concorso molto creativo! Dopo aver esaminato il collegamento al modulo ASCII, ho capito l'esempio 2 (da quando ho sentito la canzone), ma poiché non conosco la chitarra, penso che la sfida abbia un'alta curva di apprendimento. Ho anche poca esperienza con la manipolazione dell'audio oltre all'utilizzo di base di Audacity.
mbomb007

Il MIDI viene considerato come un "file audio"?
orlp

@orlp Sì, lo fa
Decadimento beta

1
Bene per riferimento futuro: v * (2 ^ (f / 12)) = x; v = frequenza della stringa; f = tasto (il numero nella scheda); x = frequenza riprodotta; Le schede inoltre non indicano la durata di una nota; il tuo programma deve essere intelligente.
Grant Davis,

Risposte:


7

MATLAB

Questo è un po 'incompiuto. Ho usato un metodo rapido e sporco per rendere l'audio il più facilmente possibile. Il metodo che ho usato ha reso difficile implementare la piegatura / martellamento (non avevo mai sentito prima quelle parole in questo contesto).

Detto questo, questo script leggerà in un file di testo chiamato "inputs.txt" contenente la scheda ASCII come richiesto e riprodurrà la canzone.

% temporizzazione
t = 0.25; % ovviamente, questa linea potrebbe essere 't = input (' timing: ');
        % se si imposta un valore traballante tale che t * 8192 non sia un numero intero, alcuni
        % roba fallirà
Frequenze% e variabili extra per consentire una pigrizia in seguito
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;
% questo memorizzerà la canzone in un modo più intuitivo
canzone = zeri (1,6);
Funzione% per ottenere la frequenza da v = frequenza e f = tasto
w = @ (v, f) v * (2 ^ (f / 12));
% ottiene input e avvia il ciclo grande
file = fopen ('input.txt');
line = fgetl (file);
mentre ischar (linea)
    % il primo carattere della linea ci darà la frequenza della linea
    lfreqv = eval (linea (1)); %frequenza
    lfreqN = eval ([linea (1), 'N']); % indice orizzontale di frequenza
    % avvia il ciclo piccolo su ogni riga
    per k = 3: (numel (linea)) - 1
        if (strcmp (line (k), '-')) || (strcmp (line (k), '|')) || (strcmp (line (k), 'h')) || (strcmp (line (k), 'b'))
            canzone (k-2, lfreqN) = 0;
        altro
            canzone (k-2, lfreqN) = w (lfreqv, double (line (k)));
        fine
    fine
    line = fgetl (file);
fine
fclose (file);
% questo conterrà la canzone
tune = [];
vols = zeri (1,6);
playf = zeri (1,6);
per songIndex = 1: size (brano, 1)
    ctune = [];
    per k = 1: 6
        if brano (songIndex, k) == 0
            vols (k) = 2 * vols (k) / 4;
        altro
            vols (k) = 1;
            playf (k) = brano (songIndex, k);
        fine
        ctune (k, 1: t * 8192) = volumi (k) * sin (0,5 * pi * visualizzarloF (k) * (1: (t * 8192)) / 8192);
    fine
    tune = [tune ctune];
fine
soundsc (sum (tune));

Ecco un link al suono del primo input di test.

Ecco un collegamento al suono del terzo ingresso di prova. (Star Spangled Banner o Ice Cream Truck?)

Il secondo input di test mi è sembrato piuttosto negativo, ma potrebbe essere perché utilizza molti bs e hs che lo script ignora.

Come puoi sentire, l'output non ha la stessa qualità dell'originale. Sembra quasi che ci sia un metronomo in sottofondo. Penso che questi brani abbiano carattere.


Wow, sembra un carillon ... Davvero bello!
Decadimento beta

5

Python 3

Ho dovuto provare questo.

Questo converte una scheda in un file MIDI riprodotto da un piano. Non ho idea di come piegare una corda su un piano, quindi non può farlo, ma il martello e il pull-off sono semplici.

Ho generato i file di test in questo modo: $ python3 tab.py The-weight.txt 0.14dov'è 0.14la lunghezza di una singola nota in secondi.

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!')

Anche il codice è su github, https://github.com/Mattias1/ascii-tab , dove ho anche caricato il risultato degli esempi forniti dall'OP. L'ho provato anche su alcune delle mie schede. È abbastanza strano sentire un pianoforte suonarlo, ma non è male.

Esempi:

Ho aggiunto alcuni link diretti, ma non sono sicuro di quanto durano, quindi terrò anche i vecchi link per il download.

  1. Il peso o gioco
  2. Odora di spirito adolescenziale o gioco
  3. Banner stellato con stelle , o gioco
  4. Melodia di Matty , o suona
  5. dm tune , o suona

E la scheda dalla melodia di Matty (la mia preferita) di seguito:

    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 ?! Spero che non sia l'ultima battuta ...
Beta Decay

Haha, beh, baro un po '. 2/3di questi "battiti" sono in realtà trattini.
Matty,

Woah, la melodia di Matty sembra piuttosto bella. Com'è una chitarra?
Decadimento beta

1
Grazie @BetaDecay, è una melodia che ho realizzato una volta (la linea di base) ispirata alla luna blu di Tommy Emmanuel ( youtube.com/watch?v=v0IY3Ax2PkY ). Ma non sembra la metà di come lo fa.
Matty,

4

Java Script

Nota: utilizza il kit audio di sviluppo Web; Questo è fuori dalla IE's League; Testato su Google Chrome

È possibile inserire le schede nell'area di testo. Vale a dire che potresti mettere la melodia di Matty dal post di Matty nell'area di testo (con le lettere sopra le note) e verrà comunque analizzata correttamente.

Fare clic per eseguire il programma

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);

Riesci a identificare questa canzone?


1
Si blocca su personaggi come | / b h p. Perché non fare solo un po 'di analisi delle stringhe per sostituirle -? Suonerà abbastanza bene e funziona. (E forse diviso su newline usando una casella di input.). Questo renderà questo uno script divertente con cui giocare.
Matty,

Quello che stavo pianificando di fare, non ci sono mai riuscito.
Grant Davis,

Sono d'accordo, la diversa linea per ogni stringa è un dolore ma per il resto suona bene
Decadimento Beta

Si è dimenticato di accedere prima di modificare il post.
Grant Davis,

Riconosco la melodia ma non riesco a dargli un nome ... Sembra bello però
Decadimento beta

2

Giava

Questo programma converte una tablatura nel formato WAV a 16 bit.

In primo luogo, ho scritto un sacco di codice di analisi della tablatura. Non sono sicuro che la mia analisi sia del tutto corretta, ma penso che vada bene. Inoltre, potrebbe utilizzare più convalida per i dati.

Successivamente, ho creato il codice per generare l'audio. Ogni stringa viene generata separatamente. Il programma tiene traccia della frequenza, dell'ampiezza e della fase correnti. Quindi genera 10 sovratoni per la frequenza con ampiezze relative inventate e le somma. Infine, le stringhe vengono combinate e il risultato viene normalizzato. Il risultato viene salvato come audio WAV, che ho scelto per il suo formato ultra-semplice (nessuna libreria utilizzata).

"Supporta" hammering ( h) e pull ( p) ignorandoli poiché non ho davvero avuto il tempo di farli sembrare troppo diversi. Il risultato suona un po 'come una chitarra (ho trascorso alcune ore ad analizzare la mia chitarra in Audacity).

Inoltre, supporta bending ( b), release ( r) e slide ( /e \, intercambiabili).xè implementato come silenziamento della stringa.

Puoi provare a modificare le costanti all'inizio del codice. Soprattutto l'abbassamento silenceRatespesso porta a una migliore qualità.

Risultati di esempio

Il codice

Voglio avvertire tutti i principianti di Java: non provare a imparare nulla da questo codice, è terribilmente scritto. Inoltre, è stato scritto rapidamente e in 2 sessioni e non è stato pensato per essere usato mai più, quindi non ha commenti. (Potrebbe aggiungerne un po 'più tardi: 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;
    }
}

So di non averlo specificato, ma potresti pubblicare alcuni casi di test che le persone possono ascoltare come nelle altre risposte?
Decadimento beta

@BetaDecay Aggiornato la mia risposta, ora ha un sacco di test
PurkkaKoodari

Quei link non funzionano: /
Decadimento beta

@BetaDecay Ho ricontrollato un'altra connessione in modalità di navigazione in incognito di un browser che non utilizzo. Funzionano per me, almeno.
PurkkaKoodari,

Bene, mi piace molto la tua versione di Mattys, anche se a volte la base è difficile da ascoltare.
Matty,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.