Skip to content

Commit

Permalink
feat: Synthesizer Waveforms
Browse files Browse the repository at this point in the history
  • Loading branch information
Korilakkuma committed Jan 25, 2024
1 parent e07408b commit 1f17aa4
Show file tree
Hide file tree
Showing 2 changed files with 236 additions and 0 deletions.
182 changes: 182 additions & 0 deletions docs/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,179 @@ const createSinFunctionPath = (svg, withParameters) => {
}
};

const createSquareFunctionPath = (svg) => {
const innerWidth = Number(svg.getAttribute('width')) - padding * 2;
const innerHeight = Number(svg.getAttribute('height')) - padding * 2;

const a = Number(svg.getAttribute('data-a'));
const f = Number(svg.getAttribute('data-f'));
const t = (svg.getAttribute('data-t') ?? '').split(',');

const path = document.createElementNS(xmlns, 'path');

let d = '';

for (let i = 0, len = f * sampleRate; i < len; i++) {
const t = sampleRate / f;
const n = i < t ? i : i % t;
const v = a * (n < t / 2 ? 1 : -1);

const x = (i / len) * innerWidth + padding;
const y = (1 - v) * (innerHeight / 2) + padding;

if (i === 0) {
d += `M${x + lineWidth / 2} ${y}`;
} else {
d += ` L${x} ${y}`;
}
}

path.setAttribute('d', d);

path.setAttribute('stroke', 'rgba(0, 0, 255, 1.0)');
path.setAttribute('fill', 'none');
path.setAttribute('stroke-width', lineWidth.toString(10));
path.setAttribute('stroke-linecap', lineCap);
path.setAttribute('stroke-linejoin', lineJoin);

svg.appendChild(path);

if (svg.getAttribute('data-parameters') === 'true') {
t.forEach((sec, index) => {
const text = document.createElementNS(xmlns, 'text');

text.textContent = `${sec} sec`;

text.setAttribute('x', (padding + 24 + (innerWidth / 2) * index).toString(10));
text.setAttribute('y', (padding + innerHeight / 2 + 16).toString(10));

text.setAttribute('text-anchor', 'middle');
text.setAttribute('stroke', 'none');
text.setAttribute('fill', baseColor);
text.setAttribute('font-size', '16px');

svg.appendChild(text);
});
}
};

const createSawtoothFunctionPath = (svg) => {
const innerWidth = Number(svg.getAttribute('width')) - padding * 2;
const innerHeight = Number(svg.getAttribute('height')) - padding * 2;

const a = Number(svg.getAttribute('data-a'));
const f = Number(svg.getAttribute('data-f'));
const t = (svg.getAttribute('data-t') ?? '').split(',');

const w = 2 * Math.PI;

const path = document.createElementNS(xmlns, 'path');

let d = '';

for (let i = 0, len = f * sampleRate; i < len; i++) {
const t = sampleRate / f;
const n = i < t ? i : i % t;
const s = (2 * n) / t;
const v = a * (s - 1);

const x = (i / len) * innerWidth + padding;
const y = (1 - v) * (innerHeight / 2) + padding;

if (i === 0) {
d += `M${x + lineWidth / 2} ${y}`;
} else {
d += ` L${x} ${y}`;
}
}

path.setAttribute('d', d);

path.setAttribute('stroke', 'rgba(0, 0, 255, 1.0)');
path.setAttribute('fill', 'none');
path.setAttribute('stroke-width', lineWidth.toString(10));
path.setAttribute('stroke-linecap', lineCap);
path.setAttribute('stroke-linejoin', lineJoin);

svg.appendChild(path);

if (svg.getAttribute('data-parameters') === 'true') {
t.forEach((sec, index) => {
const text = document.createElementNS(xmlns, 'text');

text.textContent = `${sec} sec`;

text.setAttribute('x', (padding + 24 + (innerWidth / 2) * index).toString(10));
text.setAttribute('y', (padding + innerHeight / 2 + 16).toString(10));

text.setAttribute('text-anchor', 'middle');
text.setAttribute('stroke', 'none');
text.setAttribute('fill', baseColor);
text.setAttribute('font-size', '16px');

svg.appendChild(text);
});
}
};
const createTriangleFunctionPath = (svg) => {
const innerWidth = Number(svg.getAttribute('width')) - padding * 2;
const innerHeight = Number(svg.getAttribute('height')) - padding * 2;

const a = Number(svg.getAttribute('data-a'));
const f = Number(svg.getAttribute('data-f'));
const t = (svg.getAttribute('data-t') ?? '').split(',');

const w = 2 * Math.PI;

const path = document.createElementNS(xmlns, 'path');

let d = '';

for (let i = 0, len = f * sampleRate; i < len; i++) {
const t = sampleRate / f;
const n = i < t ? i : i % t;
const s = (4 * n) / t;
const v = a * (n < t / 2 ? -1 + s : 3 - s);

const x = (i / len) * innerWidth + padding;
const y = (1 - v) * (innerHeight / 2) + padding;

if (i === 0) {
d += `M${x + lineWidth / 2} ${y}`;
} else {
d += ` L${x} ${y}`;
}
}

path.setAttribute('d', d);

path.setAttribute('stroke', 'rgba(0, 0, 255, 1.0)');
path.setAttribute('fill', 'none');
path.setAttribute('stroke-width', lineWidth.toString(10));
path.setAttribute('stroke-linecap', lineCap);
path.setAttribute('stroke-linejoin', lineJoin);

svg.appendChild(path);

if (svg.getAttribute('data-parameters') === 'true') {
t.forEach((sec, index) => {
const text = document.createElementNS(xmlns, 'text');

text.textContent = `${sec} sec`;

text.setAttribute('x', (padding + 24 + (innerWidth / 2) * index).toString(10));
text.setAttribute('y', (padding + innerHeight / 2 + 16).toString(10));

text.setAttribute('text-anchor', 'middle');
text.setAttribute('stroke', 'none');
text.setAttribute('fill', baseColor);
text.setAttribute('font-size', '16px');

svg.appendChild(text);
});
}
};

createCoordinateRect(document.getElementById('svg-figure-sin-function'));
createSinFunctionPath(document.getElementById('svg-figure-sin-function'));

Expand All @@ -167,3 +340,12 @@ createSinFunctionPath(document.getElementById('svg-figure-sin-function-with-para

createCoordinateRect(document.getElementById('svg-figure-sin-function-with-parameters-1-0.5Hz'));
createSinFunctionPath(document.getElementById('svg-figure-sin-function-with-parameters-1-0.5Hz'));

createCoordinateRect(document.getElementById('svg-figure-square-function-with-parameters-0.5-4Hz'));
createSquareFunctionPath(document.getElementById('svg-figure-square-function-with-parameters-0.5-4Hz'));

createCoordinateRect(document.getElementById('svg-figure-sawtooth-function-with-parameters-0.5-4Hz'));
createSawtoothFunctionPath(document.getElementById('svg-figure-sawtooth-function-with-parameters-0.5-4Hz'));

createCoordinateRect(document.getElementById('svg-figure-triangle-function-with-parameters-0.5-4Hz'));
createTriangleFunctionPath(document.getElementById('svg-figure-triangle-function-with-parameters-0.5-4Hz'));
54 changes: 54 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,60 @@ <h4>振幅と周波数 (周期)</h4>
</section>
<p>いかがでしたか ? 振幅と周波数は Web Audio API の解説においても頻出する用語なので, ある程度理解しておくと, Web Audio API の理解も進むでしょう.</p>
</section>
<section id="section-synthesizer-waveforms">
<h3>基本波形</h3>
<p>
<code>OscillatorNode</code><code>type</code> プロパティ (<code>OscillatorType</code>の値は, sin 波を生成する文字列
<code>&apos;sine&apos;</code> 以外にも, 矩形波を生成する <code>&apos;square&apos;</code> やノコギリ波を生成する <code>&apos;sawtooth&apos;</code>,
三角波を生成する <code>&apos;triangle&apos;</code> があります. sin 波の形はわかりましたが, それ以外はどのような形をしているのか見てみましょう.
</p>
<figure>
<svg
id="svg-figure-square-function-with-parameters-0.5-4Hz"
width="720"
height="405"
data-parameters="true"
data-a="0.5"
data-f="2"
data-t="0.0,0.5,1.0"
/>
<figcaption>振幅 <code>0.5</code>, 周波数 <code>4 Hz</code> (周期 <code>0.25 sec</code>) の矩形波 (<code>&apos;square&apos;</code>)</figcaption>
</figure>
<figure>
<svg
id="svg-figure-sawtooth-function-with-parameters-0.5-4Hz"
width="720"
height="405"
data-parameters="true"
data-a="0.5"
data-f="2"
data-t="0.0,0.5,1.0"
/>
<figcaption>
振幅 <code>0.5</code>, 周波数 <code>4 Hz</code> (周期 <code>0.25 sec</code>) のノコギリ波 (<code>&apos;sawtooth&apos;</code>)
</figcaption>
</figure>
<figure>
<svg
id="svg-figure-triangle-function-with-parameters-0.5-4Hz"
width="720"
height="405"
data-parameters="true"
data-a="0.5"
data-f="2"
data-t="0.0,0.5,1.0"
/>
<figcaption>振幅 <code>0.5</code>, 周波数 <code>4 Hz</code> (周期 <code>0.25 sec</code>) の三角波 (<code>&apos;triangle&apos;</code>)</figcaption>
</figure>
<p>
矩形波・ノコギリ波・三角波のいずれも正弦波と同じように, 周期性をもつ波 (関数) であるということです. 周期性をもつので,
周波数の概念を適用することができます. そして, 最も重要な点ですが, 周期性をもつ波は周波数の異なる正弦波を合成してできるということです.
矩形波・ノコギリ波・三角波はいずれも周期性をもちます. 周期性をもつので,
矩形波・ノコギリ波・三角波はいずれも周波数の異なる正弦波を合成して生成することができます. シンセサイザーでも,
正弦波・矩形波・ノコギリ波・三角波は基本波形として, サウンド生成のベースとなる波形です. そして, Web Audio API においても, 基本波形はサウンド生成
(<code>OscillatorNode</code>) のベースになる波形です.
</p>
</section>
</section>
</section>
</main>
Expand Down

0 comments on commit 1f17aa4

Please sign in to comment.