나를 위해 노래를 재생


23

도전

기타 타 브러 처가 주어지면 탭으로 표시되는 노래를 출력해야합니다. 컴퓨터의 스피커 나 오디오 파일 (.wav, .mp3, .midi, .aiff 등) 일 수 있습니다. 타이밍을위한 두 번째 입력도 있습니다.

탭은 파일을 통해 입력하거나 STDIN으로 바로 입력 할 수 있습니다. 탭은 ASCII 형식 입니다.

투기

모든 탭은 E2 (82.41Hz), A2 (110.00Hz), D3 (146.83Hz), G3 (196.00Hz), B3 (246.94Hz), E4 (329.63Hz)의 표준 E 튜닝 기능을 갖춘 6 개의 6 줄 기타 용입니다.

당신이 제공해야 할 유일한 기술 (일반 피킹 이외에)은 다음과 같습니다.

  • 굽힘 (항상 반음 굽힘)
  • 망치질
  • 당기기
  • 상하 슬라이딩

음소거 된 문자열의 사운드를 합성 할 수 없으므로로 취급 x하십시오 -.

구부릴 때 구부러지지 않은 상태에서 문자열로 전체 구부러진 상태를 다시 구부러진 상태로 다시 출력하십시오.

두 번째 입력은 탭의 각 기호가 초 단위로 표시되는 시간입니다. 예를 들면 다음과 같습니다.

입력의 경우 :

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

타이밍 0.5때문에 거기에 3출력 된 오디오 파일입니다 (기호 (하지만 노트)의 열 3*0.5=1.5) 1.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.059463 : 1의 비율 (즉, 약 6 %의 차이)을 가장 가까운 1Hz로 조정하는 것이 좋은 인-튠 사운드를 얻기에 충분하지 않습니다. 물론 인기 콘테스트 인 경우, 좋지 않은 튜닝은 허용되지만이기는 것은 아닙니다.
레벨 리버 St

매우 창의적인 경연 대회! ASCII 형식에 대한 링크를 살펴본 후 예제 2 (노래를 들었으므로)를 이해할 수 있지만 기타를 모르기 때문에 난이도가 높은 것으로 나타났습니다. 또한 Audacity의 기본 사용법 이외의 오디오 조작 경험이 거의 없습니다.
mbomb007

MIDI는 '오디오 파일'로 간주됩니까?
orlp

@orlp 예, 그렇습니다
Beta Decay

1
향후 참조를 위해 잘 : v * (2 ^ (f / 12)) = x; v = 스트링의 빈도; f = 프렛 (탭의 숫자); x = 재생 주파수; 탭은 또한 메모의 길이를 나타내지 않습니다. 당신의 프로그램은 똑똑해야합니다.
그랜트 데이비스

답변:


7

MATLAB

이것은 끝나지 않은 것입니다. 오디오를 최대한 쉽게 만들기 위해 빠르고 더러운 방법을 사용했습니다. 내가 사용한 방법으로 벤딩 / 해머링을 구현하기가 어려워졌다.

이 스크립트는 모두 필요에 따라 ascii-tab을 포함하는 "inputs.txt"라는 텍스트 파일을 읽고 노래를 재생합니다.

%타이밍
t = 0.25; 물론이 줄은 't = input ('timing : ');
        t * 8192가 정수가 아닌 값을 만들면 일부
        % stuff가 실패합니다
나중에 게으름을 허용하는 % 주파수 및 추가 변수
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에서 주파수를 얻는 % function
w = @ (v, f) v * (2 ^ (f / 12));
% 입력을 받고 큰 루프를 시작
파일 = fopen ( 'input.txt');
라인 = fgetl (파일);
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'))
            노래 (k-2, lfreqN) = 0;
        그밖에
            노래 (k-2, lfreqN) = w (lfreqv, double (line (k)));
        종료
    종료
    라인 = fgetl (파일);
종료
fclose (파일);
이 노래를 개최합니다
조정 = [];
부피 = 0 (1,6);
playf = 제로 (1,6);
songIndex = 1 : size (노래, 1)
    ctune = [];
    k = 1 : 6의 경우
        song (songIndex, k) == 0 인 경우
            vols (k) = 2 * vols (k) / 4;
        그밖에
            vols (k) = 1;
            playf (k) = 노래 (songIndex, k);
        종료
        ctune (k, 1 : t * 8192) = vols (k) * sin (0.5 * pi * playf (k) * (1 : (t * 8192)) / 8192);
    종료
    튠 = [tune ctune];
종료
soundsc (sum (tune));

다음은 첫 번째 테스트 입력의 사운드에 대한 링크입니다.

다음은 세 번째 테스트 입력의 사운드에 대한 링크입니다. (스타 스팽글 배너 또는 아이스크림 트럭?)

두 번째 테스트 입력은 나에게 아주 나쁜 소리지만 많이 사용하기 때문에 그것은있을 수 bs와 h의 스크립트 무시합니다.

당신이들을 수 있듯이, 출력은 원본과 품질이 다릅니다. 백그라운드에서 메트로놈이 재생되는 것처럼 들립니다. 이 곡들에는 성격이 있다고 생각합니다.


와우, 그것은 뮤직 박스처럼 들린다. .. 정말로 멋지다!
Beta Decay

5

파이썬 3

나는 이것을 시도해야했다.

피아노가 연주 할 때 탭을 미디 파일로 변환합니다. 피아노에서 현을 구부리는 방법을 모릅니다. 그렇게 할 수는 없지만 해머 온과 풀 오프는 간단합니다.

: 그래서 같은 테스트 파일 생성 초 단일 노트의 길이입니다.$ python3 tab.py The-weight.txt 0.140.14

from midiutil.MidiFile3 import MIDIFile
import sys

# Read the relevant lines of the file
lines = []
if len(sys.argv) > 1:
    filename = sys.argv[1]
    try:
        beats_per_minute = 60 / float(sys.argv[2])
    except:
        beats_per_minute = 756
else:
    filename = 'mattys-tune.txt'
    beats_per_minute = 756
with open(filename) as f:
    for line in f:
        if len(line) > 3 and (line[1] == '|' or line[2] == '|'):
            line = line.replace('\n', '')
            lines.append(line)
assert len(lines) % 6 == 0

# Initialize the MIDIFile object (with 1 track)
time = 0
duration = 10
volume = 100
song = MIDIFile(1)
song.addTrackName(0, time, "pianized_guitar_tab.")
song.addTempo(0, time, beats_per_minute)

# The root-pitches of the guitar
guitar = list(reversed([52, 57, 62, 67, 71, 76])) # Assume EADGBe tuning
def add_note(string, fret):
    song.addNote(0, string, guitar[string] + fret, time, duration, volume)

# Process the entire tab
for current in range(0, len(lines), 6):  # The current base string
    for i in range(len(lines[current])): # The position in the current string
        time += 1
        for s in range(6):               # The number of the string
            c = lines[current + s][i]
            try: next_char = lines[current + s][i + 1]
            except: next_char = ''
            if c in '-x\\/bhp':
                # Ignore these characters for now
                continue
            elif c.isdigit():
                # Special case for fret 10 and higher
                if next_char.isdigit():
                    c += next_char
                    lines[current + s] = lines[current + s][:i+1] + '-' + lines[current + s][i+2:]
                # It's a note, play it!
                add_note(s, int(c))
            else:
                # Awww
                time -= 1
                break

# And write it to disk.
def save():
    binfile = open('song.mid', 'wb')
    song.writeFile(binfile)
    binfile.close()
    print('Done')
try:
    save()
except:
    print('Error writing to song.mid, try again.')
    input()
    try:
        save()
    except:
        print('Failed!')

코드는 github에도 https://github.com/Mattias1/ascii-tab 이며 OP에서 제공 한 예제의 결과도 업로드했습니다. 나는 또한 내 자신의 탭에서 시도했다. 피아노가 연주하는 것을 듣는 것은 매우 이상하지만 나쁘지 않습니다.

예 :

직접 링크를 추가했지만 시간이 얼마인지 확실하지 않으므로 이전 다운로드 링크도 유지합니다.

  1. 무게 또는 놀이
  2. 십대 정신 냄새 또는 놀이
  3. 별이 튀어 나온 배너 또는 재생
  4. 매티의 조정 , 또는 플레이
  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

Woah, Matty의 곡은 매우 시원하게 들립니다. 기타는 어떻습니까?
Beta Decay

1
@BetaDecay에게 감사드립니다. 토미 엠마누엘의 푸른 달 ( youtube.com/watch?v=v0IY3Ax2PkY ) 에서 영감을 얻은 노래 입니다. 그러나 그가하는 방식만큼 반음은 들리지 않습니다.
Matty

4

자바 스크립트

참고 : 웹 개발 오디오 키트를 사용합니다. 이것은 IE의 리그에서 벗어난 방법입니다. Chrome에서 테스트

텍스트 영역에 탭을 넣을 수 있습니다. IE에서는 Matty의 게시물에서 Matty의 노래를 텍스트 영역 (메모 위에 글자와 함께)에 넣을 수 있으며 여전히 올바르게 구문 분석됩니다.

프로그램을 실행하려면 클릭하십시오

자바 스크립트 :

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

내가하려고했던 일에 나는 결코 그 일을하지 않았다.
그랜트 데이비스

각 줄의 다른 줄은 고통이지만 그렇지 않으면 좋은 소리라고 생각합니다
Beta Decay

게시물을 수정하기 전에 로그인하지 못했습니다.
그랜트 데이비스

나는 노래를 인식하지만 그것에 이름을 넣을 수 없습니다 ... 멋진 소리
베타 Decay

2

자바

이 프로그램은 tablature를 16 비트 WAV 형식으로 변환합니다.

먼저, 나는 tablature 파싱 코드를 많이 썼습니다. 구문 분석이 완전히 올바른지 확실하지 않지만 괜찮습니다. 또한 데이터에 대해 더 많은 유효성 검사를 사용할 수 있습니다.

그 후 오디오를 생성하는 코드를 만들었습니다. 각 문자열은 별도로 생성됩니다. 프로그램은 현재 주파수, 진폭 및 위상을 추적합니다. 그런 다음 메이크업 된 상대 진폭으로 주파수에 대해 10 개의 오버톤을 생성하고 합산합니다. 마지막으로 문자열이 결합되고 결과가 정규화됩니다. 결과는 WAV 오디오로 저장되며 매우 간단한 형식 (라이브러리를 사용하지 않음)으로 선택했습니다.

소리가 너무 다르게 들릴 시간이 없었기 때문에 무시하고 망치는 것 ( h)과 당기는 것 ( p)을 "지원"합니다 . 결과는 기타처럼 들립니다 (Audacity에서 내 기타를 분석하는 데 몇 시간이 걸렸습니다).

또한 굽힘 ( b), 해제 ( r) 및 슬라이딩 ( /\, 교체 가능)을 지원합니다. x문자열을 음소거하는 것으로 구현됩니다.

코드 시작 부분에서 상수를 조정할 수 있습니다. 특히 낮추면 silenceRate품질이 향상되는 경우가 많습니다.

결과 예

코드

내가 어떤 자바 초보자에게 경고하고자 : 할 수 없습니다 이 코드에서 아무것도 배우려고, 그것은 정말 쓰여. 또한 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

해당 링크가 작동하지 않습니다 : /
Beta Decay

@BetaDecay 사용하지 않는 브라우저의 시크릿 모드에서 다른 연결을 두 번 확인했습니다. 그들은 적어도 나를 위해 일합니다.
PurkkaKoodari

베이스는 듣기가 어렵지만 Mattys 버전이 많이 조정되는 것을 좋아합니다.
Matty
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.