Chơi một bài hát cho tôi


23

Thử thách

Cho tablature guitar, bạn phải xuất bài hát được đại diện bởi tab. Điều này có thể là loa của máy tính của bạn hoặc tệp âm thanh (.wav, .mp3, .midi, .aiff, v.v.). Cũng sẽ có một đầu vào thứ hai cho thời gian.

Các tab có thể được nhập qua một tệp hoặc chuyển thẳng đến STDIN. Tab này sẽ ở dạng ASCII .

Thông số kỹ thuật

Tất cả các tab dành cho 6 cây guitar sáu dây với điều chỉnh E tiêu chuẩn: E2 (82,41 Hz), A2 (110,00 Hz), D3 (146,83 Hz), G3 (196,00 Hz), B3 (246,94 Hz), E4 (329,63 Hz).

Các kỹ thuật duy nhất (ngoài việc chọn bình thường) bạn phải phục vụ là:

  • Uốn (đây sẽ luôn là một nửa uốn cong)
  • Võng trên
  • Kéo ra
  • Trượt lên / xuống

Vì bạn không thể tổng hợp âm thanh của chuỗi bị tắt tiếng, hãy coi xlà a -.

Khi uốn, xuất ra quá trình chuyển đổi hoàn toàn từ unbent sang chuỗi sang uốn cong để unbent lại.

Đầu vào thứ hai sẽ là thời gian mà mỗi biểu tượng trên tab thể hiện bằng giây. Ví dụ:

Đối với đầu vào:

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

Với thời gian 0.5, vì có 3các cột biểu tượng (nhưng không có ghi chú), tệp âm thanh được xuất ra là ( 3*0.5=1.5) 1.5giây im lặng.

Tab ví dụ

1 - Trọng lượng (Jack White, Trang Jimmy + Phiên bản Edge)

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 - Mùi như tinh thần tuổi teen

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

3 - Biểu ngữ đốm sao

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
Tôi đã thêm một vài vị trí thập phân vào tần số của bạn. Cho rằng một nửa cung = 1 băn khoăn là tỷ lệ 1.059463: 1 (nghĩa là chênh lệch khoảng 6%) điều chỉnh đến 1Hz gần nhất là không đủ chính xác để có được âm thanh đồng điệu tốt. Tất nhiên là một cuộc thi phổ biến, điều chỉnh kém có thể được chấp nhận nhưng nó sẽ không thắng.
Cấp sông St

Cuộc thi rất sáng tạo! Sau khi tôi xem liên kết đến mẫu ASCII, tôi có thể hiểu ví dụ 2 (vì tôi đã nghe bài hát này), nhưng vì tôi không biết guitar, tôi nghĩ rằng thử thách có một đường cong học tập cao. Tôi cũng có ít kinh nghiệm về thao tác âm thanh ngoài việc sử dụng Audacity cơ bản.
mbomb007

MIDI có được tính là "tệp âm thanh" không?
orlp

@orlp Vâng, đúng vậy
Beta Decay

1
Vâng để tham khảo trong tương lai: v * (2 ^ (f / 12)) = x; v = tần số của chuỗi; f = Fret (số trên tab); x = tần số phát; Các tab cũng không cho bạn biết độ dài của một ghi chú; chương trình của bạn cần phải thông minh.
Cấp cho Davis

Câu trả lời:


7

MATLAB

Đây là loại chưa hoàn thành. Tôi đã sử dụng một phương pháp nhanh và bẩn để làm cho âm thanh dễ dàng nhất có thể. Phương pháp tôi đã sử dụng gây khó khăn cho việc thực hiện uốn / búa (tôi cũng chưa bao giờ nghe những từ đó trong ngữ cảnh này trước đây).

Đã nói tất cả những điều đó, tập lệnh này sẽ đọc trong một tệp văn bản có tên là "input.txt" chứa tab ascii theo yêu cầu và phát bài hát.

% thời gian
t = 0,25; Tất nhiên, dòng này có thể là 't = input (' time: ');
        % nếu bạn tạo ra giá trị khổng lồ sao cho t * 8192 không phải là số nguyên, một số
        % công cụ sẽ thất bại
% tần số và các biến phụ để cho phép một số sự lười biếng sau này
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;
% này sẽ lưu trữ bài hát theo cách thân thiện với máy tính hơn
bài hát = số không (1,6);
Hàm% để lấy tần số từ v = tần số và f = băn khoăn
w = @ (v, f) v * (2 ^ (f / 12));
% nhận đầu vào và bắt đầu vòng lặp lớn
file = fopen ('input.txt');
dòng = fgetl (tập tin);
trong khi isar (dòng)
    % ký tự đầu tiên của dòng sẽ cho chúng ta tần số dòng
    lfreqv = eval (dòng (1)); %tần số
    lfreqN = eval ([dòng (1), 'N']); % chỉ số ngang của tần số
    % bắt đầu vòng lặp nhỏ trên mỗi dòng
    cho k = 3: (chữ số (dòng)) - 1
        if (strcmp (dòng (k), '-')) || (strcmp (dòng (k), '|')) || (strcmp (dòng (k), 'h')) || (strcmp (dòng (k), 'b'))
            bài hát (k-2, lfreqN) = 0;
        khác
            bài hát (k-2, lfreqN) = w (lfreqv, double (dòng (k)));
        kết thúc
    kết thúc
    dòng = fgetl (tập tin);
kết thúc
fclose (tập tin);
% này sẽ giữ bài hát
điều chỉnh = [];
vols = số không (1,6);
playf = số không (1,6);
cho bài hát Index = 1: kích thước (bài hát, 1)
    ctune = [];
    cho k = 1: 6
        if song (song Index, k) == 0
            vols (k) = 2 * vols (k) / 4;
        khác
            vols (k) = 1;
            playf (k) = song (song Index, k);
        kết thúc
        ctune (k, 1: t * 8192) = vols (k) * sin (0,5 * pi * playf (k) * (1: (t * 8192)) / 8192);
    kết thúc
    chỉnh = [chỉnh ctune];
kết thúc
soundsc (tổng (giai điệu));

Đây là một liên kết đến âm thanh của đầu vào thử nghiệm đầu tiên.

Đây là một liên kết đến âm thanh của đầu vào thử nghiệm thứ ba. (Star Spangled Banner hoặc Ice Cream Truck?)

Đầu vào thử nghiệm thứ hai nghe có vẻ khá tệ đối với tôi, nhưng điều đó có thể là do nó sử dụng rất nhiều bs và hs mà kịch bản bỏ qua.

Như bạn có thể nghe thấy, đầu ra không hoàn toàn giống với chất lượng ban đầu. Nó giống như có một âm thanh phát trong nền. Tôi nghĩ những giai điệu này có tính cách.


Wow, nghe có vẻ như một hộp nhạc ... Thật tuyệt!
Beta Decay

5

Con trăn 3

Tôi đã phải thử cái này

Điều này chuyển đổi một tab thành một tập tin midi như được chơi bởi một cây đàn piano. Tôi không biết làm thế nào để thực hiện một chuỗi uốn cong trên một cây đàn piano, vì vậy nó không thể làm điều đó, nhưng việc gõ búa và kéo dây rất đơn giản.

Tôi tạo ra các tập tin thử nghiệm như vậy: $ python3 tab.py The-weight.txt 0.14nơi 0.14là chiều dài của một nốt nhạc trong vài giây.

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

Mã này cũng có trên github, https://github.com/Mattias1/ascii-tab , nơi tôi cũng đã tải lên kết quả của các ví dụ do OP cung cấp. Tôi cũng đã thử nó trên một số tab của riêng tôi. Nghe một cây đàn piano chơi nó khá lạ, nhưng nó không tệ.

Ví dụ:

Tôi đã thêm một số liên kết trực tiếp, nhưng không chắc chúng tồn tại bao lâu, vì vậy tôi cũng sẽ giữ các liên kết tải xuống cũ.

  1. Trọng lượng , hoặc chơi
  2. Có mùi như tinh thần tuổi teen , hoặc chơi
  3. Sao băng biểu ngữ , hoặc chơi
  4. Giai điệu của Matty , hay chơi
  5. dm điều chỉnh , hoặc chơi

Và tab từ giai điệu của Matty (yêu thích của tôi) bên dưới:

    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
Ái chà, 756 BPM?! Tôi hy vọng đó không phải là nhịp cuối cùng ...
Beta Decay

Haha, tốt, tôi gian lận một chút. 2/3trong số những 'nhịp đập' trên thực tế là dấu gạch ngang.
Matty

Woah, giai điệu của Matty nghe khá tuyệt. Nó giống như thế nào trên một cây đàn guitar?
Beta Decay

1
Cảm ơn @BetaDecay, đó là một giai điệu tôi từng thực hiện, (đường cơ sở) lấy cảm hứng từ mặt trăng xanh của Tommy Emmanuel ( youtube.com/watch?v=v0IY3Ax2PkY ). Nhưng nó không có vẻ tốt bằng một nửa cách anh ấy làm điều đó.
Matty

4

Tập lệnh Java

Lưu ý: Sử dụng Bộ phát triển âm thanh Web; Đây là cách thoát khỏi Liên minh IE; Đã thử nghiệm trên Google Chrome

Bạn có thể đặt các tab trong textarea. IE bạn có thể đặt giai điệu của Matty từ bài đăng của Matty trong textarea (với các chữ cái trên ghi chú) và nó vẫn sẽ phân tích chính xác.

Bấm để chạy chương trình

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

Bạn có thể xác định bài hát này?


1
Nó đâm vào nhân vật như thế nào | / b h p. Tại sao không chỉ cần phân tích cú pháp chuỗi nhỏ để thay thế chúng -? Điều đó sẽ nghe khá ok và nó hoạt động. (Và có thể phân chia trên các dòng mới bằng cách sử dụng một hộp đầu vào.). Điều đó sẽ làm cho điều này trở thành một kịch bản thú vị để chơi với.
Matty

Đó là những gì tôi đã dự định làm, tôi chỉ không bao giờ có được nó.
Cấp Davis

Tôi đồng ý, dòng khác nhau cho mỗi chuỗi là một nỗi đau nhưng nếu không thì nó có vẻ tốt
Beta Decay

Rất tiếc quên đăng nhập trước khi chỉnh sửa bài.
Cấp Davis

Tôi nhận ra giai điệu nhưng tôi không thể đặt tên cho nó ... Mặc dù nghe có vẻ hay
Beta Decay

2

Java

Chương trình này chuyển đổi định dạng thành định dạng WAV 16 bit.

Đầu tiên, tôi đã viết một loạt các mã phân tích cú pháp. Tôi không chắc nếu phân tích cú pháp của tôi là hoàn toàn chính xác, nhưng tôi nghĩ rằng nó ổn. Ngoài ra, nó có thể sử dụng xác nhận nhiều hơn cho dữ liệu.

Sau đó, tôi tạo mã để tạo âm thanh. Mỗi chuỗi được tạo riêng. Chương trình theo dõi tần số, biên độ và pha hiện tại. Sau đó, nó tạo ra 10 âm bội cho tần số với biên độ tương đối được tạo thành và thêm chúng lên. Cuối cùng, các chuỗi được kết hợp và kết quả được chuẩn hóa. Kết quả được lưu dưới dạng âm thanh WAV, mà tôi đã chọn cho định dạng cực kỳ đơn giản (không sử dụng thư viện).

Nó "hỗ trợ" búa ( h) và kéo ( p) bằng cách bỏ qua chúng vì tôi thực sự không có thời gian để làm cho chúng nghe quá khác biệt. Mặc dù vậy, kết quả nghe hơi giống một cây đàn guitar (đã dành vài giờ để phân tích cây đàn guitar của tôi trong Audacity).

Ngoài ra, nó hỗ trợ uốn ( b), giải phóng ( r) và trượt ( /\, có thể hoán đổi cho nhau). xđược thực hiện như tắt tiếng chuỗi.

Bạn có thể thử điều chỉnh các hằng số khi bắt đầu mã. Đặc biệt là hạ thấp silenceRatethường dẫn đến chất lượng tốt hơn.

Kết quả ví dụ

Mật mã

Tôi muốn cảnh báo bất kỳ người mới bắt đầu Java: Bạn không cố gắng học hỏi bất cứ điều gì từ mã này, nó khủng khiếp bằng văn bản. Ngoài ra, nó được viết nhanh chóng và trong 2 phiên và nó không có nghĩa là sẽ được sử dụng một lần nữa nên nó không có ý kiến. (Có thể thêm một số sau: 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;
    }
}

Tôi biết tôi đã không chỉ định nó, nhưng bạn có thể đăng một số trường hợp thử nghiệm mà mọi người có thể nghe giống như trong các câu trả lời khác không?
Beta Decay

@BetaDecay Cập nhật câu trả lời của tôi, hiện có một loạt các bài kiểm tra
PurkkaKoodari

Các liên kết đó không hoạt động: /
Beta Decay

@BetaDecay Tôi đã kiểm tra lại trên một kết nối khác trong chế độ ẩn danh của trình duyệt tôi không sử dụng. Họ làm việc cho tôi, ít nhất.
PurkkaKoodari

Thật tuyệt, tôi thích phiên bản Mattys của bạn rất nhiều, mặc dù cơ sở đôi khi rất khó nghe.
Matty
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.