เล่นเพลงให้ฉัน


23

ท้าทาย

รับ tablature กีตาร์คุณต้องส่งออกเพลงที่แสดงโดยแท็บ นี่อาจเป็นลำโพงของคอมพิวเตอร์ของคุณหรือไฟล์เสียง (.wav, .mp3, .midi, .aiff ฯลฯ ) นอกจากนี้ยังมีอินพุตที่สองสำหรับกำหนดเวลา

แท็บอาจป้อนข้อมูลผ่านไฟล์หรือตรงไปที่ STDIN แท็บจะอยู่ในรูปแบบ ASCII

สเป็ค

แท็บทั้งหมดใช้สำหรับกีต้าร์สตริง 6 ตัวที่มีการจูน E มาตรฐาน: E2 (82.41 Hz), A2 (110.00 Hz), D3 (146.83 Hz), G3 (196.00 Hz), B3 (246.94 Hz), E4 (329.63 Hz)

เทคนิคเดียว (นอกเหนือจากการเลือกตามปกติ) ที่คุณต้องทำคือ:

  • ดัด (จะเป็นครึ่งโค้งเสมอ)
  • ตอกบน
  • ดึงออก
  • เลื่อนขึ้น / ลง

เนื่องจากคุณไม่สามารถสังเคราะห์เสียงของสตริงปิดเสียงการรักษาเป็นx-

เมื่อดัดงอให้เอาท์พุทช่วงการเปลี่ยนภาพจาก unbent เป็น string ไปเป็น bent เป็น unent อีกครั้ง

อินพุตที่สองจะเป็นเวลาที่แต่ละสัญลักษณ์บนแท็บแสดงเป็นวินาที ตัวอย่างเช่น:

สำหรับอินพุต:

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

ด้วยการกำหนดเวลา0.5เนื่องจากมี3คอลัมน์ของสัญลักษณ์ (แต่ไม่มีโน้ต) ไฟล์เสียงที่ส่งออกคือ ( 3*0.5=1.5) 1.5วินาทีของความเงียบ

แท็บตัวอย่าง

1 - The Weight (แจ็คไวท์, 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 - กลิ่นเหมือนวิญญาณวัยรุ่น

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
ฉันเพิ่มทศนิยมอีกสองสามถึงความถี่ของคุณ ระบุว่าหนึ่ง semitone = 1 ทำให้ไม่สบายใจเป็นอัตราส่วนของ 1.059463: 1 (นั่นคือความแตกต่างประมาณ 6%) การปรับไปที่ 1Hz ที่ใกล้ที่สุดนั้นไม่แม่นยำพอที่จะได้เสียงที่ดี แน่นอนว่าเป็นการประกวดความนิยมการปรับจูนที่ไม่ดีอาจยอมรับได้ แต่มันจะไม่ชนะ
ระดับแม่น้ำเซนต์

การประกวดที่สร้างสรรค์มาก! หลังจากที่ฉันดูลิงก์ไปยังแบบฟอร์ม ASCII ฉันสามารถเข้าใจตัวอย่างที่ 2 (เนื่องจากฉันเคยได้ยินเพลง) แต่เนื่องจากฉันไม่รู้จักกีต้าร์ฉันจึงคิดว่าความท้าทายมีช่วงการเรียนรู้สูง ฉันยังมีประสบการณ์เล็กน้อยกับการปรับแต่งเสียงอื่น ๆ นอกเหนือจากการใช้ความกล้าขั้นพื้นฐาน
mbomb007

MIDI ถือเป็น 'ไฟล์เสียง' หรือไม่
orlp

@orlp ใช่มันทำ
Beta Decay

1
ดีสำหรับการอ้างอิงในอนาคต: v * (2 ^ (f / 12)) = x; v = ความถี่ของสตริง; f = Fret (ตัวเลขบนแท็บ); x = เล่นความถี่ แท็บยังไม่ได้บอกความยาวของโน้ต โปรแกรมของคุณต้องฉลาด
Grant Davis

คำตอบ:


7

MATLAB

นี่คือการเรียงลำดับของยังไม่เสร็จ ฉันใช้วิธีที่รวดเร็วและสกปรกเพื่อทำให้เสียงง่ายที่สุดเท่าที่จะทำได้ วิธีที่ฉันใช้ทำให้ยากต่อการใช้การดัด / การตอก (ฉันไม่เคยได้ยินคำพูดเหล่านั้นในบริบทนี้มาก่อน)

ต้องบอกว่าทั้งหมดนี้สคริปต์นี้จะอ่านในไฟล์ข้อความที่เรียกว่า "inputs.txt" ที่มีแท็บ ascii ตามที่ต้องการและเล่นเพลง

% ระยะเวลา
t = 0.25; % แน่นอนบรรทัดนี้อาจเป็น 't = input (' timing: ');
        % ถ้าคุณสร้างค่าที่ไม่หวั่นเกรงเช่น t * 8192 ไม่ใช่จำนวนเต็มบางตัว
        เนื้อหาจะล้มเหลว
% ความถี่และตัวแปรเพิ่มเติมเพื่อให้ความขี้เกียจในภายหลัง
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;
นี้จะเก็บเพลงในแบบที่เป็นมิตรกับคอมพิวเตอร์มากขึ้น
เพลง = ศูนย์ (1,6);
ฟังก์ชัน% เพื่อรับความถี่จาก v = frequency และ f = fret
W = @ (v, f) โวลต์ * (2 ^ (f / 12));
รับอินพุตและเริ่มลูปขนาดใหญ่
file = fopen ('input.txt');
บรรทัด = fgetl (ไฟล์);
ในขณะที่ ischar (บรรทัด)
    % อักขระแรกของบรรทัดจะให้ความถี่ของสาย
    lfreqv = eval (บรรทัด (1)); %ความถี่
    lfreqN = eval ([บรรทัด (1), 'N']); ดัชนีแนวนอนของความถี่
    % เริ่มวนซ้ำเล็ก ๆ ในแต่ละบรรทัด
    สำหรับ k = 3: (numel (บรรทัด)) - 1
        if (strcmp (บรรทัด (k), '-')) || (strcmp (บรรทัด (k), '|')) | | (strcmp (บรรทัด (k), 'h')) || (strcmp (บรรทัด (k), 'B'))
            เพลง (k-2, lfreqN) = 0;
        อื่น
            เพลง (k-2, lfreqN) = w (lfreqv, double (บรรทัด (k)));
        ปลาย
    ปลาย
    บรรทัด = fgetl (ไฟล์);
ปลาย
fclose (ไฟล์);
นี้จะถือเพลง
เพลง = [];
vols = ศูนย์ (1,6);
playf = ศูนย์ (1,6);
สำหรับ songIndex = 1: ขนาด (เพลง, 1)
    ctune = [];
    สำหรับ k = 1: 6
        ถ้าเพลง (songIndex, k) == 0
            vols (k) = 2 * vols (k) / 4;
        อื่น
            vols (k) = 1;
            playf (k) = เพลง (songIndex, k);
        ปลาย
        ctune (k, 1: T * 8192) = โวส์ (k) * บาป (0.5 * * * * * * * * ปี่ playf (k) * (1: (t * 8192)) / 8192);
    ปลาย
    ปรับแต่ง = [ปรับแต่ง ctune];
ปลาย
soundsc (ผลรวม (ปรับ));

นี่คือลิงค์ไปยังเสียงของอินพุตการทดสอบแรก

นี่คือลิงค์ไปยังเสียงของอินพุตการทดสอบที่สาม (แบนเนอร์แพรวพราวหรือรถบรรทุกไอศกรีม?)

การป้อนข้อมูลการทดสอบที่สองฟังสวยไม่ดีกับผม แต่นั่นอาจจะเป็นเพราะใช้จำนวนมากbและhs ซึ่งละเว้นสคริปต์

อย่างที่คุณสามารถได้ยินได้เสียงที่ออกมานั้นไม่ได้คุณภาพเหมือนต้นฉบับเลยทีเดียว มันฟังดูเหมือนว่ามีเครื่องเมตรอนอมเล่นอยู่ด้านหลัง ฉันคิดว่าเพลงเหล่านี้มีตัวละคร


ว้าวฟังดูเหมือนกล่องดนตรี ... ดีจริงๆ!
Beta Decay

5

Python 3

ฉันต้องลองอันนี้

สิ่งนี้จะแปลงแท็บเป็นไฟล์ midi ที่เล่นโดยเปียโน ฉันไม่รู้ว่าจะทำสายเปียโนอย่างไรมันจึงทำไม่ได้ แต่การตอกและการดึงออกของเปียโนนั้นตรงไปตรงมา

ผมสร้างไฟล์ทดสอบชอบดังนั้น: $ python3 tab.py The-weight.txt 0.14ที่0.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!')

รหัสนี้อยู่ใน GitHub ด้วยเช่นกันhttps://github.com/Mattias1/ascii-tabซึ่งฉันได้อัปโหลดผลลัพธ์ของตัวอย่างที่ให้โดย OP ฉันได้ลองใช้กับแท็บของฉันเองด้วย มันค่อนข้างแปลกที่จะได้ยินเปียโนเล่น แต่มันก็ไม่เลว

ตัวอย่าง:

ฉันเพิ่มลิงก์โดยตรง แต่ไม่แน่ใจว่าจะอยู่ได้นานแค่ไหนดังนั้นฉันจะเก็บลิงค์ดาวน์โหลดเดิมไว้ด้วย

  1. น้ำหนักหรือเล่น
  2. มีกลิ่นเหมือนวิญญาณวัยรุ่นหรือเล่น
  3. ติดดาวแบนเนอร์ที่เล่นแล้วหรือเล่น
  4. เพลงของ Mattyหรือเล่น
  5. ปรับแต่ง dmหรือเล่น

และแท็บจากเพลงของ 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! ฉันหวังว่ามันจะไม่ใช่จังหวะสุดท้าย ...
Beta Decay

ฮ่าฮ่าฉันโกงนิดหน่อย 2/3ของ 'เต้น' เหล่านั้นในความเป็นจริงขีดคั่น
Matty

ว้าวเพลงของแมทตี้ฟังดูเท่ห์ ในกีต้าร์มันคืออะไร?
Beta Decay

1
ขอบคุณ @BetaDecay มันเป็นเพลงที่ฉันเคยทำ (พื้นฐาน) ที่ได้รับแรงบันดาลใจจากดวงจันทร์สีน้ำเงินของ Tommy Emmanuel ( youtube.com/watch?v=v0IY3Ax2PkY ) แต่มันฟังดูไม่ค่อยดีเท่าที่เขาทำ
Matty

4

จาวาสคริปต์

หมายเหตุ: ใช้ชุดพัฒนาเสียงของเว็บ นี่คือทางออกจากลีกของ IE; ผ่านการทดสอบใน Google Chrome

คุณสามารถใส่แท็บใน textarea IE คุณสามารถใส่การปรับแต่งของ Matty จากการโพสต์ของ Matty ใน textarea (ด้วยตัวอักษรเหนือโน้ต) และมันจะยังคงแยกวิเคราะห์ได้อย่างถูกต้อง

คลิกเพื่อเรียกใช้โปรแกรม

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เกิดปัญหาเกี่ยวกับตัวอักษรเช่น ทำไมไม่แยกสตริงเล็ก ๆ เพื่อแทนที่ด้วย-? นั่นจะฟังดูค่อนข้างโอเคและใช้งานได้ (และอาจแบ่งเป็นบรรทัดใหม่โดยใช้ช่องป้อนข้อมูลเดียว) นั่นจะทำให้สคริปต์นี้สนุกกับการเล่น
Matty

นั่นคือสิ่งที่ฉันวางแผนไว้ว่าจะทำฉันไม่เคยเข้าไปใกล้มัน
Grant Davis

ฉันเห็นด้วยบรรทัดที่แตกต่างกันสำหรับแต่ละสายเป็นความเจ็บปวด แต่อย่างอื่นมันฟังดูดี
Beta Decay

โอ๊ะลืมเข้าสู่ระบบก่อนที่จะแก้ไขโพสต์
Grant Davis

ฉันจำการปรับแต่งได้ แต่ฉันไม่สามารถใส่ชื่อได้ ... ฟังดูเจ๋ง
Beta Decay

2

ชวา

โปรแกรมนี้แปลง tablature เป็นรูปแบบ WAV แบบ 16 บิต

ประการแรกฉันเขียนโค้ดการแยกวิเคราะห์แบบแท็บทั้งหมด ฉันไม่แน่ใจว่าการแยกวิเคราะห์ของฉันถูกต้องทั้งหมดหรือไม่ แต่ฉันคิดว่าไม่เป็นไร นอกจากนี้ยังสามารถใช้การตรวจสอบความถูกต้องของข้อมูลได้มากขึ้น

หลังจากนั้นฉันสร้างรหัสเพื่อสร้างเสียง แต่ละสตริงถูกสร้างแยกต่างหาก โปรแกรมติดตามความถี่ปัจจุบันแอมพลิจูดและเฟส จากนั้นจะสร้าง 10 หวือหวาสำหรับความถี่ที่มีแอมพลิจูดสัมพัทธ์ที่สร้างขึ้นและเพิ่มขึ้น ในที่สุดสตริงจะถูกรวมเข้าด้วยกันและผลลัพธ์จะถูกทำให้เป็นมาตรฐาน ผลลัพธ์ถูกบันทึกเป็นเสียง WAV ซึ่งฉันเลือกสำหรับรูปแบบที่เรียบง่ายที่สุด (ไม่มีไลบรารีที่ใช้)

มัน "รองรับ" การตอก ( h) และดึง ( p) โดยไม่สนใจเพราะฉันไม่มีเวลาทำให้เสียงแตกต่างไป ผลที่ได้ออกมาดูเหมือนกีตาร์ (ใช้เวลาหลายชั่วโมงในการวิเคราะห์สเปกตรัมกีตาร์ของฉันด้วยความกล้า)

นอกจากนี้ยังรองรับการดัด ( b), การปล่อย ( r) และการเลื่อน (/และ\เปลี่ยนได้) xถูกนำมาใช้เป็น muting สตริง

คุณสามารถลอง tweaking ค่าคงที่ในการเริ่มต้นของรหัส โดยเฉพาะการลด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;
    }
}

ฉันรู้ว่าฉันไม่ได้ระบุ แต่คุณสามารถโพสต์กรณีทดสอบบางอย่างที่คนฟังในคำตอบอื่นได้หรือไม่?
Beta Decay

@BetaDecay อัปเดตคำตอบของฉันตอนนี้มีการทดสอบมากมาย
PurkkaKoodari

ลิงก์เหล่านั้นใช้ไม่ได้: /
สลายตัวของเบต้า

@BetaDecay ฉันตรวจสอบอีกครั้งในการเชื่อมต่ออื่นในโหมดไม่ระบุตัวตนของเบราว์เซอร์ที่ฉันไม่ได้ใช้ พวกเขาทำงานให้ฉันอย่างน้อย
PurkkaKoodari

ดีฉันชอบรุ่น Mattys ของคุณปรับแต่งมากมายแม้ว่าบางครั้งฐานจะได้ยินยาก
Matty
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.