矢沢永吉『成りあがり』28歳の記憶と共に ― I LOVE Rock'N'Roll LIFE:A Creative Experiment(Unplugged、歌詞、実験的コード譜)
矢沢永吉★成りあがりに刻まれた「28歳の記録」 伝説のバイブル誕生秘話とキャロル解散の真実
以下2026年5月15日(金)にupさせて頂いた 完成versionです。どうぞ よろしくお願い致します。
以下記事は上VIDEOが出来るまでの、BGM音源日誌となります。あわせて どうぞ よろしくお願い致します。
I LOVE Rock'N'Roll LIFE: Unplugged Version
youtube担当のJUNさんから 新しい長編VIDEO:
仮題:
矢沢永吉『成りあがり』に刻まれた「28歳の記録」 伝説のバイブル誕生秘話とキャロル解散の真実
のBGM依頼を受けておりました楽曲が出来あがりましたので、こちらに先に配置させて頂きます。
以下の前回の続きとなります。
以下の音源はDEMO版となります。お時間が許されましたら、ご視聴頂ければ幸いです。よろしくお願い致します。
Vocalは今回も 私の声をmodelとして使用しております。
Lyrics : 歌詞
I LOVE Rock'N'Roll LIFE: Lyrics
Written by BooksChannel
[Intro]
[Verse 1]
1回こっきりの人生だ ぼやぼやしてる暇なんてないぜ
人生は思うより短い… 瞬きの一瞬でBYE BYE BYE
[Pre-Chorus]
云う程つらかない Oh-Baby Come together (Life Is Very Short)
云う程ヤワじゃない Oh-Baby Your Heart No - No No No...
[Chorus]
I Love Rock'N'Roll LIFE Now! やるか引くかの人生
I Love Rock'N'Roll LIFE Now! お前の道はお前のモノ
I Love Rock'N'Roll LIFE Now! 振子が揺れてる人生
I Love Rock'N'Roll LIFE Now! 成りあがれ ロッケンロール マイ・ライフ
[Interlude]
[Bridge]
倒れる時もあるさ Down Down Down
泣きたい夜もあるだろう Cry Cry Cry
笑い蹴飛ばせ…EveryThing Will be Allright
[Verse 2]
黙ってりゃいいってもんじゃない 叫びのコードを叩きつけろ
生まれ生きてそして死んでいく 仮面のお前にBYE BYE BYE
[Pre-Chorus2]
云う程つらかない Oh-Baby Come together (Life Is Very Short)
云う程ヤワじゃない Oh-Baby Your Guts No - No No No
[Chorus2]
I Love Rock'N'Roll LIFE Now! 駄目でもともとの人生
I Love Rock'N'Roll LIFE Now! とびっきりの夢 晒し生きろ
I Love Rock'N'Roll LIFE Now! 壊れた時計の人生
I Love Rock'N'Roll LIFE Now! 成りあがれ ロッケンロール マイ・ライフ
[Chorus3]
I Love Rock'N'Roll LIFE Now!
I Love Rock'N'Roll LIFE Now! 成りあがれ ロッケンロール マイ・ライフ
I Love Rock'N'Roll LIFE Now!
I Love Rock'N'Roll LIFE Now!
ギターコード譜


ギターコードPDF
BooksChannelオリジナルHTMLツール

以下memoにhtmlでUTF-8で保存され、ローカルでご使用下さい。
私は本屋ですので、ご質問等はどうかご勘弁頂ければ幸いです。
あくまでも 自己責任ということで よろしくお願い致します。
付録で簡単なコードを調べることができるちょっとしたツールを付けています。
開放弦の記入はどうか…お忘れなく…
▼置いておきます。LyricsとChordの書き方は上の画像をご確認頂ければ幸いです。
追記…令和8年4月24日(金)午後8時40分
以下どうせやるならと…updateしました。ちょっと使い易くなっているかも???しれません。"Tha….t" 作りましたので、不具合あったら御免なさい。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Song-Books Channel Ultimate v2026.4 Professional</title>
<style>
:root {
--bg-color: #050505;
--header-bg: #111111;
--accent-color: #ffb300;
--editor-bg: #111111;
--preview-bg: #ffffff;
--chord-text: #0d47a1;
--chord-bg: #e3f2fd;
--section-text: #c62828;
--section-bg: #ffebee;
}
html, body { height: 100%; margin: 0; padding: 0; }
body { font-family: 'Segoe UI', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif; display: flex; flex-direction: column; background: var(--bg-color); color: #eee; overflow: hidden; }
header { background: var(--header-bg); padding: 10px 25px; display: flex; justify-content: space-between; align-items: center; border-bottom: 3px solid var(--accent-color); z-index: 1000; flex-shrink: 0; }
.logo { font-size: 1.4em; font-weight: bold; color: var(--accent-color); letter-spacing: 2px; }
.controls { display: flex; gap: 12px; }
button { background: #222; color: #fff; border: 1px solid #444; padding: 8px 16px; cursor: pointer; border-radius: 6px; font-size: 12px; font-weight: bold; }
button:hover { background: var(--accent-color); color: #000; }
.main-container { display: flex; flex: 1; overflow: hidden; position: relative; }
textarea, #preview-container { flex: 1; height: 100%; overflow-y: auto; border: none; outline: none; box-sizing: border-box; }
textarea { background: var(--editor-bg); color: #aaa; font-family: 'Consolas', monospace; padding: 30px; border-right: 2px solid #222; line-height: 1.6; resize: none; font-size: 15px; }
#preview-container { background: var(--preview-bg); color: #000; padding: 50px; }
.line { display: flex; flex-wrap: wrap; margin-bottom: 1.8em; line-height: 1; align-items: flex-end; }
.segment { display: inline-flex; flex-direction: column; align-items: flex-start; min-width: 0.5em; }
.chord-box {
min-height: 1.5em; font-size: 0.85em; font-weight: 900; color: var(--chord-text);
background: var(--chord-bg); padding: 1px 6px; border-radius: 4px;
border: 1px solid #bbdefb; margin-bottom: 4px; white-space: nowrap;
-webkit-print-color-adjust: exact; print-color-adjust: exact;
}
.chord-empty { min-height: 1.5em; margin-bottom: 4px; visibility: hidden; }
.lyric-text { font-size: 19px; white-space: pre; line-height: 1.1; color: #000; font-weight: 500; }
.section-header {
display: inline-block; background: var(--section-bg); color: var(--section-text);
padding: 6px 15px; border-radius: 4px; font-weight: bold; margin: 25px 0 12px 0;
border-left: 8px solid var(--section-text); font-size: 1.1em;
-webkit-print-color-adjust: exact; print-color-adjust: exact;
}
/* ===== Chord Finder v2026.4 ===== */
#chord-finder {
position: fixed; bottom: 20px; right: 20px; width: 520px;
background: #0d0d0d; border: 2px solid var(--accent-color); border-radius: 15px;
padding: 20px; z-index: 2000; display: none;
box-shadow: 0 20px 60px rgba(0,0,0,1);
max-height: calc(100vh - 40px); overflow-y: auto;
}
.finder-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid #333;
}
.finder-title { font-size: 15px; font-weight: bold; color: var(--accent-color); letter-spacing: 1px; }
.finder-btns { display: flex; gap: 6px; }
.finder-btns button { padding: 4px 10px; font-size: 11px; background: #222; }
.flat-btn.active { background: var(--accent-color); color: #000; }
.close-btn { color: #ff8a80 !important; font-size: 16px !important; padding: 2px 10px !important; }
.tone-selector {
display: flex; gap: 4px; margin-bottom: 10px; padding: 6px;
background: #1a1a1a; border-radius: 6px; align-items: center;
}
.tone-selector span { font-size: 10px; color: #666; margin-right: 4px; letter-spacing: 1px; }
.tone-btn {
padding: 4px 10px; font-size: 10px; background: #222; color: #999;
border: 1px solid #333; border-radius: 3px; cursor: pointer;
}
.tone-btn.active { background: var(--accent-color); color: #000; border-color: #fff; font-weight: bold; }
.fret-header { display: flex; gap: 4px; margin-bottom: 3px; padding-left: 34px; }
.fret-header span { width: 32px; text-align: center; font-size: 10px; color: #555; }
.fret-row { display: flex; gap: 4px; margin-bottom: 4px; align-items: center; }
.fret { width: 32px; height: 22px; background: #222; cursor: pointer; font-size: 11px;
display: flex; align-items: center; justify-content: center; color: #555;
border-radius: 4px; border: 1px solid #333; user-select: none; }
.fret:hover { border-color: var(--accent-color); }
.fret.active { background: var(--accent-color); color: #000; font-weight: bold; border-color: #fff; }
.fret.muted-row { background: #2a0a0a; color: #4a1010; border-color: #3a1010; cursor: not-allowed; }
.string-name {
width: 30px; font-size: 15px; color: var(--accent-color); font-weight: bold;
text-align: center; cursor: pointer; user-select: none; padding: 2px 0;
}
.string-name.muted { color: #ff5252; text-decoration: line-through; }
.primary-panel {
background: linear-gradient(135deg, #1a0f00, #0f0f0f);
border: 1px solid var(--accent-color); border-radius: 10px;
padding: 14px; margin-top: 14px;
}
.primary-top { display: flex; justify-content: space-between; align-items: center; }
.primary-chord { font-size: 36px; font-weight: 900; color: var(--accent-color); letter-spacing: 1px; }
.primary-actions { display: flex; gap: 6px; }
.action-btn {
padding: 6px 12px; font-size: 11px; font-weight: bold;
background: var(--accent-color); color: #000; border: none;
border-radius: 4px; cursor: pointer;
}
.action-btn:hover { background: #fff; }
.action-btn.secondary { background: #333; color: #fff; }
.action-btn.secondary:hover { background: #555; }
.primary-notes { font-size: 13px; color: #bbb; margin-top: 6px; letter-spacing: 1.5px; }
.primary-formula { font-size: 11px; color: #888; margin-top: 3px; }
.alt-list { margin-top: 10px; }
.alt-title { font-size: 10px; color: #666; margin-bottom: 4px; letter-spacing: 1px; }
.alt-item {
display: flex; justify-content: space-between; align-items: center;
padding: 4px 10px; background: #1a1a1a; border-radius: 4px;
margin-bottom: 3px; border-left: 2px solid #333;
}
.alt-name { font-size: 14px; color: #ddd; font-weight: bold; }
.alt-insert {
padding: 2px 8px; font-size: 10px; background: #333; color: #fff;
border: none; border-radius: 3px; cursor: pointer;
}
.alt-insert:hover { background: var(--accent-color); color: #000; }
#toggle-btn {
position: fixed; bottom: 20px; right: 20px; z-index: 2100;
background: var(--accent-color); border: none; padding: 12px 24px;
border-radius: 50px; font-weight: bold; cursor: pointer; font-size: 13px;
box-shadow: 0 6px 20px rgba(255, 179, 0, 0.4);
}
.hint { font-size: 10px; color: #555; text-align: center; margin-top: 8px; line-height: 1.4; }
/* ===== PRINT ===== */
@media print {
html, body { height: auto !important; overflow: visible !important; background: white !important; }
header, textarea, #toggle-btn, #chord-finder, .controls { display: none !important; }
.main-container { display: block !important; height: auto !important; overflow: visible !important; }
#preview-container {
display: block !important; width: 100% !important; height: auto !important;
padding: 0 !important; margin: 0 !important; overflow: visible !important;
background: white !important;
}
.lyric-text { font-size: 18pt !important; line-height: 1.1 !important; }
.chord-box { font-size: 11pt !important; min-height: 1.3em !important; padding: 1pt 4pt !important; margin-bottom: 2pt !important; }
.section-header { font-size: 14pt !important; margin-top: 15pt !important; margin-bottom: 8pt !important; padding: 4pt 12pt !important; }
.line { margin-bottom: 1.2em !important; page-break-inside: avoid; break-inside: avoid; }
}
</style>
</head>
<body>
<header>
<div class="logo">Song-Books Channel<span style="color:#fff">Ultimate v2026.4 Pro</span></div>
<div class="controls">
<button onclick="transpose(-1)">KEY -1</button>
<button onclick="transpose(1)">KEY +1</button>
<button onclick="window.print()">PRINT / PDF</button>
<button onclick="clearAll()" style="color:#ff8a80">CLEAR</button>
</div>
</header>
<div class="main-container">
<textarea id="editor" spellcheck="false" oninput="render()"></textarea>
<div id="preview-container"></div>
<div id="chord-finder">
<div class="finder-header">
<span class="finder-title">♪ GUITAR CHORD FINDER Pro</span>
<div class="finder-btns">
<button onclick="toggleFlat()" id="flat-btn" class="flat-btn">♯</button>
<button onclick="resetFrets()">RESET</button>
<button onclick="closeFinder()" class="close-btn">×</button>
</div>
</div>
<div class="tone-selector">
<span>TONE</span>
<button class="tone-btn active" id="tone-steel" onclick="setTone('steel')">Steel 鋼</button>
<button class="tone-btn" id="tone-nylon" onclick="setTone('nylon')">Nylon 柔</button>
<button class="tone-btn" id="tone-12str" onclick="setTone('12str')">12弦 煌</button>
<span style="margin-left:auto; color:#555;">STRUM</span>
<button class="tone-btn active" id="strum-down" onclick="setStrum('down')">↓</button>
<button class="tone-btn" id="strum-up" onclick="setStrum('up')">↑</button>
<button class="tone-btn" id="strum-arp" onclick="setStrum('arp')">♪</button>
</div>
<div id="fretboard"></div>
<div class="hint">弦名(E B G D A E)クリック=ミュート⇔解除 / フレット「O」=開放</div>
<div class="primary-panel" id="primary-panel" style="display:none;">
<div class="primary-top">
<div class="primary-chord" id="primary-chord">---</div>
<div class="primary-actions">
<button class="action-btn secondary" onclick="playChord()">▶ 音</button>
<button class="action-btn" onclick="insertPrimary()">→ 挿入</button>
</div>
</div>
<div class="primary-notes" id="primary-notes"></div>
<div class="primary-formula" id="primary-formula"></div>
</div>
<div class="alt-list" id="alt-list"></div>
</div>
</div>
<button id="toggle-btn" onclick="openFinder()">♪ CHORD FINDER</button>
<script>
const editor = document.getElementById('editor');
const preview = document.getElementById('preview-container');
const notesSharp = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];
const notesFlat = ["C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B"];
let useFlat = false;
function nname(n) { return useFlat ? notesFlat[n] : notesSharp[n]; }
editor.value = localStorage.getItem('bc_content_v7') || "";
// ===== Render & Transpose =====
function render() {
const text = editor.value;
try { localStorage.setItem('bc_content_v7', text); } catch(e) {}
const lines = text.split('\n');
preview.innerHTML = lines.map(line => {
const trimmed = line.trim();
if (trimmed.startsWith('[') && trimmed.endsWith(']') && !line.includes('] ')) {
if (trimmed.match(/^\[(Verse|Chorus|Bridge|Intro|Outro|Interlude|Pre-Chorus|Solo)/i)) {
return `<div class="section-header">${trimmed.replace(/[\[\]]/g, '')}</div>`;
}
}
let parts = line.split(/(\[[^\]]+\])/);
let lineHtml = '<div class="line">';
for (let i = 0; i < parts.length; i++) {
let part = parts[i];
if (part.startsWith('[') && part.endsWith(']')) {
let chord = part.slice(1, -1);
let lyric = parts[i+1] || "";
lineHtml += `<div class="segment"><div class="chord-box">${chord}</div><div class="lyric-text">${lyric || ' '}</div></div>`;
i++;
} else if (part !== "") {
lineHtml += `<div class="segment"><div class="chord-empty"></div><div class="lyric-text">${part}</div></div>`;
}
}
return lineHtml + '</div>';
}).join('');
}
function transpose(dir) {
const regex = /\[([A-G][#b]?[^\]]*)\]/g;
editor.value = editor.value.replace(regex, (match, chord) => {
return "[" + chord.replace(/[A-G][#b]?/g, m => {
let n = m.replace('Db','C#').replace('Eb','D#').replace('Gb','F#').replace('Ab','G#').replace('Bb','A#');
let idx = notesSharp.indexOf(n);
return idx === -1 ? m : notesSharp[(idx + dir + 12) % 12];
}) + "]";
});
render();
}
function clearAll() { if(confirm("消去しますか?")) { editor.value=""; render(); } }
render();
// ============================================================
// CHORD FINDER v2026.4 Professional Edition
// ============================================================
const stringRoots = [4, 11, 7, 2, 9, 4];
const stringMidi = [64, 59, 55, 50, 45, 40];
const stringNames = ["E", "B", "G", "D", "A", "E"];
let currentFrets = [-1, -1, -1, -1, -1, -1];
let mutedStrings = [false, false, false, false, false, false];
const chordLibrary = [
{ name: "5", ivals: [0,7], pop: 3 },
{ name: "", ivals: [0,4,7], pop: 5 },
{ name: "m", ivals: [0,3,7], pop: 5 },
{ name: "sus4", ivals: [0,5,7], pop: 4 },
{ name: "sus2", ivals: [0,2,7], pop: 4 },
{ name: "dim", ivals: [0,3,6], pop: 3 },
{ name: "aug", ivals: [0,4,8], pop: 2 },
{ name: "7", ivals: [0,4,7,10], pop: 5 },
{ name: "m7", ivals: [0,3,7,10], pop: 5 },
{ name: "maj7", ivals: [0,4,7,11], pop: 5 },
{ name: "mMaj7", ivals: [0,3,7,11], pop: 3 },
{ name: "m7b5", ivals: [0,3,6,10], pop: 3 },
{ name: "dim7", ivals: [0,3,6,9], pop: 3 },
{ name: "7sus4", ivals: [0,5,7,10], pop: 4 },
{ name: "7sus2", ivals: [0,2,7,10], pop: 2 },
{ name: "aug7", ivals: [0,4,8,10], pop: 2 },
{ name: "7b5", ivals: [0,4,6,10], pop: 2 },
{ name: "maj7#5", ivals: [0,4,8,11], pop: 1 },
{ name: "maj7b5", ivals: [0,4,6,11], pop: 1 },
{ name: "6", ivals: [0,4,7,9], pop: 4 },
{ name: "m6", ivals: [0,3,7,9], pop: 4 },
{ name: "add9", ivals: [0,2,4,7], pop: 4 },
{ name: "m(add9)", ivals: [0,2,3,7], pop: 4 },
{ name: "add11", ivals: [0,4,5,7], pop: 3 },
{ name: "m(add11)", ivals: [0,3,5,7], pop: 3 },
{ name: "9", ivals: [0,2,4,7,10], pop: 4 },
{ name: "m9", ivals: [0,2,3,7,10], pop: 4 },
{ name: "maj9", ivals: [0,2,4,7,11], pop: 4 },
{ name: "mMaj9", ivals: [0,2,3,7,11], pop: 1 },
{ name: "6/9", ivals: [0,2,4,7,9], pop: 4 },
{ name: "m6/9", ivals: [0,2,3,7,9], pop: 2 },
{ name: "7b9", ivals: [0,1,4,7,10], pop: 3 },
{ name: "7#9", ivals: [0,3,4,7,10], pop: 2 },
{ name: "9b5", ivals: [0,2,4,6,10], pop: 1 },
{ name: "9#5", ivals: [0,2,4,8,10], pop: 1 },
{ name: "7#11", ivals: [0,4,6,7,10], pop: 2 },
{ name: "maj7#11", ivals: [0,4,6,7,11], pop: 2 },
{ name: "7add11", ivals: [0,4,5,7,10], pop: 2 },
{ name: "m7add11", ivals: [0,3,5,7,10], pop: 3 },
{ name: "11", ivals: [0,2,5,7,10], pop: 2 },
{ name: "maj7add11", ivals: [0,4,5,7,11], pop: 2 },
{ name: "m11", ivals: [0,2,3,5,7,10], pop: 3 },
{ name: "13", ivals: [0,2,4,7,9,10], pop: 2 },
{ name: "m13", ivals: [0,2,3,7,9,10], pop: 2 },
{ name: "maj13", ivals: [0,2,4,7,9,11], pop: 1 }
];
function initFretboard() {
const board = document.getElementById('fretboard');
board.innerHTML = '';
let header = '<div class="fret-header">';
for (let f = 0; f <= 12; f++) header += `<span>${f === 0 ? 'O' : f}</span>`;
header += '</div>';
board.innerHTML = header;
for (let s = 0; s < 6; s++) {
const row = document.createElement('div');
row.className = 'fret-row';
const nameDiv = document.createElement('div');
nameDiv.className = 'string-name' + (mutedStrings[s] ? ' muted' : '');
nameDiv.id = `sn-${s}`;
nameDiv.innerText = stringNames[s];
nameDiv.onclick = () => {
mutedStrings[s] = !mutedStrings[s];
if (mutedStrings[s]) currentFrets[s] = -1;
updateFretUI();
};
row.appendChild(nameDiv);
for (let f = 0; f <= 12; f++) {
const btn = document.createElement('div');
btn.className = 'fret';
btn.id = `f-${s}-${f}`;
btn.innerText = f === 0 ? 'O' : f;
btn.onclick = () => {
if (mutedStrings[s]) mutedStrings[s] = false;
currentFrets[s] = (currentFrets[s] === f) ? -1 : f;
updateFretUI();
};
row.appendChild(btn);
}
board.appendChild(row);
}
}
function updateFretUI() {
for (let s = 0; s < 6; s++) {
const sn = document.getElementById(`sn-${s}`);
sn.className = 'string-name' + (mutedStrings[s] ? ' muted' : '');
for (let f = 0; f <= 12; f++) {
const el = document.getElementById(`f-${s}-${f}`);
if (mutedStrings[s]) { el.className = 'fret muted-row'; continue; }
el.className = (currentFrets[s] === f) ? 'fret active' : 'fret';
}
}
let bassNote = -1;
const playedRaw = [];
for (let s = 5; s >= 0; s--) {
if (mutedStrings[s] || currentFrets[s] === -1) continue;
const note = (stringRoots[s] + currentFrets[s]) % 12;
if (bassNote === -1) bassNote = note;
playedRaw.push(note);
}
const panel = document.getElementById('primary-panel');
const chordEl = document.getElementById('primary-chord');
const notesEl = document.getElementById('primary-notes');
const formEl = document.getElementById('primary-formula');
const altEl = document.getElementById('alt-list');
if (bassNote === -1) { panel.style.display = 'none'; altEl.innerHTML = ''; return; }
panel.style.display = 'block';
const uniqueNotes = [...new Set(playedRaw)];
const sortedForDisplay = uniqueNotes.slice().sort((a,b) => ((a-bassNote+12)%12) - ((b-bassNote+12)%12));
notesEl.innerText = sortedForDisplay.map(n => nname(n)).join(' - ');
const candidates = findChordCandidates(bassNote, uniqueNotes);
if (candidates.length === 0) { chordEl.innerText = '?'; formEl.innerText = '該当コードなし'; altEl.innerHTML = ''; return; }
const primary = candidates[0];
chordEl.innerText = primary.name;
formEl.innerText = primary.formula;
const alts = candidates.slice(1, 5);
altEl.innerHTML = alts.length === 0 ? '' : (
'<div class="alt-title">別解・候補:</div>' +
alts.map(c =>
`<div class="alt-item">
<span class="alt-name">${c.name}</span>
<button class="alt-insert" onclick="insertChord('${c.name}')">→挿入</button>
</div>`
).join('')
);
}
function findChordCandidates(bassNote, notes) {
const candidates = [];
const totalPlayed = notes.length;
for (const rootCandidate of notes) {
const ivalsFromRoot = notes.map(n => (n - rootCandidate + 12) % 12);
const ivalSet = new Set(ivalsFromRoot);
for (const chord of chordLibrary) {
if (!chord.ivals.every(iv => ivalSet.has(iv))) continue;
const extras = ivalsFromRoot.filter(iv => !chord.ivals.includes(iv)).length;
const required = chord.ivals.length;
let score = required * 10 - extras * 7 + chord.pop;
if (rootCandidate === bassNote) score += 5;
if (extras === 0 && totalPlayed === required) score += 15;
let name = nname(rootCandidate) + chord.name;
if (rootCandidate !== bassNote) name += "/" + nname(bassNote);
const formula = '(R' + chord.ivals.slice(1).map(iv => ' ' + intervalName(iv)).join('') + ')';
candidates.push({ name, score, extras, required, formula });
}
}
candidates.sort((a, b) => b.score - a.score || a.extras - b.extras);
const seen = new Set();
return candidates.filter(c => { if (seen.has(c.name)) return false; seen.add(c.name); return true; });
}
function intervalName(iv) {
const map = {1:'b9', 2:'9', 3:'b3', 4:'3', 5:'11', 6:'b5/#11', 7:'5', 8:'#5/b13', 9:'6/13', 10:'b7', 11:'7'};
return map[iv] || String(iv);
}
function resetFrets() {
currentFrets = [-1,-1,-1,-1,-1,-1];
mutedStrings = [false,false,false,false,false,false];
updateFretUI();
}
function openFinder() { document.getElementById('chord-finder').style.display = 'block'; document.getElementById('toggle-btn').style.display = 'none'; }
function closeFinder() { document.getElementById('chord-finder').style.display = 'none'; document.getElementById('toggle-btn').style.display = 'block'; }
function toggleFlat() { useFlat = !useFlat; const btn = document.getElementById('flat-btn'); btn.innerText = useFlat ? '♭' : '♯'; btn.classList.toggle('active', useFlat); updateFretUI(); }
function insertChord(chordName) {
const start = editor.selectionStart, end = editor.selectionEnd, text = editor.value;
const insert = `[${chordName}]`;
editor.value = text.slice(0, start) + insert + text.slice(end);
editor.selectionStart = editor.selectionEnd = start + insert.length;
editor.focus();
render();
}
function insertPrimary() {
const name = document.getElementById('primary-chord').innerText;
if (name && name !== '---' && name !== '?') insertChord(name);
}
// ============================================================
// ★★★ STEEL-STRING ACOUSTIC SYNTHESIS ★★★
// Karplus-Strong physical modelling
// (Stanford CCRMA, Karplus & Strong 1983)
// ============================================================
let audioCtx = null;
const bufferCache = {};
let toneMode = 'steel'; // 'steel' | 'nylon' | '12str'
let strumMode = 'down'; // 'down' | 'up' | 'arp'
function setTone(mode) {
toneMode = mode;
['steel','nylon','12str'].forEach(m => {
document.getElementById('tone-' + m).classList.toggle('active', m === mode);
});
// トーンが変わったらキャッシュを破棄(音色パラメータが変わるため)
Object.keys(bufferCache).forEach(k => delete bufferCache[k]);
}
function setStrum(mode) {
strumMode = mode;
['down','up','arp'].forEach(m => {
document.getElementById('strum-' + m).classList.toggle('active', m === mode);
});
}
function ensureAudio() {
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
if (audioCtx.state === 'suspended') audioCtx.resume();
}
// Karplus-Strong による単音バッファ生成
// frequency: 周波数 (Hz) / stringIdx: 0=高音E, 5=低音E
function createPluckedStringBuffer(frequency, stringIdx) {
const sr = audioCtx.sampleRate;
const duration = 3.8;
const length = Math.floor(duration * sr);
const buffer = audioCtx.createBuffer(1, length, sr);
const data = buffer.getChannelData(0);
// 遅延ライン長 = 弦の往復時間に相当 (サンプル数)
const N = Math.max(2, Math.floor(sr / frequency));
const delayLine = new Float32Array(N);
// --- 音色別パラメータ ---
let damping, filterBlend, preEmphasis;
if (toneMode === 'nylon') {
// ナイロン弦: 柔らかく倍音少なめ、減衰やや速い
damping = 0.993; filterBlend = 0.5; preEmphasis = 0.2;
} else if (toneMode === '12str') {
// 12弦: ブライトで煌びやか、倍音豊か、サステイン長め
damping = 0.997; filterBlend = 0.42; preEmphasis = 0.5;
} else {
// スチール弦 (Martin / Gibson 系のドレッドノート想定)
// 鋼の銀色 ── 長いサステイン、高域の艶
damping = 0.9965; filterBlend = 0.44; preEmphasis = 0.4;
}
// --- 初期励起(excitation): ピックアタックを模したノイズバースト ---
// 1. 白色ノイズで初期化
for (let i = 0; i < N; i++) {
delayLine[i] = Math.random() * 2 - 1;
}
// 2. プリエンファシス(一次ハイパス) ── ピックが弦を弾く瞬間の金属的な高域成分
let prev = 0;
for (let i = 0; i < N; i++) {
const curr = delayLine[i];
delayLine[i] = curr - preEmphasis * prev;
prev = curr;
}
// 3. ピック位置による倍音構造 ── ブリッジ寄り(1/5地点)で弾いた想定
// これが「鋼の銀色」の硬質な煌めきの正体。
const pickPos = 0.18; // 0.0=ナット寄り 0.5=センター
const pickOffset = Math.floor(N * pickPos);
for (let i = 0; i < N; i++) {
const j = (i + pickOffset) % N;
delayLine[i] = 0.7 * delayLine[i] - 0.3 * delayLine[j];
}
// 4. 正規化 (クリップ防止)
let peak = 0;
for (let i = 0; i < N; i++) peak = Math.max(peak, Math.abs(delayLine[i]));
if (peak > 0) for (let i = 0; i < N; i++) delayLine[i] /= peak;
// --- Karplus-Strong メインループ ---
// 遅延ラインを1周するごとに、高域がフィルタで削れ、全体が damping で減衰。
// これが「弦の振動 → 時間経過で高次倍音から先に失われる」物理現象を再現する。
const a = filterBlend;
const b = 1 - a;
let idx = 0;
for (let i = 0; i < length; i++) {
data[i] = delayLine[idx];
const next = (idx + 1) % N;
// 2-tap 加重平均 = 一次ローパス(ブレンド可変)
delayLine[idx] = damping * (a * delayLine[idx] + b * delayLine[next]);
idx = next;
}
// 5. フェードアウト末端処理 (最後の80msでスムーズに減衰)
const fadeLen = Math.floor(sr * 0.08);
for (let i = 0; i < fadeLen; i++) {
const k = length - fadeLen + i;
data[k] *= 1 - (i / fadeLen);
}
return buffer;
}
// 和音全体の発音
function playChord() {
ensureAudio();
const now = audioCtx.currentTime;
// --- マスターEQチェーン (アコースティック・ギターの共鳴胴シミュレーション) ---
// 80Hz HP → ボディ共鳴(160Hz) → 胴鳴りの濁り除去(500Hz) → プレゼンス(3.5kHz) → 空気感(8kHz シェルフ)
const hp = audioCtx.createBiquadFilter();
hp.type = 'highpass'; hp.frequency.value = 75; hp.Q.value = 0.7;
const body = audioCtx.createBiquadFilter();
body.type = 'peaking'; body.frequency.value = 165; body.Q.value = 1.4;
body.gain.value = (toneMode === 'nylon') ? 4.5 : 3.2;
const muddy = audioCtx.createBiquadFilter();
muddy.type = 'peaking'; muddy.frequency.value = 480; muddy.Q.value = 0.9;
muddy.gain.value = -2.5;
const presence = audioCtx.createBiquadFilter();
presence.type = 'peaking';
presence.frequency.value = (toneMode === '12str') ? 4200 : 3400;
presence.Q.value = 0.75;
presence.gain.value = (toneMode === 'nylon') ? 1.5 : 4.0;
const air = audioCtx.createBiquadFilter();
air.type = 'highshelf';
air.frequency.value = 8500;
air.gain.value = (toneMode === 'nylon') ? 0.5 : 3.5;
const masterGain = audioCtx.createGain();
masterGain.gain.value = 0.26;
hp.connect(body).connect(muddy).connect(presence).connect(air).connect(masterGain).connect(audioCtx.destination);
// --- ストラム順序と間隔 ---
// ダウン: 低音弦→高音弦 / アップ: 高音弦→低音弦 / アルペジオ: ゆっくり低→高
const playOrder = [];
for (let s = 5; s >= 0; s--) {
if (mutedStrings[s] || currentFrets[s] === -1) continue;
playOrder.push(s);
}
if (strumMode === 'up') playOrder.reverse();
const gap = strumMode === 'arp' ? 0.14 : (strumMode === 'up' ? 0.016 : 0.022);
playOrder.forEach((s, idx) => {
const midi = stringMidi[s] + currentFrets[s];
const freq = 440 * Math.pow(2, (midi - 69) / 12);
// キャッシュキー(音色とフレットに依存)
const cacheKey = `${toneMode}_${s}_${currentFrets[s]}`;
if (!bufferCache[cacheKey]) {
bufferCache[cacheKey] = createPluckedStringBuffer(freq, s);
}
const source = audioCtx.createBufferSource();
source.buffer = bufferCache[cacheKey];
// 実ギターの弦ごとの微小なピッチ揺らぎ (±3cent)
source.detune.value = (Math.random() - 0.5) * 6;
// 12弦モード: 同じフレーズを +1オクターブでもう一本重ねる
let source2 = null;
if (toneMode === '12str' && s >= 2) { // 低音側3本のみオクターブ上を追加
source2 = audioCtx.createBufferSource();
source2.buffer = bufferCache[cacheKey];
source2.playbackRate.value = 2.0; // 1オクターブ上
source2.detune.value = (Math.random() - 0.5) * 8;
}
// 弦ごとのゲインとパン
const stringGain = audioCtx.createGain();
// ダウンストラムは低音弦が先で音量大きめ、アップは高音弦が先で輝く
const baseVel = strumMode === 'up' ? 0.50 : 0.60;
stringGain.gain.value = baseVel + Math.random() * 0.18;
const panner = audioCtx.createStereoPanner();
// 弦ごとのステレオ定位(低音=左寄り, 高音=右寄り)、ただし控えめに
panner.pan.value = ((2.5 - s) / 2.5) * 0.18;
const startT = now + idx * gap;
source.connect(stringGain).connect(panner).connect(hp);
source.start(startT);
if (source2) {
const g2 = audioCtx.createGain();
g2.gain.value = 0.35;
source2.connect(g2).connect(stringGain);
source2.start(startT + 0.004);
}
});
}
initFretboard();
updateFretUI();
</script>
</body>
</html>

1742日目
いいなと思ったら応援しよう!
よろしければBooksChannel本屋日記物語をサポートいただければ幸いです。いただいたサポートはクリエイターとしての活動費に使用させていただきます。