私のために歌を演奏する


23

チャレンジ

ギターのタブ譜を指定すると、タブで表される曲を出力する必要があります。これは、コンピューターのスピーカーまたはオーディオファイル(.wav、.mp3、.midi、.aiffなど)に対するものです。タイミング用の2番目の入力もあります。

タブは、ファイルを介して入力することも、STDINに直接入力することもできます。タブはASCII形式になりますます。

スペック

すべてのタブは、標準的なEチューニングの6本の6弦ギター用です:E2(82.41 Hz)、A2(110.00 Hz)、D3(146.83 Hz)、G3(196.00 Hz)、B3(246.94 Hz)、E4(329.63 Hz)。

(通常のピッキング以外に)必要な唯一のテクニックは次のとおりです。

  • ベンディング(これは常にハーフトーンベンドになります)
  • Ha打
  • 引っ張っ
  • スライドアップ/ダウン

ミュートされたストリングのサウンドを合成することはできないためx-ます。

曲げるとき、曲げられていない状態から弦への完全な遷移を出力します。

2番目の入力は、タブ上の各シンボルが秒単位で表す時間です。例えば:

入力用:

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

タイミング0.5では3、シンボルの列がある(ただし音符がない)ため、出力されるオーディオファイルは(3*0.5=1.51.5秒の無音になります。

タブの例

1- ウェイト(ジャックホワイト、ジミーページ+エッジエディション)

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- ティーンスピリットのような匂い

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

3- 星条旗

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
周波数に小数点以下の桁をいくつか追加しました。1半音= 1フレットが1.059463:1の比率(つまり、約6%の差)であるとすると、最も近い1Hzへのチューニングは、適切な同調音を得るのに十分正確ではありません。もちろん、人気のあるコンテストであるため、チューニングの不備は許容されるかもしれませんが、勝つことはできません。
レベルリバーセント

非常に創造的なコンテスト!ASCIIフォームへのリンクを見た後、例2(歌を聞いたので)を理解できましたが、ギターを知らないので、この課題には高い学習曲線があると思います。また、Audacityの基本的な使用法以外のオーディオ操作の経験もほとんどありません。
mbomb007

MIDIは「オーディオファイル」としてカウントされますか?
orlp

はい@orlp、それはありません
ベータ崩壊

1
将来の参照用:v *(2 ^(f / 12))= x; v =文字列の頻度。f = Fret(タブ上の数字); x =再生された頻度。タブは、メモの長さも示しません。プログラムはスマートである必要があります。
グラントデイビス

回答:


7

MATLAB

これは一種の未完成です。私は、オーディオをできるだけ簡単にするために、迅速で汚れた方法を使用しました。私が使用した方法では、曲げ/ハンマー加工を実装するのが困難になりました(この文脈でこれらの言葉を聞いたこともありませんでした)。

すべてを述べたが、このスクリプトは、必要に応じてascii-tabを含む「inputs.txt」というテキストファイルを読み込み、曲を再生します。

%タイミング
t = 0.25; もちろん、この行は 't = input(' timing: ');
        %t * 8192が整数でないようにta winky値を作成した場合、
        %stuffは失敗します
後で怠someを許容する頻度と追加の変数
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;
これにより、コンピューターに優しい方法で曲が保存されます
song = zeros(1,6);
v = frequencyおよびf = fretから周波数を取得する%function
w = @(v、f)v *(2 ^(f / 12));
入力を取得して大きなループを開始する
file = fopen( 'input.txt');
line = fgetl(file);
一方、ischar(line)
    行の最初の文字は、行の頻度を示します
    lfreqv = eval(line(1)); %周波数
    lfreqN = eval([line(1)、 'N']); 頻度の水平インデックス
    各行で小さなループを開始します
    k = 3:(numel(line))-1の場合
        if(strcmp(line(k)、 '-'))||(strcmp(line(k)、 '|'))||(strcmp(line(k)、 'h'))||(strcmp(line (k)、 'b'))
            song(k-2、lfreqN)= 0;
        他に
            song(k-2、lfreqN)= w(lfreqv、double(line(k)));
        終わり
    終わり
    line = fgetl(file);
終わり
fclose(ファイル);
これは曲を保持します
tune = [];
vols = zeros(1,6);
playf = zeros(1,6);
songIndex = 1:size(song、1)の場合
    ctune = [];
    k = 1:6の場合
        if song(songIndex、k)== 0
            vols(k)= 2 * vols(k)/ 4;
        他に
            vols(k)= 1;
            playf(k)= song(songIndex、k);
        終わり
        ctune(k、1:t * 8192)= vols(k)* sin(0.5 * pi * playf(k)*(1:(t * 8192))/ 8192);
    終わり
    tune = [tune ctune];
終わり
soundsc(sum(tune));

最初のテスト入力の音声へのリンクを次に示します。

3番目のテスト入力の音声へのリンクを次に示します。(星条旗またはアイスクリームトラック?)

2番目のテスト入力は非常に悪く聞こえましたが、それはスクリプトが無視するbsとhsを多く使用しているためかもしれません。

聞こえるように、出力はオリジナルと全く同じ品質ではありません。バックグラウンドでメトロノームが演奏されているように聞こえます。これらの曲には個性があると思います。


うわー、それはオルゴールのように聞こえます...本当にいいね!
ベータ崩壊

5

Python 3

私はこれを試さなければなりませんでした。

これはタブをピアノで演奏されるMIDIファイルに変換します。ピアノで弦を曲げる方法がわからないので、それはできませんが、ハンマーオンとプルオフは簡単です。

次のようなテストファイルを生成しました。$ python3 tab.py The-weight.txt 0.14ここ0.14で、1つのメモの長さは秒です。

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

コードはgithub(https://github.com/Mattias1/ascii-tab)にもあります。OPで提供されている例の結果もアップロードしました。私も自分のタブのいくつかでそれを試しました。ピアノがそれを弾くのを聞くのは非常に奇妙ですが、悪くはありません。

例:

いくつかの直接リンクを追加しましたが、どれくらいの期間滞在するのかわからないので、古いダウンロードリンクも保持します。

  1. 重さ、または遊び
  2. 十代の精神のように臭い、または遊ぶ
  3. スターは、バナーをsprangled、またはプレイ
  4. マティの曲、またはプレイ
  5. dm tuneまたはplay

そして、以下のMattyの曲(私のお気に入り)のタブ:

    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
うわ、756 BPM ?! 私はそれが...最後の拍ではありません願っています
ベータ崩壊

ハハ、まあ、私は少しチート。2/3それらの「ビート」のうち、実際にはダッシュです。
マティ

うわー、マティーの曲はかなりクールに聞こえます。ギターではどんな感じ?
ベータ崩壊

1
@BetaDecayに感謝します。トミー・エマニュエルのブルームーン(youtube.com/watch?v=v0IY3Ax2PkY)に触発された(ベースライン)私がかつて作った曲です。しかし、それは彼がそれをする方法の半分ほど良く聞こえません。
マティー

4

Javaスクリプト

注:Web Development Audio Kitを使用します。これはIEのリーグから抜け出す方法です。Google Chromeでテスト済み

テキストエリアにタブを配置できます。IEでは、テキスト領域にMattyの投稿からMattyの曲を入れることができ(メモの上に文字があります)、それでも正しく解析されます。

クリックしてプログラムを実行

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

この歌を識別できますか?


1
のようなキャラクターでクラッシュします| / b h p。ちょっとした文字列解析をしてそれらを置き換えるだけでは-どうですか?それはまったく問題ないように聞こえ、動作します。(1つの入力ボックスを使用して改行で分割することもできます。)これで、これは遊ぶための楽しいスクリプトになります。
マティ

私がやろうとしていたことを、私はそれを回避することはできませんでした。
グラントデイビス

私は同意します、各文字列の異なる行は苦痛ですが、それ以外の場合はいいですね
ベータ崩壊

投稿を編集する前にログインを忘れました。
グラントデイビス

私は曲を認識していますが、名前を付けることはできません...かっこいいですね
ベータ崩壊

2

Java

このプログラムは、タブ譜を16ビットWAV形式に変換します。

まず、タブ譜の解析コードを大量に作成しました。解析が完全に正しいかどうかはわかりませんが、大丈夫だと思います。また、データに対してより多くの検証を使用できます。

その後、音声を生成するコードを作成しました。各文字列は個別に生成されます。プログラムは、現在の周波数、振幅、位相を追跡します。次に、構成された相対振幅を持つ周波数の10倍音を生成し、それらを加算します。最後に、文字列が結合され、結果が正規化されます。結果はWAVオーディオとして保存されます。これは、その非常にシンプルな形式(ライブラリを使用しない)のために選択しました。

それらを無視することで、ハンマー(h)とプル(p)を「サポート」します。なぜなら、私は本当にそれらをあまりにも異なる音にする時間がないからです。ただし、結果はギターのように聞こえます(Audacityでギターを分析するのに数時間かかりました)。

また、曲げ(b)、解放(r)、スライド(/および\、交換可能)をサポートします。x文字列のミュートとして実装されます。

コードの先頭で定数を微調整してみてください。特に値を下げると、silenceRateしばしば品質が向上します。

結果の例

コード

Java初心者には警告したい。このコードから何も学ぼうとしないでください。ひどく書かれています。また、2回のセッションで迅速に作成され、二度と使用されることを意図していなかったため、コメントはありません。(後で追加する場合があります: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;
    }
}

私はそれを指定していないことは知っていますが、他の回答のように人々が聞くことができるいくつかのテストケースを投稿できますか?
ベータ崩壊

@BetaDecay私の答えを更新し、現在、テストの束を持っています
PurkkaKoodari

これらのリンクは機能しません:/
ベータ崩壊

@BetaDecay使用していないブラウザのシークレットモードで別の接続を再確認しました。少なくとも彼らは私のために働いています。
-PurkkaKoodari

いいですね、私はあなたのバージョンのマティスが大好きです。ベースは時々聞きにくいです。
マティ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.