Benim için bir şarkı çal


23

Meydan okuma

Gitar tablasına bakıldığında, tab ile temsil edilen şarkıyı çıkarmanız gerekir. Bu, bilgisayarınızın hoparlörlerine veya bir ses dosyasına (.wav, .mp3, .midi, .aiff, vb.) Olabilir. Ayrıca zamanlama için ikinci bir giriş olacaktır.

Sekmeler bir dosya aracılığıyla veya doğrudan STDIN'e girilebilir. Sekme ASCII formunda olacaktır .

spec

Tüm sekmeler standart E ayarlı 6 altı telli gitar içindir: E2 (82.41 Hz), A2 (110.00 Hz), D3 (146.83 Hz), G3 (196.00 Hz), B3 (246.94 Hz), E4 (329.63 Hz).

Hazırlamanız gereken tek teknik (normal seçim dışında):

  • Bükme (bu her zaman yarım ton bükme olur)
  • Üzerinde çekiç
  • Çekerek
  • Yukarı / aşağı kaydırmak

Sessiz bir dizenin sesini sentezleyemediğiniz için a xgibi davranın -.

Bükme sırasında, unbent'ten dizgeye tam geçişi tekrar bükülmüş duruma getirin.

İkinci giriş, sekmedeki her sembolün saniye cinsinden temsil ettiği zaman olacaktır. Örneğin:

Giriş için:

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

Zamanlama ile 0.5, 3sembollerin kolonları (ancak notların olmaması) olması nedeniyle, çıkarılan ses dosyası ( 3*0.5=1.5) 1.5saniye sessizliktir.

Örnek sekmeler

1 - Ağırlık (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 - Teen Spirit gibi kokuyor

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

3 - Yıldız Spangled Afiş

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
Frekanslarınıza birkaç ondalık basamak ekledim. Bir yarı ton = 1 perde olduğu için, 1.059463: 1'lik bir oran (yani yaklaşık% 6'lık bir fark) en yakın 1Hz'e ayarlandığı için, iyi bir ayar sesi elde etmek için yeterince kesin değildir. Elbette popülerlik yarışması olmasından dolayı, zayıf ayarlamalar kabul edilebilir, ancak kazanamaz.
Seviye River St

Çok yaratıcı bir yarışma! ASCII formunun bağlantısına baktıktan sonra, örnek 2'yi anlayabiliyordum (şarkıyı duyduğumdan beri), ancak gitar bilmediğim için zorluğun yüksek bir öğrenme eğrisi olduğunu düşünüyorum. Ayrıca Audacity'nin temel kullanımından başka ses manipülasyonu konusunda çok az deneyime sahibim.
mbomb007

MIDI 'ses dosyası' olarak sayılıyor mu?
orlp

@ orlp Evet, öyle
Beta Decay

1
Gelecekteki referans için iyi: v * (2 ^ (f / 12)) = x; v = dizgenin frekansı; f = Fret (sekmedeki sayı); x = oynanan frekans; Sekmeler ayrıca notun uzunluğunu da söylemez; programınızın akıllı olması gerekiyor.
Grant Davis, 0:

Yanıtlar:


7

MATLAB

Bu bitmemiş bir tür. Sesi olabildiğince kolay hale getirmek için hızlı ve kirli bir yöntem kullandım. Kullandığım yöntem bükme / çekiçlemeyi zorlaştırdı (bu kelimeleri daha önce hiç duymamıştım).

Bunların hepsini söyledikten sonra, bu komut dosyası ascii-tab'ı içeren "inputs.txt" adlı bir metin dosyasında okuyacak ve şarkıyı çalacaktır.

%zamanlama
t = 0.25; Tabii ki, bu satır 't = giriş olabilir (' zamanlama: ');
        t * 8192'nin bir tamsayı olmadığı şekilde kazançlı bir değer verirseniz,
        % malzeme başarısız olacak
Daha sonra tembellik için% frekans ve ekstra değişkenler
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;
% Bu şarkıyı daha bilgisayar dostu bir şekilde saklayacaktır.
şarkı = sıfır (1,6);
v = frekans ve f = fret'ten frekans almak için% işlevi
W = @ (h, f) V * (2 ^ (f / 12));
% get get ve büyük döngüyü başlat
file = fopen ('input.txt');
line = fgetl (dosya);
ischar (satır) iken
    Satırın ilk karakteri% olarak bize çizgi frekansını verir.
    lfreqv = eval (satır (1)); %Sıklık
    lfreqN = eval ([satır (1), 'N']); % yatay frekans indeksi
    % her satırdaki küçük döngüyü başlat
    k = 3 için: (sayı (çizgi)) - 1
        if (strcmp (satır (k), '-')) || || (strcmp (satır (k), '|')) || (strcmp (satır (k), 'h')) || (strcmp (satır) (k) 'b'))
            şarkı (k-2, lfreqN) = 0;
        Başka
            şarkı (k-2, lfreqN) = w (lfreqv, çift (satır (k)));
        son
    son
    line = fgetl (dosya);
son
fclose (dosya);
Bu şarkıyı tutacak%
ayar = [];
vol = sıfır (1,6);
playf = sıfır (1,6);
songIndex = 1 için: size (şarkı, 1)
    ctune = [];
    k = 1: 6 için
        eğer şarkı (songIndex, k) == 0
            vol (k) = 2 x vol (k) / 4;
        Başka
            vol (k) = 1;
            playf (k) = şarkı (songIndex, k);
        son
        ctune (k, 1: t * 8192) = cilt (k) * sin (0.5 x Pi * playf (k) * (1: (t * 8192)) / 8192);
    son
    ayar = [ayar ctune];
son
soundsc (toplam (ayar));

İşte ilk test girişinin sesine bir link.

İşte üçüncü test girişinin sesine bir link. (Star Spangled Banner veya Dondurma Kamyonu?)

İkinci test girişi bana çok kötü geldi, ancak bunun nedeni betiğin yoksaydığı çok bs ve hs kullanması olabilir .

Duyduğunuz gibi, çıktı, orijinal ile tamamen aynı değildir. Arka planda bir metronom çalıyor gibi geliyor. Bence bu melodilerin karakteri var.


Vay, bu bir müzik kutusu gibi geliyor ... Gerçekten güzel!
Beta Decay,

5

Python 3

Bunu denemek zorundaydım.

Bu, bir sekmeyi piyano tarafından çalındığı şekilde bir midi dosyasına dönüştürür. Bir piyanoda bükme ipi nasıl yapılacağı hakkında hiçbir fikrim yok, bu yüzden bunu yapamaz, ancak çekiç çekme ve çekme işlemleri basittir.

: Öyle gibi Test dosyalarını oluşturulan saniyede tek not uzunluğudur.$ python3 tab.py The-weight.txt 0.140.14

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

Kod da github'da, https://github.com/Mattias1/ascii-tab , ki burada OP tarafından sağlanan örneklerin sonucunu da yükledim. Ben de kendi sekmelerimden bazılarında denedim. Piyanonun çaldığını duymak çok garip ama fena değil.

Örnekler:

Bazı doğrudan bağlantılar ekledim, ancak ne kadar süre kaldıklarından emin değilim, bu yüzden eski indirme bağlantılarını da koruyacağım.

  1. Ağırlık veya oyun
  2. Genç ruh gibi kokuyor ya da oyun oynuyor
  3. Yıldız yaylı afiş veya oyun
  4. Matty'nin müziği ya da çal
  5. dm melodi veya çal

Ve Matty'nin ezgisinden bir sekme (en sevdiğim) aşağıda:

    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
Vay, 756 BPM ?! Umarım bu son ritim değildir ...
Beta Çürüğü

Haha, ben biraz hile yapıyorum. 2/3Bu 'vuruşların' aslında çizgi vardır.
Matty,

Woah, Matty'nin şarkısı kulağa hoş geliyor. Gitarda nasıl bir şey?
Beta Decay

1
Teşekkürler @ BetaDecay, bir zamanlar yaptığım bir melodi, Tommy Emmanuel'in mavi ayından ( youtube.com/watch?v=v0IY3Ax2PkY ) ilham alındı . Ama nasıl yaptığı kadar iyi gelmiyor.
Matty,

4

Java Script

Not: Web Geliştirme Ses Kiti'ni kullanır; Bu IE liginin çıkış yolu; Google Chrome'da test edildi

Sekmeleri textareaya yerleştirebilirsiniz. IE, Matty'nin melodilerini, textarea'daki (notların üzerindeki harfler ile) textarea'daki mesajına koyabilir ve yine de doğru şekilde ayrıştırır.

Programı Çalıştırmak İçin Tıklayın

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

Bu şarkıyı tanımlayabilir misin?


1
Gibi karakterlerde çöküyor | / b h p. Neden sadece onları değiştirmek için küçük bir dize ayrıştırma yapmıyorsunuz -? Bu kulağa oldukça iyi gelecek ve işe yarıyor. (Belki bir giriş kutusu kullanarak yeni hatlara bölünebilir.). Bu, oynamak için eğlenceli bir senaryo yapacaktır.
Matty,

Yapmayı planladığım şey, daha önce hiç yapmadım.
Grant Davis

Katılıyorum, her dize için farklı çizgi bir acı ama aksi halde kulağa hoş geliyor
Beta Decay

Hata, gönderiyi düzenlemeden önce giriş yapmayı unuttum.
Grant Davis,


2

Java

Bu program bir tabloyu 16-bit WAV formatına dönüştürür.

İlk olarak, bir sürü tablat ayrıştırma kodu yazdım. Ayrıştırmamın tamamen doğru olup olmadığından emin değilim, ama sorun değil. Ayrıca, veriler için daha fazla doğrulama kullanabilirdi.

Ondan sonra sesi üretmek için kod yaptım. Her dize ayrı ayrı üretilir. Program mevcut frekans, genlik ve faz takip eder. Daha sonra oluşan bağıl genliklere sahip frekans için 10 üst ton oluşturur ve bunları toplar. Son olarak, dizeler birleştirilir ve sonuç normalleştirilir. Sonuç, ultra basit formatı için kullandığım WAV sesi olarak kaydedildi (kitaplık kullanılmadı).

Çekiçleme ( h) ve çekerek ( "destekler" )pOnları görmezden alarak ) çekiyorlar, çünkü onları çok farklı kılmak için gerçekten zamanım olmadı. Sonuç olarak, biraz gitar gibi geliyor (Audacity'deki gitarımı analiz ederek birkaç saatimi harcadım).

Ayrıca, bükülmeyi ( b), serbest bırakmayı ( r) ve kaydırmayı ( /ve \değiştirilebilir) destekler.xdize susturma olarak uygulanır.

Kodları başında sabitleri ayarlamayı deneyebilirsiniz. Özellikle alçaltma silenceRategenellikle daha iyi kaliteye yol açar.

Örnek sonuçlar

Kod

Ben herhangi bir Java başlayanlar uyarmak istiyorum: do not bu koddan bir şey öğrenmeye çalışın, bu korkunç yazmış. Ayrıca, hızlı bir şekilde ve 2 oturumda yazılmıştır ve bir daha hiç kullanılması gerekmediğinden yorum yazmamıştır. (Biraz sonra ekleyebilirsiniz: 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;
    }
}

Bunu belirtmediğimi biliyorum, ancak diğer cevaplarda insanların beğenebileceği bazı test durumları gönderebilir misiniz?
Beta Decay

@BetaDecay Cevabımı güncelledim, şimdi bir sürü test etti
PurkkaKoodari

Bu bağlantılar çalışmıyor: /
Beta Decay

@BetaDecay Kullanmadığım bir tarayıcının gizli modunda başka bir bağlantıyı iki kez kontrol ettim. En azından benim için çalışıyorlar.
PurkkaKoodari

Güzel, üssünün duyması zor olsa da, Matty'nizin versiyonunu çok seviyorum.
Matty,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.