見出し画像

矢沢永吉『成りあがり』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!

I LOVE Rock'N'Roll LIFE: Lyrics

ギターコード譜


ギターコード譜1
ギターコード譜2

ギターコードPDF

BooksChannelオリジナルHTMLツール

私が個人で使用している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 || '&nbsp;'}</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>
https://booksch.com/go/me

1742日目


いいなと思ったら応援しよう!

Books Channel よろしければBooksChannel本屋日記物語をサポートいただければ幸いです。いただいたサポートはクリエイターとしての活動費に使用させていただきます。