Jouez une chanson pour moi

23

Défi

Étant donné la tablature de guitare, vous devez produire le morceau représenté par l'onglet. Il peut s'agir des haut-parleurs de votre ordinateur ou d'un fichier audio (.wav, .mp3, .midi, .aiff, etc.). Il y aura également une deuxième entrée pour le chronométrage.

Les onglets peuvent être saisis via un fichier ou directement dans STDIN. L'onglet sera au format ASCII .

Spec

Tous les onglets sont pour 6 guitares à six cordes avec accordage 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).

Les seules techniques (en plus de la cueillette normale) que vous devez utiliser sont:

  • Flexion (ce sera toujours un demi-ton)
  • Marteler
  • Enlevant
  • Glissement haut / bas

Comme vous ne pouvez pas synthétiser le son d'une chaîne en sourdine, traitez xcomme un -.

Lors du pliage, sortez à nouveau la transition complète de non plié en chaîne à plié en déplié.

La deuxième entrée sera le temps que chaque symbole sur l'onglet représente en secondes. Par exemple:

Pour l'entrée:

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

Avec le timing 0.5, car il y a des 3colonnes de symboles (mais pas de notes), le fichier audio généré est de ( 3*0.5=1.5) 1.5secondes de silence.

Exemples d'onglets

1 - Le poids (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 - Sent comme l'adolescence

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

3 - bannière étoilée

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|----------------------------------------------------|
Beta Decay
la source
3
J'ai ajouté quelques décimales supplémentaires à vos fréquences. Étant donné qu'un demi-ton = 1 frette est un rapport de 1,059463: 1 (c'est-à-dire une différence d'environ 6%), le réglage sur le 1 Hz le plus proche n'est pas assez précis pour obtenir un bon son en accord. Bien sûr, étant un concours de popularité, un mauvais réglage peut être admissible, mais il ne gagnera pas.
Level River St
Concours très créatif! Après avoir regardé le lien vers le formulaire ASCII, j'ai pu comprendre l'exemple 2 (depuis que j'ai entendu la chanson), mais parce que je ne connais pas la guitare, je pense que le défi a une courbe d'apprentissage élevée. J'ai également peu d'expérience avec la manipulation audio autre que l'utilisation de base d'Audacity.
mbomb007
Le MIDI compte-t-il comme un «fichier audio»?
orlp
@orlp Oui, c'est le cas
Beta Decay
1
Eh bien pour référence future: v * (2 ^ (f / 12)) = x; v = fréquence de chaîne; f = Fret (le numéro sur l'onglet); x = fréquence jouée; Les onglets ne vous indiquent pas non plus la longueur d'une note; votre programme doit être intelligent.
Grant Davis

Réponses:

7

MATLAB

C'est en quelque sorte inachevé. J'ai utilisé une méthode rapide et sale pour rendre l'audio aussi facilement que possible. La méthode que j'ai utilisée a rendu difficile la mise en œuvre du pliage / martelage (je n'avais jamais entendu ces mots dans ce contexte auparavant).

Cela dit, ce script lira dans un fichier texte appelé "inputs.txt" contenant l'ascii-tab comme requis et jouera la chanson.

%timing
t = 0,25; % bien sûr, cette ligne pourrait être 't = input (' timing: ');
        % si vous effectuez une valeur de ta telle que t * 8192 n'est pas un entier, certains
        % stuff échouera
% fréquences et variables supplémentaires pour permettre une certaine paresse plus tard
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;
% cela stockera la chanson d'une manière plus conviviale
chanson = zéros (1,6);
Fonction% pour obtenir la fréquence de v = fréquence et f = frette
w = @ (v, f) v * (2 ^ (f / 12));
% obtenir une entrée et démarrer la grande boucle
file = fopen ('input.txt');
ligne = fgetl (fichier);
tandis que ischar (ligne)
    % le premier caractère de la ligne nous donnera la fréquence de la ligne
    lfreqv = eval (ligne (1)); %la fréquence
    lfreqN = eval ([ligne (1), 'N']); % indice de fréquence horizontal
    % démarre la petite boucle sur chaque ligne
    pour k = 3: (numel (ligne)) - 1
        if (strcmp (ligne (k), '-')) || (strcmp (ligne (k), '|')) || (strcmp (ligne (k), 'h')) || (strcmp (ligne (k), «b»))
            chanson (k-2, lfreqN) = 0;
        autre
            chanson (k-2, lfreqN) = w (lfreqv, double (ligne (k)));
        fin
    fin
    ligne = fgetl (fichier);
fin
fclose (fichier);
% cela tiendra la chanson
tune = [];
vols = zéros (1,6);
playf = zéros (1,6);
pour songIndex = 1: taille (chanson, 1)
    ctune = [];
    pour k = 1: 6
        si chanson (songIndex, k) == 0
            vols (k) = 2 * vols (k) / 4;
        autre
            vols (k) = 1;
            playf (k) = chanson (songIndex, k);
        fin
        ctune (k, 1: t * 8192) = vols (k) * sin (0,5 * pi * playf (k) * (1: (t * 8192)) / 8192);
    fin
    tune = [tune ctune];
fin
soundsc (somme (air));

Voici un lien vers le son de la première entrée de test.

Voici un lien vers le son de la troisième entrée de test. (Bannière étoilée étoilée ou camion de crème glacée?)

La deuxième entrée de test m'a semblé assez mauvaise, mais c'est peut-être parce qu'elle utilise beaucoup de bs et de hs que le script ignore.

Comme vous pouvez l'entendre, la sortie n'est pas tout à fait de la même qualité que l'original. Cela ressemble à un métronome jouant en arrière-plan. Je pense que ces airs ont du caractère.

sudo rm -rf slash
la source
Wow, cela ressemble à une boîte à musique ... Vraiment sympa!
Beta Decay
5

Python 3

J'ai dû essayer celui-ci.

Cela convertit un onglet en un fichier midi tel que joué par un piano. Je ne sais pas comment faire une flexion de cordes sur un piano, donc ça ne peut pas faire ça, mais les marteaux et les pull-off sont simples.

J'ai généré les fichiers de test comme ceci: $ python3 tab.py The-weight.txt 0.140.14est la longueur d'une seule note en secondes.

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

Le code est également sur github, https://github.com/Mattias1/ascii-tab , où j'ai également téléchargé le résultat des exemples fournis par l'OP. Je l'ai également essayé sur certains de mes propres onglets. C'est assez bizarre d'entendre un piano jouer, mais ce n'est pas mal.

Exemples:

J'ai ajouté quelques liens directs, mais je ne sais pas combien de temps ils restent, donc je conserverai également les anciens liens de téléchargement.

  1. Le poids ou jouer
  2. Ça sent l'esprit de l'adolescence ou jouer
  3. Bannière étoilée ou jouer
  4. L'air de Matty , ou jouer
  5. dm syntoniser ou jouer

Et l'onglet de l'air de Matty (mon préféré) ci-dessous:

    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-------------|
Matty
la source
1
Woah, 756 BPM?! J'espère que ce n'est pas le dernier battement ...
Beta Decay
Haha, eh bien, je triche un peu. 2/3de ces «battements» sont en fait des tirets.
Matty
Woah, l'air de Matty semble plutôt cool. Comment ça se passe à la guitare?
Beta Decay
1
Merci @BetaDecay, c'est un air que j'ai fait une fois, (la ligne de base) inspiré par la lune bleue de Tommy Emmanuel ( youtube.com/watch?v=v0IY3Ax2PkY ). Mais cela ne sonne pas à moitié aussi bien que la façon dont il le fait.
Matty
4

Java Script

Remarque: Utilise le kit audio de développement Web; C'est bien loin de la Ligue d'IE; Testé dans Google Chrome

Vous pouvez mettre les onglets dans la zone de texte. IE, vous pouvez mettre l'air de Matty du post de Matty dans la zone de texte (avec les lettres sur les notes) et il sera toujours analysé correctement.

Cliquez pour exécuter le programme

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

Pouvez-vous identifier cette chanson?

Grant Davis
la source
1
Il se bloque sur des personnages comme | / b h p. Pourquoi ne pas simplement faire une petite analyse de chaîne pour les remplacer -? Cela semblera tout à fait correct et cela fonctionne. (Et peut-être divisé sur les sauts de ligne en utilisant une zone de saisie.). Cela en fera un script amusant à jouer.
Matty
C'est ce que j'avais l'intention de faire, je n'y suis jamais arrivé.
Grant Davis
Je suis d'accord, la ligne différente pour chaque chaîne est une douleur mais sinon ça sonne bien
Beta Decay
Oups, j'ai oublié de me connecter avant de modifier l'article.
Grant Davis
Je reconnais la mélodie mais je ne peux pas lui donner de nom ... Ça a l'air cool quand même
Beta Decay
2

Java

Ce programme convertit une tablature au format WAV 16 bits.

Tout d'abord, j'ai écrit tout un tas de code d'analyse de tablatures. Je ne sais pas si mon analyse est entièrement correcte, mais je pense que ça va. En outre, il pourrait utiliser plus de validation pour les données.

Après cela, j'ai créé le code pour générer l'audio. Chaque chaîne est générée séparément. Le programme enregistre la fréquence, l'amplitude et la phase actuelles. Il génère ensuite 10 harmoniques pour la fréquence avec des amplitudes relatives composées et les additionne. Enfin, les chaînes sont combinées et le résultat est normalisé. Le résultat est enregistré en audio WAV, que j'ai choisi pour son format ultra-simple (pas de librairies utilisées).

Il "supporte" hammering ( h) et pulling ( p) en les ignorant car je n'ai vraiment pas eu le temps de les faire sonner trop différemment. Le résultat sonne un peu comme une guitare, cependant (passé quelques heures à analyser le spectre de ma guitare dans Audacity).

En outre, il prend en charge la flexion ( b), la libération ( r) et le glissement ( /et \, interchangeables). xest implémenté comme coupant la chaîne.

Vous pouvez essayer de modifier les constantes au début du code. Un abaissement particulièrement silenceRateconduit souvent à une meilleure qualité.

Exemples de résultats

Le code

Je veux avertir les débutants Java: n'essayez pas d'apprendre quoi que ce soit de ce code, il est terriblement écrit. De plus, il a été écrit rapidement et en 2 sessions et il n'était pas destiné à être utilisé à nouveau donc il n'a aucun commentaire. (Pourrait en ajouter plus tard: 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;
    }
}
PurkkaKoodari
la source
Je sais que je ne l'ai pas précisé, mais pourriez-vous publier des cas de test que les gens peuvent écouter dans les autres réponses?
Beta Decay
@BetaDecay Mis à jour ma réponse, a maintenant un tas de tests
PurkkaKoodari
Ces liens ne fonctionnent pas: /
Beta Decay
@BetaDecay J'ai revérifié une autre connexion en mode navigation privée d'un navigateur que je n'utilise pas. Ils travaillent pour moi, au moins.
PurkkaKoodari
Bien, j'aime beaucoup votre version de Mattys, même si la base est parfois difficile à entendre.
Matty