[JavaScript ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ ๋งŒ๋“ค๊ธฐ] ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ๋กœ ๋‚˜๋งŒ์˜ ์†Œ๋ฆฌ ๋งŒ๋“ค๊ธฐ

    [JavaScript ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ ๋งŒ๋“ค๊ธฐ] ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ๋กœ ๋‚˜๋งŒ์˜ ์†Œ๋ฆฌ ๋งŒ๋“ค๊ธฐ


    ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ €๋ฒˆ ํฌ์ŠคํŒ…์— ์ด์–ด HTML5 Audio API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์ œ๋กœ ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์— ๋Œ€ํ•ด์„œ ํฌ์ŠคํŒ… ํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ์ €๋ฒˆ ํฌ์ŠคํŒ…์—์„œ ์ด๋ฏธ ์ด์•ผ๊ธฐ ํ–ˆ๋“ฏ์ด Audio API๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…ธ๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ์˜ค๋””์˜ค์˜ ํ๋ฆ„์„ ๋งŒ๋“ค์–ด ๋‚ด๋Š” ๊ฒƒ์„ ๊ธฐ๋ณธ ๊ฐœ๋…์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ์ดํŽ™ํ„ฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋ช‡ ๊ฐœ์˜ ์ถ”์ƒํ™”๋œ ๋…ธ๋“œ๋“ค์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ ‡๊ฒŒ ์–ด๋ ค์šธ ๊ฑด ์—†๋‹ค.

    ์šฐ๋ฆฌ๋Š” ๋‹จ์ง€ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“œ๋ ค๊ณ  ํ•˜๋Š” ์ดํŽ™ํ„ฐ๋“ค์ด ๊ฐ๊ฐ ์–ด๋–ค ์—ญํ• ์„ ํ•˜๋ฉฐ, ์–ด๋–ค ์›๋ฆฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ์–ด๋–ค ์šฉ๋„๋กœ ์‚ฌ์šฉ๋˜๋Š”์ง€๋งŒ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ๋œ๋‹ค. ์˜ค๋””์˜ค์— ์‚ฌ์šฉํ•˜๋Š” ์ดํŽ™ํ„ฐ๋Š” ๊ทธ ์ข…๋ฅ˜๊ฐ€ ๊ต‰์žฅํžˆ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ์ดํŽ™ํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ ์ˆ˜๋Š” ์—†๊ณ , ํ•„์ž๊ฐ€ ์ƒ๊ฐํ–ˆ์„ ๋•Œ ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋ณธ์ ์ธ ์ดํŽ™ํ„ฐ 5๊ฐœ ์ •๋„๋ฅผ ๊ตฌํ˜„ํ•ด๋ณผ ์ƒ๊ฐ์ด๋‹ค.

    ๊ธฐ๋ณธ์ ์œผ๋กœ ์˜ค๋””์˜ค๋ฅผ ๋กœ๋“œํ•˜์—ฌ ์†Œ์Šค ๋…ธ๋“œ(Source Node)๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์€ ์ด๋ฏธ ์ €๋ฒˆ ํฌ์ŠคํŒ…์—์„œ ์„ค๋ช…ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ์„ค๋ช…ํ•˜์ง€ ์•Š๊ฒ ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ฐ”๋กœ ์ดํŽ™ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋‚ด์šฉ๋ถ€ํ„ฐ ์„ค๋ช…ํ•œ๋‹ค. ๋ชจ๋“  ์ดํŽ™ํ„ฐ๋Š” ๋จผ์ € ํ•ด๋‹น ์ดํŽ™ํ„ฐ๊ฐ€ ํ•˜๋Š” ์ผ๊ณผ ์›๋ฆฌ์— ๋Œ€ํ•ด์„œ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ  ์ดํ›„ ๋ฌป์ง€๋„ ๋”ฐ์ง€์ง€๋„ ์•Š๊ณ  ๋ฐ”๋กœ ๊ตฌํ˜„ ๋“ค์–ด๊ฐ€๋„๋ก ํ•˜๊ฒ ๋‹ค.

    ์ž, ๊ทธ๋Ÿผ ํ•˜๋‚˜ํ•˜๋‚˜ ๋œฏ์–ด๋ณด๋„๋ก ํ•˜์ž.

    Compressor

    compressor

    ์ปดํ”„๋ ˆ์„œ(Compressor)๋Š” ์†Œ๋ฆฌ๊ฐ€ ์ผ์ • ํฌ๊ธฐ ์ด์ƒ์œผ๋กœ ์ปค์งˆ ๊ฒฝ์šฐ์— ์ด๋ฅผ ๊พน๊พน ๋ˆŒ๋Ÿฌ์„œ ๋‹ค์‹œ ์ž‘์€ ์†Œ๋ฆฌ๋กœ ๋งŒ๋“œ๋Š” ์ผ์ข…์˜ ์••์ถ•๊ธฐ ์—ญํ• ์„ ํ•˜๋Š” ์ดํŽ™ํ„ฐ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ์†Œ๋ฆฌ์˜ ํฌ๊ธฐ๋ฅผ ์กฐ์ ˆํ•˜๋Š” ์ดํŽ™ํ„ฐ๋ฅผ ๋‹ค์ด๋‚˜๋ฏน ์ดํŽ™ํ„ฐ๋ผ๊ณ  ํ•œ๋‹ค.

    ๊ธฐ๋ณธ์ ์œผ๋กœ ์˜ค๋””์˜ค ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ปดํ”„๋ ˆ์„œ๋ฅผ ๊ฑธ์–ด๋†“๊ณ  ๋ฏน์‹ฑ์„ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€๋ฐ, ์ด๋Š” ์˜ค๋””์˜ค ์‹ ํ˜ธ๊ฐ€ ์ผ์ • ํฌ๊ธฐ ์ด์ƒ์œผ๋กœ ๊ฐ‘์ž๊ธฐ ์ปค์กŒ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ํด๋ฆฌํ•‘(Clipping) ํ˜„์ƒ์„ ๋ฐฉ์–ดํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๊ธฐ๋„ ํ•˜๋‹ค. ๊ทธ๋Ÿผ ์—ฌ๊ธฐ์„œ ํ•œ๊ฐ€์ง€ ์˜๋ฌธ์ด ๋“ค ์ˆ˜ ์žˆ๋Š”๋ฐ,

    ์•„๋‹ˆ ๋‹จ์ˆœํžˆ ํด๋ฆฌํ•‘์„ ๋ง‰๋Š” ๊ฑฐ๋ฉด ๊ทธ๋ƒฅ Gain์„ ์ค„์ด๋ฉด ํ•ด๊ฒฐ๋˜๋Š” ๊ฑฐ ์•„๋‹ˆ์•ผ?

    ๋งž๋‹ค. ์‚ฌ์‹ค ๊ฒŒ์ธ์„ ์ค„์—ฌ๋„ ์–ด๋Š ์ •๋„ ํด๋ฆฌํ•‘์„ ๋ฐฉ์–ดํ•  ์ˆ˜๋Š” ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ ์Œ์•…์ด๋ž€ ์…ˆ์—ฌ๋ฆผ์ด ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌด์ž‘์ • ๊ฒŒ์ธ์„ ๋‚ฎ์ถ”๋ฉด ์ž‘์€ ์†Œ๋ฆฌ๋Š” ์•„์˜ˆ ์ž…๋ ฅ๋˜์ง€๋„ ์•Š๋Š” ์Šฌํ”ˆ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด ์—ฌ๋Ÿฌ๋ถ„์ด ๋…ธ๋ž˜๋ฐฉ์— ๊ฐ”์„ ๋•Œ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž. ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐœ๋ผ๋“œ๋ฅผ ๋ถ€๋ฅธ๋‹ค๋ฉด ๋…ธ๋ž˜์˜ ๋„์ž…๋ถ€์—์„œ๋Š” ์ž”์ž”ํ•œ ๋Š๋‚Œ์œผ๋กœ ์กฐ์šฉํžˆ ๋ถ€๋ฅด๋‹ค๊ฐ€ ํ›„๋ ด์—์„œ๋Š” ๊ณ ์Œ์„ ๋‚ด๊ธฐ์œ„ํ•ด ์„ฑ๋Œ€๋ฅผ ํ†ต๊ณผํ•˜๋Š” ๊ณต๊ธฐ์˜ ์••๋ ฅ์ด ์˜ฌ๋ผ๊ฐ€๋ฉฐ ์Œ๋Ÿ‰์ด ์ปค์ง„๋‹ค. ์ด๋•Œ ๋ฌด์ž‘์ • ๊ฒŒ์ธ์„ ๋‚ฎ์ถฐ์„œ ๋…น์Œํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ ‘๊ทผํ•œ๋‹ค๋ฉด ํ•„์—ฐ์ ์œผ๋กœ ๊ฐ€์žฅ ํฐ ์†Œ๋ฆฌ์ธ ํ›„๋ ด์˜ ๋นต๋นต ์ง€๋ฅด๋Š” ์†Œ๋ฆฌ์˜ ํฌ๊ธฐ์— ๊ฒŒ์ธ์„ ๋งž์ถœ ์ˆ˜ ๋ฐ–์— ์—†๊ณ , ๊ทธ๋Ÿฌ๋ฉด ๋„์ž…๋ถ€์˜ ์ž”์ž”ํ•œ ๋ถ€๋ถ„์€ ๊ฑฐ์˜ ์ž…๋ ฅ๋˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.

    buzz 창법에 따라 조금씩 다르지만 이 음량 차이는 생각보다 크다.

    ์ด๋•Œ ์ปดํ”„๋ ˆ์„œ๋กœ ์ž…๋ ฅ ๊ฒŒ์ธ์„ ์ ๋‹นํ•œ ์ˆ˜์ค€์œผ๋กœ ๋†’ํ˜€์ฃผ๊ณ  ๋„ˆ๋ฌด ํฐ ์†Œ๋ฆฌ๋Š” ์••์ถ•ํ•˜์—ฌ ๋…ธ๋ž˜ ๋„์ž…๋ถ€์˜ ์ž‘์€ ์†Œ๋ฆฌ์™€ ํ›„๋ ด๋ถ€์˜ ํฐ ์†Œ๋ฆฌ์˜ ๊ฒฉ์ฐจ๋ฅผ ์ขํ˜€ ์ „์ฒด์ ์ธ ์†Œ๋ฆฌ์˜ ํฌ๊ธฐ๋ฅผ ๋งž์ถ”๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

    audio compression Threshold를 넘어선 세기의 신호를 압축해서 Threshold 밑으로 들어가도록 만든다

    ๋˜ํ•œ ํ•„์ž๊ฐ€ ์ปดํ”„๋ ˆ์„œ ์†Œ๋ฆฌ๋ฅผ ์••์ถ•ํ•œ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ์†Œ๋ฆฌ๋ฅผ ์••์ถ•ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋ญ”์ง€ ์ž˜ ์ดํ•ด๊ฐ€ ์•ˆ๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์ผ๋ฐ˜์ ์ธ ์Œ์›์—์„œ ๋“ฃ๊ณ  ์žˆ๋Š” ํฝ!, ํƒ! ํ•˜๋Š” ๊น”๋”ํ•œ ๋“œ๋Ÿผ์†Œ๋ฆฌ๊ฐ€ ๋ฐ”๋กœ ์••์ถ•๋œ ์†Œ๋ฆฌ์ด๋‹ค. (๋ณดํ†ต ์ด๋ ‡๊ฒŒ ํŒํŒ์น˜๋Š” ์†Œ๋ฆฌ๋ฅผ Damping์ด๋ผ๊ณ  ํ•œ๋‹ค.)

    ์ผ๋ฐ˜์ ์œผ๋กœ ๋“œ๋Ÿผ์„ ๋…น์Œํ•˜๋ฉด ๋“œ๋Ÿผ ํŠน์œ ์˜ ํ†ต์ด ์šธ๋ฆฌ๋Š” ์ž”ํ–ฅ์ด ๋‚จ๋Š”๋ฐ, ์ด ์†Œ๋ฆฌ๋ฅผ ์ปดํ”„๋ ˆ์„œ๋กœ ์••์ถ•ํ•˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋“ฃ๋Š” ๊น”๋”ํ•œ ๋“œ๋Ÿผ์†Œ๋ฆฌ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

    ๊ทธ ์™ธ์—๋„ ๋ฒ ์ด์Šค์— ์ปดํ”„๋ ˆ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹จ๋‹จํ•œ ๋Š๋‚Œ์„ ๋ถ€์—ฌํ•˜๊ฑฐ๋‚˜ ๋ฉ€๋ฆฌ ์žˆ๋Š” ์†Œ๋ฆฌ๋ฅผ ๊ฐ€๊นŒ์ด๋กœ ๋Œ์–ด์˜ค๊ฑฐ๋‚˜ ๊ทธ ๋ฐ˜๋Œ€ ์—ญํ• ๋„ ํ•  ์ˆ˜ ์žˆ๋Š” ๋“ฑ, ์ปดํ”„๋ ˆ์„œ๋งŒ ์ž˜ ์‚ฌ์šฉํ•ด๋„ ์†Œ๋ฆฌ์— ๊ต‰์žฅํžˆ ๋งŽ์€ ๋Š๋‚Œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ•„์ž์—๊ฒŒ ์‚ฌ์šด๋“œ ์—”์ง€๋‹ˆ์–ด๋‹์„ ์•Œ๋ ค์ฃผ์…จ๋˜ ์„ ์ƒ๋‹˜๋„ ์ปดํ”„๋ ˆ์„œ์˜ ์ค‘์š”์„ฑ์„ ๊ต‰์žฅํžˆ ๊ฐ•์กฐํ•˜์…จ๋˜ ๊ธฐ์–ต์ด ๋‚œ๋‹ค.

    ์ปดํ”„๋ ˆ์„œ๋Š” ๋ช‡๊ฐ€์ง€ ๊ฐ’๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹ ํ˜ธ๋ฅผ ์–ธ์ œ๋ถ€ํ„ฐ ์••์ถ•ํ•  ๊ฒƒ์ธ์ง€, ์–ด๋Š ์ •๋„์˜ ์†๋„๋กœ ์••์ถ•ํ•  ๊ฒƒ์ธ์ง€์™€ ๊ฐ™์€ ์„ธํŒ…์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ๋‹ค. HTML5 Audio API์—์„œ ์ œ๊ณตํ•˜๋Š” DynamicsCompressorNode๋„ ์ด ๊ฐ’๋“ค์„ ๋™์ผํ•˜๊ฒŒ ์ œ๊ณตํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์šฐ๋ฆฌ๋Š” ์ด ๊ฐ’๋“ค์ด ์–ด๋–ค ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ์•Œ์•„์•ผ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ด ๋…ธ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

    Threshold

    Threshold๋Š” ์†Œ๋ฆฌ๋ฅผ ์–ด๋Š ํฌ๊ธฐ๋ถ€ํ„ฐ ์••์ถ•ํ•  ๊ฒƒ์ธ์ง€๋ฅผ ์ •ํ•˜๋Š” ์ž„๊ณ„์ ์„ ์˜๋ฏธํ•œ๋‹ค. ๋‹จ์œ„๋Š” DB(๋ฐ์‹œ๋ฒจ)์„ ์‚ฌ์šฉํ•œ๋‹ค.

    Ratio

    Ratio๋Š” Threshold๋ฅผ ๋„˜์€ ์†Œ๋ฆฌ๊ฐ€ ์–ด๋Š ์ •๋„์˜ ๋น„์œจ๋กœ ์ค„์–ด๋“ค ๊ฒƒ์ธ์ง€๋ฅผ ์ •ํ•˜๋Š” ๊ฐ’์ด๋‹ค. ์ด ๊ฐ’์€ ์ž…๋ ฅ:์ถœ๋ ฅ์˜ ๋น„๋ฅผ ์˜๋ฏธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” 2:1, 5:1์™€ ๊ฐ™์€ ๋น„์œจ๋กœ ์ด์•ผ๊ธฐํ•œ๋‹ค.

    ratio

    ํ•˜์ง€๋งŒ HTML5 Audio API์˜ ์†์„ฑ์—์„œ๋Š” ๋‹จ์œ„๊ฐ€ ์กฐ๊ธˆ ๋‹ค๋ฅด๋‹ค. ๊ณต์‹ ๋ฌธ์„œ์—๋Š” โ€œ์ถœ๋ ฅ ๊ฐ’์˜ 1db๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ db๊ฐ’โ€์ด๋ผ๊ณ  ์ ํ˜€์žˆ๋Š”๋ฐ ๊ทธ๋ƒฅ ์ด ์†์„ฑ์— 12๋ฅผ ํ• ๋‹นํ•˜๋ฉด ์••์ถ• ๋น„์œจ์ด 12:1๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. (๊ณต๋Œ์ด๋“ค ํŠน์ง•์ธ ์–ด๋ ต๊ฒŒ ๋งํ•˜๊ธฐ๊ฐ€ ๋ฐœ๋™ํ–ˆ๋‹ค)

    ๋ณดํ†ต ์ปดํ”„๋ ˆ์„œ๋ฅผ ์ ๋‹นํžˆ ๊ฑธ์—ˆ๋‹ค๊ณ  ํ•˜๋ฉด 4:1 ์ •๋„์˜ ๋น„์œจ์„ ๋งํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์†์„ฑ์˜ ๊ธฐ๋ณธ ๊ฐ’์ธ 12:1์€ ์ƒ๋‹นํžˆ ํ•˜๋“œํ•œ ์••์ถ• ๋น„์œจ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

    Attack

    Attack์€ ์†Œ๋ฆฌ๋ฅผ ์–ด๋Š ์ •๋„์˜ ๋น ๋ฅด๊ธฐ๋กœ ์••์ถ•ํ•  ๊ฒƒ์ธ์ง€๋ฅผ ์ •ํ•˜๋Š” ๊ฐ’์ด๋‹ค. Threshold๋ฅผ ๋„˜์€ ๊ฐ’์„ ์–ผ๋งˆ๋‚˜ ๋น ๋ฅด๊ฒŒ ๋•Œ๋ ค์„œ ๋ˆŒ๋Ÿฌ ๋‹ด์„ ์ง€๋ฅผ ์ •ํ•˜๋ฉด ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ž. ๋งŽ์€ ๋ถ„๋“ค์ด ์—ฌ๊ธฐ์„œ ์ •ํ•ด์ฃผ๋Š” ์–ดํƒ ํƒ€์ž„์ด โ€œAttack์ด ์‹œ์ž‘๋˜๋Š” ์‹œ๊ฐ„โ€์œผ๋กœ ์ž˜๋ชป ์•Œ๊ณ  ์žˆ๋Š” ๋ฐ, ์‚ฌ์‹ค ์‹ ํ˜ธ์˜ ํฌ๊ธฐ๊ฐ€ Threshold๋ฅผ ๋„˜์œผ๋ฉด Attack ์ž์ฒด๋Š” ๋ฐ”๋กœ ์‹œ์ž‘๋œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ •ํ•ด์ฃผ๋Š” ์–ดํƒ ํƒ€์ž„์€ ์ •ํ•ด์ง„ โ€œRatio๋กœ ์ •ํ•ด์ค€ ๋น„์œจ๊นŒ์ง€ ๋„๋‹ฌํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„โ€์ด๋‹ค.

    ๋‹จ์œ„๋Š” ๋ณดํ†ต ๋ฐ€๋ฆฌ์ดˆ(ms)๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ Audio API์—์„œ๋Š” ์ดˆ(seconds)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

    Release

    Attack์ด ์†Œ๋ฆฌ๋ฅผ ๋ˆ„๋ฅด๋Š” ๋น ๋ฅด๊ธฐ์˜€๋‹ค๋ฉด Release๋Š” ์••์ถ•ํ•œ ์†Œ๋ฆฌ๋ฅผ ์–ด๋Š ์ •๋„์˜ ๋น ๋ฅด๊ธฐ๋กœ ๋‹ค์‹œ ํ’€์–ด์ค„ ๊ฒƒ์ธ๊ฐ€๋ฅผ ์ •ํ•˜๋Š” ๊ฐ’์ด๋‹ค. ์ด๋•Œ ํ’€์–ด์ฃผ๋Š” ๊ฐ’์€ ์†Œ๋ฆฌ์˜ ์›๋ž˜ ํฌ๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ํ‘œ์ค€ ์Œ๋Ÿ‰์ธ 10db์— ๋„๋‹ฌํ•˜๋Š” ์‹œ๊ฐ„์„ ๋ชฉํ‘œ๋กœ ํ•œ๋‹ค.

    Release๋„ Attack๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋‹จ์œ„๋Š” ๋ณดํ†ต ๋ฐ€๋ฆฌ์ดˆ(ms)๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ Audio API์—์„œ๋Š” ์ดˆ(seconds)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

    Knee

    Knee๋Š” ์‚ฌ์‹ค ๋Œ€๋ถ€๋ถ„์˜ ํ•˜๋“œ์›จ์–ด ์ปดํ”„๋ ˆ์„œ์—๋Š” ์—†๋Š” ๊ธฐ๋Šฅ์ด์ง€๋งŒ ์†Œํ”„ํŠธ์›จ์–ด ์ปดํ”„๋ ˆ์„œ์—์„œ๋Š” ๊ฝค ์ž์ฃผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. ์ด ๊ฐ’์€ ์ปดํ”„๋ ˆ์„œ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ ์šฉ๋  ๊ฒƒ์ธ์ง€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.

    hard soft compression

    ์œ„ ๊ทธ๋ฆผ์˜ ๊ทธ๋ž˜ํ”„์˜ ๊บพ์ด๋Š” ์ •๋„๊ฐ€ ์ปดํ”„๋ ˆ์„œ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์„œ์„œํžˆ ์ ์šฉ๋˜๋Š”์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋‹ค. ์ด๋•Œ ๋น ๋ฅด๊ฒŒ ํŒ! ์ ์šฉํ•˜๋Š” ์ปดํ”„๋ ˆ์…˜์„ Hardํ•˜๋‹ค๊ณ  ํ•˜๊ณ  ์ฒœ์ฒœํžˆ ์ ์šฉํ•˜๋Š” ์ปดํ”„๋ ˆ์…˜์„ Softํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.

    Compressror ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

    ์‚ฌ์‹ค ์œ„์—์„œ ์ด์•ผ๊ธฐ ํ–ˆ๋“ฏ์ด HTML5 Audio API๋Š” ์ž์ฒด์ ์œผ๋กœ DynamicsCompressorNode๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ ์†Œ๋ฆฌ๋ฅผ ์••์ถ•ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง์ ‘ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋‹จ์ง€ ๋…ธ๋“œ๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์—ฐ๊ฒฐํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋  ๋ฟ์ด๋‹ค.

    ์ด๋ฒˆ์—๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์—…๋กœ๋“œํ•œ ์˜ค๋””์˜ค ํŒŒ์ผ์—์„œ ์˜ค๋””์˜ค ๋ฒ„ํผ๋ฅผ ์ถ”์ถœํ•˜์—ฌ ์†Œ์Šค ๋…ธ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ <audio> ํƒœ๊ทธ์—์„œ ์ถ”์ถœํ•˜์—ฌ ์†Œ์Šค ๋…ธ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค. (์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊ฐ„๋‹จํ•ด์ง„๋‹ค) ์ง€๊ธˆ ์ƒ์„ฑํ•œ ์†Œ์Šค๋…ธ๋“œ๋Š” ์•ž์œผ๋กœ ๋‹ค๋ฅธ ์ดํŽ™ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ๋„ ๊ณ„์† ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.

    const audioContext = new (AudioContext || webkitAudioContext)();
    const audioDOM = document.getElementById('my-audio');
    const sourceNode = audioContext.createMediaElementSource(audioDOM);
    
    const threshold = -24;
    const attack = 0.003;
    const release = 0.25;
    const ratio = 12;
    const knee = 30;
    
    const compressorNode = audioContext.createDynamicsCompressor();
    compressorNode.threshold.setValueAtTime(threshold, audioContext.currentTime);
    compressorNode.attack.setValueAtTime(attack, audioContext.currentTime);
    compressorNode.release.setValueAtTime(release, audioContext.currentTime);
    compressorNode.ratio.setValueAtTime(ratio, audioContext.currentTime);
    compressorNode.knee.setValueAtTime(knee, audioContext.currentTime);
    
    const inputGainNode = audioContext.createGain();
    const outputGainNode = audioContext.createGain();
    
    sourceNode.connect(inputGainNode);
    inputGainNode.connect(compressorNode);
    compressorNode.connect(outputGainNode);
    outputGainNode.connect(audioContext.destination);

    ํ•„์ž๋Š” ์†Œ์Šค -> ๊ฒŒ์ธ -> ์ปดํ”„๋ ˆ์„œ -> ๊ฒŒ์ธ์˜ ์ˆœ์„œ๋กœ ์˜ค๋””์˜ค ์†Œ์Šค์˜ ํ๋ฆ„์„ ์ƒ์„ฑํ–ˆ๋Š”๋ฐ, ์‚ฌ์‹ค ์ด๊ฑด ๊ฐœ์ธ์˜ ์ทจํ–ฅ์ด๋‹ค. ํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ ๋Œ€๋ถ€๋ถ„์˜ ์ปดํ”„๋ ˆ์„œ๋Š” ์ธํ’‹ ๊ฒŒ์ธ๊ณผ ์•„์›ƒํ’‹ ๊ฒŒ์ธ์„ ๋ชจ๋‘ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ ํ•„์ž๋„ ์ด์™€ ๋™์ผํ•˜๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋‹ค.

    ์ดํ›„ ์†Œ์Šค๋…ธ๋“œ๋ฅผ ์žฌ์ƒํ•ด๋ณด๋ฉด ์••์ถ•๋œ ์†Œ๋ฆฌ๋ฅผ ๋“ค์„ ์ˆ˜ ์žˆ๊ธด ํ•œ๋ฐ, ์‚ฌ์‹ค ์‚ฌ์šด๋“œ ์—”์ง€๋‹ˆ์–ด๊ฐ€ ์•„๋‹Œ ์ผ๋ฐ˜์ธ์ด ์†Œ๋ฆฌ์˜ ๋ฏธ์„ธํ•œ ์••์ถ•์˜ ์ •๋„๋ฅผ ๋Š๋ผ๊ธฐ๋Š” ํž˜๋“œ๋ฏ€๋กœ ์œ„์˜ ๊ฐ’๋“ค์„ ์กฐ๊ธˆ ๊ทน๋‹จ์ ์œผ๋กœ ๋ฐ”๊ฟ”๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

    Reverb

    reverb

    ๋ฆฌ๋ฒ„๋ธŒ(Reverb)๋Š” ์†Œ๋ฆฌ์— ์šธ๋ฆผ์„ ํ†ตํ•ด ๊ณต๊ฐ„๊ฐ์„ ๋ถ€์—ฌํ•˜๋Š” ๊ณต๊ฐ„๊ณ„ ์ดํŽ™ํ„ฐ์ด๋‹ค. ์†Œ๋ฆฌ์— ์šธ๋ฆผ์„ ํ†ตํ•ด ๊ณต๊ฐ„๊ฐ์„ ๋ถ€์—ฌํ•œ๋‹ค๋Š” ๊ฒŒ ์–ด๋–ค ์˜๋ฏธ์ผ๊นŒ?

    ์‚ฌ์‹ค ์šฐ๋ฆฌ๋Š” ์†Œ๋ฆฌ๋ฅผ ๋“ฃ๊ณ  ํ˜„์žฌ ์žˆ๋Š” ๊ณต๊ฐ„์ด ๋„“์€์ง€ ์ข์€์ง€, ์ด ๊ณต๊ฐ„์ด ๊ฑฐ์นœ ๋ฒฝ๋ฉด์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋Š”์ง€, ์•„๋‹ˆ๋ฉด ์œ ๋ฆฌ๊ฐ™์€ ๋งจ๋“ค๋งจ๋“คํ•œ ๊ณต๊ฐ„์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋Š”์ง€๋ฅผ ๋Œ€๋žต์ ์œผ๋กœ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ ์ฐจ์ด๊ฐ€ ์›Œ๋‚™ ๋ฏธ์„ธํ•ด์„œ ํ›ˆ๋ จ๋˜์ง€ ์•Š์€ ์‚ฌ๋žŒ์ด๋ผ๋ฉด ์•Œ์•„์ฑ„๊ธฐ ํž˜๋“ค ๋ฟ์ด๋‹ค.

    ์–ด๋–ป๊ฒŒ ์ด๋Ÿฐ ์ผ์ด ๊ฐ€๋Šฅํ• ๊นŒ? ๋ฐ”๋กœ ์†Œ๋ฆฌ์˜ ๋ฐ˜์‚ฌ์— ์˜ํ•œ ์ž”ํ–ฅ ๋•Œ๋ฌธ์ด๋‹ค. ๋จผ์ €, ์†Œ๋ฆฌ๋ฅผ ๋“ฃ๊ณ  ๊ณต๊ฐ„์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ์›๋ฆฌ๋Š” ๊ฐ„๋‹จํ•˜๋‹ค. ํ•„์ž๊ฐ€ ์–ด๋–ค ๋ฐฉ ์•ˆ์—์„œ ์†Œ๋ฆฌ๋ฅผ ์™!ํ•˜๊ณ  ์ง€๋ฅธ ๋’ค ์–ผ๋งˆ ํ›„์— ์ฒซ๋ฒˆ์งธ ๋ฐ˜์‚ฌ์Œ์ด ๋“ค๋ฆฌ๋Š”์ง€๋ฅผ ๊ฐ์ง€ํ•˜๋ฉด ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์ด ์ฒซ๋ฒˆ์งธ ๋ฐ˜์‚ฌ์Œ์€ ms ๋‹จ์œ„์˜ ๊ต‰์žฅํžˆ ๋น ๋ฅธ ์†๋„๋กœ ๋‹ค์‹œ ํ•„์ž์—๊ฒŒ ๋Œ์•„์˜ค๊ธฐ ๋•Œ๋ฌธ์— 1์ดˆ, 2์ดˆ ์ด๋ ‡๊ฒŒ ์„ธ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ทธ๋ƒฅ ๋Š๊ปด์•ผํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

    ์ด๋•Œ ์ด ๋ฐ˜์‚ฌ์Œ์„ ์ดˆ๊ธฐ ๋ฐ˜์‚ฌ์Œ(Early Reflection)์ด๋ผ๊ณ  ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ๋‹ค. ์†Œ๋ฆฌ๊ฐ€ ํ•œ๋ฒˆ ๋ฐ˜์‚ฌ๋˜์–ด ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ท€๋กœ ์ „๋‹ฌ๋œ ๋’ค์—๋„ ๋ฐ˜์‚ฌ๋Š” ๊ณ„์† ๋  ๊ฒƒ์ด๋‹ค. ์ด๋•Œ ์ด ์ž”ํ–ฅ๋“ค์€ ๊ณต๊ฐ„์˜ ์‚ฌ๋ฐฉํŒ”๋ฐฉ์œผ๋กœ ๋ถ€๋”ชํžˆ๊ณ  ๋ฐ˜์‚ฌ๋˜์–ด ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ท€๋กœ ๋‹ค์‹œ ๋Œ์•„์˜ฌ ๊ฒƒ์ด๋‹ค.

    reflection 초록색 선이 초기 반사음, 사방팔방 부딪히는 파란색 선이 바로 잔향이다

    ์ด๋•Œ ์ด ์ž”ํ–ฅ์ด ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ๋“ค๋ฆฌ๋Š”๊ฐ€, ์–ผ๋งˆ๋‚˜ ์„ ๋ช…ํ•˜๊ฒŒ ๋“ค๋ฆฌ๋Š”๊ฐ€์™€ ๊ฐ™์€ ํŠน์„ฑ์ด ๋ฐฉ์˜ ์žฌ์งˆ์„ ๊ฒฐ์ •ํ•œ๋‹ค. ์ด์•ผ๊ธฐ๋งŒ ๋“ค์œผ๋ฉด ์ด๋ ‡๊ฒŒ ์†Œ๋ฆฌ๋ฅผ ๋“ฃ๊ณ  ๊ณต๊ฐ„์„ ํŒ๋ณ„ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒƒ ๊ฐ™์ง€๋งŒ ์—ฌ๋Ÿฌ๋ถ„์ด ์ด๋ฏธ ํ‰์†Œ์— ๋“ฃ๊ณ  ์Œ์•…์—๋Š” ๋ชจ๋‘ ์ด ์›๋ฆฌ๋ฅผ ์ ์šฉํ•œ ๊ณต๊ฐ„์  ์„ค๊ณ„๊ฐ€ ํ•จ๊ป˜ ๋‹ด๊ฒจ์žˆ๋‹ค.

    ์ด๋ ‡๊ฒŒ ๋ฆฌ๋ฒ„๋ธŒ๋Š” ๋ง ๊ทธ๋Œ€๋กœ ์ž”ํ–ฅ์„ ๋งŒ๋“ค์–ด๋‚ด๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋“œ์›จ์–ด ๋ฆฌ๋ฒ„๋ธŒ ์ค‘์—์„œ๋Š” ์Šคํ”„๋ง์ด๋‚˜ ์ฒ ํŒ ๋“ฑ์˜ ์žฌ๋ฃŒ๋ฅผ ์žฅ๋น„ ๋‚ด๋ถ€์— ๋„ฃ์–ด๋†“๊ณ  ์˜ค๋””์˜ค๋ฅผ ์žฌ์ƒํ•˜์—ฌ ์žฌ๋ฃŒ๊ฐ€ ๋–จ๋ฆฌ๋ฉฐ ๋ฐœ์ƒํ•œ ์ž”ํ–ฅ์„ ์ฆํญํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์žˆ๋‹ค. ์ฆ‰, ๋œฏ์–ด๋ณด๋ฉด ์žฅ๋น„ ๋‚ด๋ถ€์— ์Šคํ”„๋ง์ด๋‚˜ ์ฒ ํŒ ํ•˜๋‚˜ ๋”ธ๋ž‘ ๋“ค์–ด์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. (์ด๋Ÿฐ ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ๋กœ ์ข‹์€ ์†Œ๋ฆฌ๋ฅผ ๋ฝ‘๋Š”๋‹ค๋Š” ๊ฒŒ ๋” ๋ฌด์„ญโ€ฆ)

    ๊ทธ๋Ÿฌ๋‚˜ ๋ฆฌ๋ฒ„๋ธŒ๋ฅผ ์†Œํ”„ํŠธ์›จ์–ด๋กœ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” ์ด์•ผ๊ธฐ๊ฐ€ ์กฐ๊ธˆ ๋‹ค๋ฅด๋‹ค. ์ปดํ“จํ„ฐ๋Š” ์Šคํ”„๋ง์ด๋‚˜ ์ฒ ํŒ์˜ ๋–จ๋ฆผ๊ณผ ๊ฐ™์€ ์ž์—ฐ์ ์ธ ์•„๋‚ ๋กœ๊ทธ ์‹ ํ˜ธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ง์ ‘ ๊ณ„์‚ฐ์„ ํ†ตํ•ด ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค. ์ด๋•Œ ์†Œํ”„ํŠธ์›จ์–ด ๋ฆฌ๋ฒ„๋ธŒ๋Š” ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜๋กœ ๋‚˜๋ˆ„์–ด์ง€๋Š”๋ฐ ๋ฐ”๋กœ Convolution Reverb์™€ Algorithm Reverb์ด๋‹ค.

    ํ•˜์ง€๋งŒ ์ด ํฌ์ŠคํŒ…์—์„œ ๋‘ ๋ฆฌ๋ฒ„๋ธŒ๋ฅผ ๋ชจ๋‘ ๊ตฌํ˜„ํ•˜๊ธฐ์—๋Š” ๊ธ€์ด ๋„ˆ๋ฌด ๊ธธ์–ด์งˆ ๊ฒƒ ๊ฐ™์œผ๋ฏ€๋กœ ์•„์‰ฌ์šด๋Œ€๋กœ ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ์— ์ดˆ์ ์„ ๋งž์ถฐ ์ง„ํ–‰ํ•˜๊ฒ ๋‹ค. (์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฆฌ๋ฒ„๋ธŒ๋งŒ ํ•ด๋„ ํฌ์ŠคํŒ… ํ•˜๋‚˜ ๋ถ„๋Ÿ‰์ด๋‹ค.)

    Convolution Reverb

    ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ(Convolution Reverb)๋Š” ์‹ค์ œ ๊ณต๊ฐ„์˜ ์ž”ํ–ฅ์„ ๋…น์Œํ•œ ํ›„์— ์ž”ํ–ฅ ์˜ค๋””์˜ค ์†Œ์Šค์™€ ์›๋ณธ ์˜ค๋””์˜ค ์†Œ์Šค๋ฅผ ์‹ค์ œ ๊ณต๊ฐ„์˜ ์šธ๋ฆผ์„ ์›๋ณธ ์˜ค๋””์˜ค ์†Œ์Šค์— ํ•ฉ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

    ์ด๋•Œ ์‹ค์ œ ๊ณต๊ฐ„์˜ ์ž”ํ–ฅ์„ ๋…น์Œํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ•์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด, ๋…น์Œํ•˜๊ณ ์ž ํ•˜๋Š” ๊ณต๊ฐ„์— ์ˆœ์ˆ˜ํ•œ ์‚ฌ์ธํŒŒ(Sine Wave)์˜ ์†Œ๋ฆฌ๋ฅผ ๋‚ฎ์€ ์ฃผํŒŒ์ˆ˜๋ถ€ํ„ฐ ๋†’์€ ์ฃผํŒŒ์ˆ˜๊นŒ์ง€ ์ญˆ์šฐ์šฐ์šฑ ์ด์–ด์„œ ํ‹€๊ณ  ๊ทธ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ž”ํ–ฅ์„ ๋…น์Œํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

    ir recording 공간의 IR을 녹음하는 모습 - 출처: http://www.alanjshan.com/impulse-response-capture/

    ์ด๋•Œ ์ด ์ž”ํ–ฅ ์‹ ํ˜ธ๋ฅผ Impulse Response(IR)์ด๋ผ๊ณ  ๋ถ€๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ๋Š” IR ๋ฆฌ๋ฒ„๋ธŒ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ๋„ ๋ถˆ๋ฆฐ๋‹ค. ์ด๋ ‡๊ฒŒ ๋…น์Œํ•œ IR์€ ์›๋ณธ ์†Œ์Šค์— ์ปจ๋ณผ๋ฃจ์…˜(Convolution), ๋˜๋Š” ํ•ฉ์„ฑ๊ณฑ์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ์šฐ๋Š” ์—ฐ์‚ฐ์„ ํ†ตํ•ด ํ•ฉ์ณ์ง€๊ฒŒ ๋œ๋‹ค.

    ์ด ์ปจ๋ณผ๋ฃจ์…˜์ด๋ผ๋Š” ๊ฐœ๋…์„ ์ˆ˜ํ•™์ ์œผ๋กœ ์ ‘๊ทผํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋จธ๋ฆฌ๋„ ์•„ํ”„๊ณ  ๋˜ ํฌ์ŠคํŒ…์ด ๊ธธ์–ด์ง€๋‹ˆ๊นŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ •์˜ํ•ด๋ณด์ž๋ฉด, ๊ทธ๋ƒฅ ์„œ๋กœ ๋‹ค๋ฅธ ์ •๋ณด๋“ค์„ ์„ž๋Š” ๊ฒƒ์ด๋ผ๊ณ  ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ํฌ์ŠคํŒ…์„ ์ฝ๋Š” ๋ถ„๋“ค์€ ์•„๋งˆ ๊ฐœ๋ฐœ์ž ๋ถ„๋“ค์ด ๋งŽ์„ ํ…Œ๋‹ˆ ์šฐ๋ฆฌ์—๊ฒŒ ์ข€ ๋” ์นœ์ˆ™ํ•œ ๋จธ์‹ ๋Ÿฌ๋‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์ปจ๋ณผ๋ฃจ์…˜์„ ์„ค๋ช…ํ•˜์ž๋ฉด ํ•™์Šต ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ค‘ ํ•˜๋‚˜์ธ CNN(Convolution Neural Network)์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

    CNN์—์„œ๋„ ์ฒซ๋ฒˆ์งธ ๋ ˆ์ด์–ด์˜ ์ด๋ฏธ์ง€๋ฅผ ๋‘๋ฒˆ์งธ ๋ ˆ์ด์–ด๋กœ ๋ณด๋‚ผ ๋•Œ ํ–‰๋ ฌ๋กœ ๊ตฌํ˜„ํ•œ ์ปค๋„(๋˜๋Š” ํ•„ํ„ฐ)์™€ ์ด๋ฏธ์ง€๋ฅผ ์„ž์–ด์„œ ํ”ผ์ฒ˜๋งต์„ ์ƒ์„ฑํ•œ ํ›„ ๋‹ค์Œ ๋ ˆ์ด์–ด๋กœ ๋ณด๋‚ด๊ฒŒ๋œ๋‹ค. ์ด๋•Œ ์ฒซ๋ฒˆ์งธ ๋ ˆ์ด์–ด์˜ ์ด๋ฏธ์ง€์™€ ์ปค๋„์˜ ์ •๋ณด๊ฐ€ ์„ž์ธ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

    convolution 원본 이미지와 커널을 섞어서 새로운 정보인 피처맵을 만들어낸다

    ์˜ค๋””์˜ค์—์„œ์˜ ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ๋„ ์ด์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” ์„ž์–ด์•ผํ•˜๋Š” ์ •๋ณด๊ฐ€ ์›๋ณธ ์†Œ์Šค์™€ IR์ด ๋œ ๊ฒƒ ๋ฟ์ด๋‹ค.

    ์ปจ๋ณผ๋ฃจ์…˜์€ ์›๋ณธ ์†Œ์Šค์™€ IR์ด๋ผ๋Š” ๋‘ ์˜ค๋””์˜ค ์†Œ์Šค์˜ ์ฃผํŒŒ์ˆ˜ ์ŠคํŽ™ํŠธ๋Ÿผ์„ ๊ณฑํ•˜๋Š” ๊ณผ์ •์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ํ†ตํ•ด ๋‘ ์†Œ์Šค ๊ฐ„์— ๊ฒน์น˜๋Š” ์ฃผํŒŒ์ˆ˜๋Š” ๊ฐ•์กฐ๋˜๊ณ  ๊ฒน์น˜์ง€ ์•Š๋Š” ์ฃผํŒŒ์ˆ˜๋Š” ๊ฐ์‡ ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ์›๋ณธ ์†Œ์Šค์™€ IR ๊ฐ„ ๊ฒน์น˜๋Š” ์ฃผํŒŒ์ˆ˜๊ฐ€ ๊ฐ•์กฐ๋˜๋ฉด ์›๋ณธ ์†Œ์Šค๋Š” IR์˜ ์Œ์งˆ์˜ ํŠน์„ฑ์„ ๋„๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๊ฒŒ ๋ฐ”๋กœ ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ์˜ ์›๋ฆฌ์ด๋‹ค.

    signal convolution 원본 신호와 녹음한 IR 신호를 컨볼루션 연산한 모습

    ์‚ฌ์‹ค HTML5 Audio API๋Š” ์ปจ๋ณผ๋ฃจ์…˜ ์—ฐ์‚ฐ์„ ๋Œ€์‹  ์ˆ˜ํ–‰ํ•ด์ฃผ๋Š” ConvolverNode๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ปจ๋ณผ๋ฃจ์…˜์ด ๋ฌด์—‡์ธ์ง€ ๋ชฐ๋ผ๋„ ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ๋Š” ์•„๋ฌด ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.

    ๊ทธ๋Ÿฌ๋‚˜ ์ ์–ด๋„ ์ด ์ดํŽ™ํ„ฐ๊ฐ€ 2๊ฐœ์˜ ์‹ ํ˜ธ ์ •๋ณด๋ฅผ ๊ณฑํ•ด์„œ ์ƒˆ๋กœ์šด ์‹ ํ˜ธ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ์›๋ฆฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„์•ผ ํ•„์ž๊ฐ€ ์™œ ์ด๋Ÿฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š”์ง€๋„ ์•Œ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€๋žต์ ์ธ ์„ค๋ช…์„ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

    ์–ด์จŒ๋“  ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ์˜ ๋Œ€๋žต์ ์ธ ์›๋ฆฌ๋ฅผ ํŒŒ์•…ํ–ˆ๋‹ค๋ฉด ์ด์ œ ๋ฐ”๋กœ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜์ž.

    Convolution Reverb ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

    ๋จผ์ € HTML5 Audio API๋Š” ReverbNode ๊ฐ™์€ ๊ฑด ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ•˜์ง€๋งŒ ์œ„์—์„œ ์„ค๋ช…ํ–ˆ๋“ฏ์ด ์ปจ๋ณผ๋ฃจ์…˜ ์—ฐ์‚ฐ์„ ์ง€์›ํ•˜๋Š” ConvolverNode๋ฅผ ์ œ๊ณตํ•ด์ฃผ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๋Š” ์ž”ํ–ฅ ์†Œ์Šค์ธ IR(Impulse Response)๋งŒ ์ง์ ‘ ๋งŒ๋“ค์–ด์ฃผ๋ฉด ๋œ๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฆฌ๋ฒ„๋ธŒ๋Š” wet๊ณผ dry๋ผ๋Š” ์ˆ˜์น˜๋กœ ์›๋ณธ ์†Œ์Šค์™€ ์ž”ํ–ฅ ์†Œ์Šค๋ฅผ ๋น„์œจ์— ๋งž๊ฒŒ ์„ž์„ ์ˆ˜ ์žˆ๋„๋ก ์ œ์ž‘๋˜๋ฏ€๋กœ ํ•„์ž๋„ ๋™์ผํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ๋‹ค.

    const mix = 0.5;
    const time = 0.01;
    const decay = 0.01;

    ๋ฆฌ๋ฒ„๋ธŒ์˜ ์‚ฌ์šฉํ•  3๊ฐœ์˜ ๋ณ€์ˆ˜๋ฅผ ๋จผ์ € ์„ค๋ช…ํ•˜์ž๋ฉด, mix๋Š” wet/dry์˜ ๋น„์œจ์„ ์˜๋ฏธํ•˜๊ณ , time์€ ์ž”ํ–ฅ์˜ ๊ธธ์ด, decay๋Š” ์ž”ํ–ฅ์ด ๊ฐ์†Œํ•˜๋Š” ๋น ๋ฅด๊ธฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋Ÿผ ์ด์ œ ์ด ๊ฐ’๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง์ ‘ IR์„ ์ƒ์„ฑํ•ด๋ณด์ž.

    function generateImpulseResponse () {
      const sampleRate = audioContext.sampleRate;
      const length = sampleRate * time;
      const impulse = audioContext.createBuffer(2, length, sampleRate);
    
      const leftImpulse = impulse.getChannelData(0);
      const rightImpulse = impulse.getChannelData(1);
    
      for (let i = 0; i < length; i++) {
        leftImpulse[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, deacy);
        rightImpulse[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, deacy);
      }
    
      return impulse;
    }

    ๋ญ”๊ฐ€ ๋ณต์žกํ•ด๋ณด์ด์ง€๋งŒ ๋œฏ์–ด๋ณด๋ฉด ๋ณ„ ๊ฑฐ ์—†๋‹ค. sampleRate๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ƒ์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” IR์˜ ์ƒ˜ํ”Œ๋ ˆ์ดํŠธ, ์ฆ‰ ์Œ์งˆ์„ ์˜๋ฏธํ•˜๊ณ  length๋Š” sampleRate * time, ์ฆ‰ time์ดˆ ๋งŒํผ์˜ ์ž”ํ–ฅ์„ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๋ฒ„ํผ์˜ ๊ธธ์ด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ƒฅ ๋ฒ„ํผ ๋…ธ๋“œ๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ•œ ๋‹ค์Œ -1 ~ 1์˜ ๋ฌด์ž‘์œ„ ๊ฐ’์„ ์ƒ์„ฑํ•œ ํ›„ 1 - i / length์— decay๋ฅผ ์ œ๊ณฑํ•œ ํ›„ ๋ฐฉ๊ธˆ ์ƒ์„ฑํ•œ ๋‚œ์ˆ˜์— ๊ณฑํ•ด์ค€๋‹ค. ์ด๋Ÿฌ๋ฉด i๊ฐ’์ด ์ปค์งˆ์ˆ˜๋ก ๊ฐ’์ด ์ž‘์•„์งˆ ๊ฒƒ์ด๊ณ , deacy ๊ฐ’์ด ์ปค์งˆ์ˆ˜๋ก ๋” ๋น ๋ฅด๊ฒŒ ์ž‘์•„์งˆ ๊ฒƒ์ด๋‹ค. ์ด๋Š” ์ž”ํ–ฅ์˜ ๊ฐ์‡ ๋ฅผ ํ‘œํ˜„ ํ•ด์ค€ ๊ฒƒ์ด๋‹ค. ์ดํ›„ ์ด ์ƒ˜ํ”Œ์„ ๋ฐฉ๊ธˆ ๋งŒ๋“  ๋ฒ„ํผ ๋…ธ๋“œ์— ์ญˆ๋ฅด๋ฅต ๋‹ด์•„์ฃผ๋ฉด ๋์ด๋‹ค.

    ์ด๋ ‡๊ฒŒ ์ƒ์„ฑ๋œ IR ๋ฒ„ํผ๋ฅผ ํŒŒํ˜•์œผ๋กœ ํ‘œํ˜„ํ•ด๋ณด๋ฉด ๋Œ€๋žต ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ชจ์–‘์„ ๊ฐ€์งˆ ๊ฒƒ์ด๋‹ค.

    decay

    ์จ˜, ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•˜๊ฒŒ IR๋ฅผ ์ƒ์„ฑํ•ด๋ณด์•˜๋‹ค. ์ด์ œ ConvolverNode๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›๋ณธ ์†Œ์Šค์™€ ์ด IR์„ ํ•ฉ์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ๋งŒ ๋‚จ์•˜๋‹ค. ๋ฆฌ๋ฒ„๋ธŒ ์ดํŽ™ํ„ฐ์˜ ์˜ค๋””์˜ค ํ๋ฆ„์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ํ•„์š”ํ•œ ๋…ธ๋“œ๋“ค์„ ๋จผ์ € ์ƒ์„ฑํ•ด๋ณด์ž.

    const inputNode = audioContext.createGain();
    const wetGainNode = audioContext.createGain();
    const dryGainNode = audioContext.createGain();
    const reverbNode = audioContext.createConvolver();
    const outputNode = audioContext.createGain();

    ์œ„์—์„œ๋„ ์„ค๋ช…ํ–ˆ๋“ฏ์ด ์ผ๋ฐ˜์ ์ธ ๋ฆฌ๋ฒ„๋ธŒ ์ดํŽ™ํ„ฐ๋Š” wet/dry๋ผ๋Š” ์ˆ˜์น˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›๋ณธ ์†Œ์Šค์™€ ๋ฆฌ๋ฒ„๋ธŒ๊ฐ€ ์ ์šฉ๋œ ์†Œ์Šค๋ฅผ ์„ž์–ด์„œ ์ถœ๋ ฅํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ์ด๋•Œ dryํ•œ ์†Œ์Šค๋Š” ๋ฆฌ๋ฒ„๋ธŒ ์ดํŽ™ํ„ฐ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ outputNode๋กœ ์—ฐ๊ฒฐ๋˜์„œ ์ถœ๋ ฅ๋˜์–ด์•ผ ํ•˜๋ฉฐ, wetํ•œ ์†Œ์Šค๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  reverbNode๋ฅผ ํ•œ๋ฒˆ ๊ฑฐ์น˜๊ณ  outputNode๋กœ ์ถœ๋ ฅ๋˜์–ด์•ผ ํ•œ๋‹ค.

    sourceNode.connect(inputNode);
    
    // Dry ์†Œ์Šค ๋…ธ๋“œ ์—ฐ๊ฒฐ
    inputNode.connect(dryGainNode);
    dryGainNode.connect(outputNode);
    dryGainNode.gain.value = 1 - mix;
    
    // IR์„ ์ƒ์„ฑํ•˜์—ฌ Convolver์˜ ์˜ค๋””์˜ค ๋ฒ„ํผ์— ์ž…๋ ฅํ•ด์ค€๋‹ค.
    reverbNode.buffer = generateImpulseResponse();
    
    // Wet ์†Œ์Šค ๋…ธ๋“œ ์—ฐ๊ฒฐ
    inputNode.connect(reverbNode);
    reverbNode.connect(wetGainNode);
    webGainNode.connect(outputNode);
    wetGainNode.gain.vaule = mix;
    
    outputNode.connect(audioContext.destination);

    ์ด๋ ‡๊ฒŒ ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด๋ณด์•˜๋‹ค. ์‚ฌ์‹ค ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ์˜ ํ€„๋ฆฌํ‹ฐ์— ๊ฐ€์žฅ ํฐ ์˜ํ–ฅ์„ ๋ผ์น˜๋Š” ๊ฒƒ์€ IR์˜ ํ€„๋ฆฌํ‹ฐ์ธ๋ฐ, ์šฐ๋ฆฌ๋Š” ๋Œ€์ถฉ ๋งŒ๋“  ์ƒ˜ํ”Œ ์˜ค๋””์˜ค๋กœ IR์„ ๋งŒ๋“ค์—ˆ์œผ๋ฏ€๋กœ ์ด ๋ฆฌ๋ฒ„๋ธŒ์˜ ํ€„๋ฆฌํ‹ฐ๋Š” ์ข‹์„ ์ˆ˜๊ฐ€ ์—†๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์†Œ์Šค ๋…ธ๋“œ๋ฅผ ์žฌ์ƒํ•ด์„œ ๋“ค์–ด๋ณด๋ฉด ์‹ ๊ธฐํ•˜๊ฒŒ๋„ ์†Œ๋ฆฌ์— ๊ณต๊ฐ„๊ฐ์ด ๋ถ€์—ฌ๋œ ๊ฒƒ์„ ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    ๋งŒ์•ฝ ๊ธฐํšŒ๊ฐ€ ๋œ๋‹ค๋ฉด ๋‹ค์Œ์—๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฆฌ๋ฒ„๋ธŒ์˜ ๊ตฌํ˜„์ฒด๋„ ํ•œ๋ฒˆ ํฌ์ŠคํŒ… ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค. ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฆฌ๋ฒ„๋ธŒ๋Š” ์‹ค์ œ ๊ณต๊ฐ„์˜ ์ž”ํ–ฅ์„ ๋…น์Œํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ 100% ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ๋งŒ ๊ตฌํ˜„๋œ ๋ฆฌ๋ฒ„๋ธŒ์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„ ์ธ์œ„์ ์ธ ๋Š๋‚Œ์ด ๋‚˜๊ธฐ๋Š” ํ•˜์ง€๋งŒ ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ์™€๋Š” ๋˜ ์ƒ‰๋‹ค๋ฅธ ๋Š๋‚Œ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์‚ฌ์šด๋“œ ์—”์ง€๋‹ˆ์–ด๋“ค์€ ์ด ๋‘๊ฐ€์ง€ ๋ฆฌ๋ฒ„๋ธŒ์˜ ํŠน์„ฑ์„ ํŒŒ์•…ํ•˜๊ณ  ์ ์žฌ์ ์†Œ์— ์‚ฌ์šฉํ•œ๋‹ค.

    ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ๋Š” ์˜คํžˆ๋ ค ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ๋ณด๋‹ค ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฆฌ๋ฒ„๋ธŒ ์ชฝ์ด ๋” ์ดํ•ด๊ฐ€ ์ž˜๋  ์ˆ˜ ์žˆ์œผ๋‚˜, ConvolverNode ํ•˜๋‚˜์™€ ๋Œ€์ถฉ ๋งŒ๋“  IR๋งŒ ์žˆ์œผ๋ฉด ๋‚˜๋จธ์ง€๋Š” ์•Œ์•„์„œ ๋‹ค ์—ฐ์‚ฐํ•ด์ฃผ๋Š” ์ปจ๋ณผ๋ฃจ์…˜ ๋ฆฌ๋ฒ„๋ธŒ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฆฌ๋ฒ„๋ธŒ๋Š” ์ง„์งœ ๋ฐ‘๋ฐ”๋‹ฅ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์•„์‰ฝ์ง€๋งŒ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฆฌ๋ฒ„๋ธŒ๋Š” ๋‹ค์Œ์— ํฌ์ŠคํŒ… ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

    ๋งŒ์•ฝ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ฆฌ๋ฒ„๋ธŒ์˜ ๊ตฌํ˜„์ฒด๊ฐ€ ๊ถ๊ธˆํ•˜์‹  ๋ถ„์€ ํ•„์ž์˜ ๊นƒํ—ˆ๋ธŒ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ์—์„œ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    Delay

    delay

    ๋”œ๋ ˆ์ด(Delay)๋Š” ๋ฆฌ๋ฒ„๋ธŒ์™€ ๊ฐ™์€ ๊ณต๊ฐ„๊ณ„ ์ดํŽ™ํ„ฐ์ด๊ณ  ์†Œ๋ฆฌ๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ๋“ค๋ ค์ค€๋‹ค๋Š” ์ ์ด ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— ๋น„์Šทํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ ์›๋ฆฌ์™€ ์šฉ๋„๋Š” ๋งŽ์ด ๋‹ค๋ฅด๋‹ค.

    ๋จผ์ €, ๋”œ๋ ˆ์ด๋Š” ๋‹จ์ˆœํžˆ ์†Œ๋ฆฌ๋ฅผ ๋ฐ˜๋ณตํ•˜๋Š” ํšจ๊ณผ์ด์ง€๋งŒ ๋ฆฌ๋ฒ„๋ธŒ๋Š” ๊ณต๊ฐ„ ๋‚ด์—์„œ์˜ ๋ณต์žกํ•œ ๋ฐ˜์‚ฌ์Œ์„ ํ‰๋‚ด๋‚ด๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ๋”œ๋ ˆ์ด๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ๋ฆฌ๋ฒ„๋ธŒ์™€ ๊ฐ™์€ ์ž์—ฐ์Šค๋Ÿฌ์šด ๊ณต๊ฐ„๊ฐ์„ ํ‘œํ˜„ํ•˜๊ธฐ๊ฐ€ ํž˜๋“ค๋‹ค.

    ๋ฐฉ๊ธˆ ๋งŒ๋“ค์–ด๋ดค๋˜ ๋ฆฌ๋ฒ„๋ธŒ ์ดํŽ™ํ„ฐ๋Š” ์‚ฌ์‹ค์ ์ธ ๊ณต๊ฐ„ ํ‘œํ˜„์ด ๋ชฉ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ปจ๋ณผ๋ฃจ์…˜์ด๋‚˜ ๋ณต์žกํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ ๋”œ๋ ˆ์ด๋Š” ๊ทธ๋ƒฅ ์›๋ณธ ์†Œ์Šค๋ฅผ ์ž ๊น ์ง€์—ฐ์‹œ์ผฐ๋‹ค๊ฐ€ n์ดˆ ํ›„์— ๋‹ค์‹œ ํ‹€์–ด์ฃผ๋ฉด์„œ ์กฐ๊ธˆ์”ฉ ์†Œ๋ฆฌ๋ฅผ ์ž‘๊ฒŒ ํ•ด์ฃผ๋ฉด ๋์ด๋‹ค.

    reverb vs delay Echo(Delay)와 Reverb의 차이

    ๋”œ๋ ˆ์ด๋Š” ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•œ ์›๋ฆฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ๋“œ๋Š” ๊ฒƒ ์—ญ์‹œ ๊ทธ๋ ‡๊ฒŒ ์–ด๋ ต์ง€ ์•Š๋‹ค.

    Delay ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

    HTML5์˜ Audio API๋Š” ์ธํ’‹์œผ๋กœ ๋ฐ›์€ ์‹ ํ˜ธ๋ฅผ ์ง€์—ฐ์‹œ์ผœ์„œ ๋‹ค์‹œ ์ถœ๋ ฅํ•˜๋Š” Delay Node๋ฅผ ์ œ๊ณตํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๋Š” ์ด ๋…ธ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋”œ๋ ˆ์ด ์ดํŽ™ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๊ทธ๋Ÿฌ๋‚˜ ๋‹จ์ˆœํžˆ DelayNode๋งŒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋‹จ ํ•œ๋ฒˆ์˜ ์ง€์—ฐ๋งŒ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๋Š” ํ•œ๊ฐ€์ง€ ์–Œ์ƒ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋”œ๋ ˆ์ด๋ฅผ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋‹ค. ๋จผ์ € ๋”œ๋ ˆ์ด์— ํ•„์š”ํ•œ ๋ณ€์ˆ˜๋“ค์„ ์„ ์–ธํ•ด๋ณด๋„๋ก ํ•˜์ž.

    const mix = 0.5;
    const feedback = 0.5;
    const time = 0.3;

    ๋ฆฌ๋ฒ„๋ธŒ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋Œ€๋ถ€๋ถ„์˜ ๋”œ๋ ˆ์ด ์ดํŽ™ํ„ฐ๋„ wet/dry ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ์›๋ณธ ์†Œ์Šค์™€ ๋”œ๋ ˆ์ด๋œ ์†Œ์Šค๋ฅผ ์„ž์–ด์„œ ์ถœ๋ ฅํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•„์ž๋„ ๋™์ผํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด์ค„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  feedback ๋ณ€์ˆ˜๋Š” ์›๋ณธ ์†Œ์Šค๊ฐ€ ํ•œ๋ฒˆ ๋”œ๋ ˆ์ด๋  ๋•Œ ๊ฐ์†Œ์‹œํ‚ฌ ์Œ๋Ÿ‰์ด๊ณ  time ๋ณ€์ˆ˜๋Š” ๋ฉ”์•„๋ฆฌ๋“ค์˜ ๊ฐ„๊ฒฉ์„ ์˜๋ฏธํ•œ๋‹ค. ๋”œ๋ ˆ์ด์— ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋“ค์„ ๋ชจ๋‘ ์„ ์–ธํ–ˆ๋‹ค๋ฉด ์ด์ œ ๋…ธ๋“œ๋“ค์„ ๋งŒ๋“ค ์ฐจ๋ ˆ์ด๋‹ค.

    const inputNode = audioContext.createGain();
    const wetGainNode = audioContext.createGain();
    const dryGainNode = audioContext.createGain();
    const feedbackNode = audioContext.createGain();
    const delayNode = audioContext.createDelay();
    const outputNode = audioContext.createGain();

    webGainNode์™€ dryGainNode๋Š” ๋ฆฌ๋ฒ„๋ธŒ์™€ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ƒฅ ๋„˜์–ด๊ฐ€๊ณ , ์ƒˆ๋กœ์šด ๋…ธ๋“œ์ธ feedbackNode์™€ delayNode์— ์ง‘์ค‘ํ•ด๋ณด์ž. ์‚ฌ์‹ค์ƒ ๋”œ๋ ˆ์ด ์ดํŽ™ํ„ฐ๋Š” ์ด ๋‘๊ฐœ์˜ ๋…ธ๋“œ๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค. ๋จผ์ €, ๋”œ๋ ˆ์ด ์ดํŽ™ํ„ฐ๊ฐ€ ํ•˜๋Š” ์ผ์— ๋Œ€ํ•ด์„œ ๋‹ค์‹œ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด์ž.

    ์ž…๋ ฅ -> ์ง€์—ฐ -> ๊ฐ์†Œ๋œ ์‹ ํ˜ธ ์ถœ๋ ฅ -> ์ž…๋ ฅ -> ์ง€์—ฐ -> ๊ฐ์†Œ๋œ ์‹ ํ˜ธ ์ถœ๋ ฅโ€ฆ

    ๋”œ๋ ˆ์ด ์ดํŽ™ํ„ฐ๊ฐ€ ํ•˜๋Š” ์ผ์€ ์ด๊ฒŒ ์ „๋ถ€๋‹ค. ์‹ ํ˜ธ๋ฅผ ์กฐ๊ธˆ์”ฉ ์ง€์—ฐ์‹œํ‚ค๊ณ  ๊ฐ์†Œ๋œ ์‹ ํ˜ธ๋ฅผ ๋‹ค์‹œ ์ถœ๋ ฅํ•˜๋Š” ์ผ์„ ๋ฐ˜๋ณตํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํ•„์ž๋Š” delayNode์™€ feedbackNode๋ฅผ ์„œ๋กœ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์ด ์ดํŽ™ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

    delay nodes 이렇게 간단한 연결만으로 딜레이를 구현할 수 있다

    ์ด๋ ‡๊ฒŒ ๋…ธ๋“œ๋“ค์„ ์—ฐ๊ฒฐํ•˜๊ฒŒ๋˜๋ฉด DelayNode๋ฅผ ํ†ตํ•ด ์ž…๋ ฅ๋œ ์˜ค๋””์˜ค ์‹ ํ˜ธ๊ฐ€ ์ง€์—ฐ๋œ ํ›„ FeedbackNode์™€ OutputNode๋กœ ์ถœ๋ ฅ๋˜๊ณ , FeedbackNode๋ฅผ ํ†ตํ•ด์„œ ๊ฒŒ์ธ์ด ๊ฐ์†Œ๋œ ์†Œ๋ฆฌ๋Š” ๋‹ค์‹œ DelayNode๋กœ ์ž…๋ ฅ๋˜์–ด ์ง€์—ฐ๋œ ํ›„ OutputNode๋กœ ์ถœ๋ ฅ๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿผ ์œ„ ๊ทธ๋ฆผ๋Œ€๋กœ ํ•œ๋ฒˆ ๋…ธ๋“œ๋“ค์„ ์—ฐ๊ฒฐํ•ด๋ณด๋„๋ก ํ•˜์ž.

    sourceNode.connect(inputNode);
    
    // Dry ์†Œ์Šค ๋…ธ๋“œ ์—ฐ๊ฒฐ
    inputNode.connect(dryGainNode);
    dryGainNode.connect(outputNode);
    dryGainNode.gain.value = 1 - mix;
    
    // Delay ๋ฃจํ”„ ์ƒ์„ฑ
    delayNode.connect(feedbackNode);
    feedbackNode.connect(delayNode);
    
    // Wet ์†Œ์Šค ๋…ธ๋“œ ์—ฐ๊ฒฐ
    inputNode.connect(delayNode);
    delayNode.connect(wetGainNode);
    wetGainNode.connect(outputNode);
    wetGainNode.gain.vaule = mix;
    
    outputNode.connect(audioContext.destination);

    ์ด์ œ ์†Œ์Šค ๋…ธ๋“œ๋ฅผ ์žฌ์ƒํ•ด๋ณด๋ฉด ๋”œ๋ ˆ์ด ์ดํŽ™ํ„ฐ๋ฅผ ํ†ตํ•ด ๋ฉ”์•„๋ฆฌ๊ฐ€ ์น˜๋Š” ๋“ฏํ•œ ํšจ๊ณผ๊ฐ€ ์ ์šฉ๋œ ์†Œ๋ฆฌ๋ฅผ ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    Filter

    filter

    ํ•„ํ„ฐ(Filter)๋Š” ๋ฌด์–ธ๊ฐ€๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ๋„๊ตฌ ํ˜น์€ ๊ฐœ๋…์„ ์˜๋ฏธํ•œ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ํ•„ํ„ฐ๋ผ๋Š” ๊ฐœ๋…์„ ํ‰์†Œ์— ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ ‡๊ฒŒ ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฐœ๋…์€ ์•„๋‹ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ์—์„œ์˜ ํ•„ํ„ฐ๋Š” ๋ฐ”๋กœ ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

    ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ํ•„ํ„ฐ๋Š” ์˜ค๋””์˜ค์˜ ์Œ์—ญ๋Œ€ ์ค‘ ํŠน์ •ํ•œ ์Œ์—ญ๋Œ€๋งŒ ์ฝ• ์ง‘์–ด๋‚ด์–ด ์—†์• ๋ฒ„๋ฆด ์ˆ˜ ์žˆ๋Š” ์ดํŽ™ํ„ฐ์ธ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ํ•„ํ„ฐ๋Š” ์ฃผ๋กœ ์†Œ๋ฆฌ์— ์„ž์—ฌ์žˆ๋Š” ๋…ธ์ด์ฆˆ๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๊ฑฐ๋‚˜ ๋„ˆ๋ฌด ๋‚ฎ๊ฑฐ๋‚˜ ๋„ˆ๋ฌด ๋†’์•„์„œ ์“ธ๋ฐ์—†๋Š” ์šธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋Š” ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๋Š”๋ฐ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.

    ์ด๋Ÿฌํ•œ ํ•„ํ„ฐ์˜ ํŠน์„ฑ์„ ์ž˜ ์‚ฌ์šฉํ•˜๋ฉด ์ƒ๋‹นํžˆ ์žฌ๋ฏธ์žˆ๋Š” ์ง“์„ ๋งŽ์ด ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋ฅผ ๋‘๊ฐœ ์ •๋„ ๋“ค์ž๋ฉด, ๋ฐ”๋กœ ์ „ํ™”๊ธฐ์—์„œ ๋‚˜์˜ค๋Š” ๋ชฉ์†Œ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ํด๋Ÿฝ์—์„œ ๋‚˜๋Š” ์Œ์•… ์†Œ๋ฆฌ์™€ ๊ฐ™์€ ์†Œ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

    ๋จผ์ €, ์ „ํ™”๊ธฐ์—์„œ ๋‚˜์˜ค๋Š” ๋ชฉ์†Œ๋ฆฌ๋Š” ์ „์ฒด ์ฃผํŒŒ์ˆ˜ ์ค‘์—์„œ ํŠน์ •ํ•œ ๋Œ€์—ญ์˜ ์ฃผํŒŒ์ˆ˜๋งŒ ํ†ต๊ณผ์‹œํ‚ค๋Š” Bandpass ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด ๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ์ „ํ™”๊ธฐ๊ฐ€ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผํŒŒ์ˆ˜ ๋Œ€์—ญ์— ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์ด์šฉํ•˜์—ฌ ์ธ๊ฐ„์˜ ๋ชฉ์†Œ๋ฆฌ ๋Œ€์—ญ์ธ 100 ~ 250hz์˜ ์ฃผํŒŒ์ˆ˜๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋ชจ๋‘ ์ž˜๋ผ๋‚ด๋Š” ๊ฒƒ์ด๋‹ค.

    ์œ„ ์˜์ƒ์˜ 5:00 ๊ฒฝ "Hey, kitty~" ํ•˜๋Š” ๋ถ€๋ถ„์ด ํ•„ํ„ฐ๊ฐ€ ์ ์šฉ๋œ ๋ชฉ์†Œ๋ฆฌ์ด๋‹ค.

    ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์‚ฌ๋žŒ์˜ ๋ชฉ์†Œ๋ฆฌ ์†Œ์Šค์— ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 100 ~ 250hz ๋Œ€์—ญ์„ ์ œ์™ธํ•˜๊ณ  ๋‚˜๋จธ์ง€ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋ชจ๋‘ ๋‚ ๋ ค๋ฒ„๋ฆฌ๊ฒŒ๋˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ „ํ™”๋ฅผ ํ• ๋•Œ ๋“ค๋ฆฌ๋Š” ๋ชฉ์†Œ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

    ํด๋Ÿฝ์—์„œ ๋‚˜๋Š” ์Œ์•… ์†Œ๋ฆฌ๋„ ๋น„์Šทํ•œ ์›๋ฆฌ๋กœ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ฒƒ์ด๋‹ค. ํด๋Ÿฝ์˜ ํŠน์„ฑ ์ƒ ๋ณดํ†ต ์ง€ํ•˜์— ์œ„์น˜ํ•˜๊ณ  ์ข์€ ์ž…๊ตฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ๊ทธ๋Ÿฐ ์ƒํ™ฉ์—์„œ ํด๋Ÿฝ์—์„œ ๋…ธ๋ž˜๋ฅผ ํ‹€๊ฒŒ๋˜๋ฉด ์†Œ๋ฆฌ๊ฐ€ ๋ฐ–์œผ๋กœ ๋น ์ ธ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋Š” ํ†ต๋กœ๊ฐ€ ๊ฑฐ์˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ ์ง€์ƒ์—์„œ ํด๋Ÿฝ์—์„œ ํ‹€๊ณ  ์žˆ๋Š” ๋…ธ๋ž˜๋ฅผ ๋“ค์–ด๋ณด๋ฉด ๊ต‰์žฅํžˆ ๋ฌต์งํ•œ ๋ถ~ ๋ถ~ํ•˜๋Š” ์†Œ๋ฆฌ๊ฐ€ ๋“ค๋ฆฌ๊ฒŒ ๋œ๋‹ค.

    ํด๋Ÿฝ ์Œ์•…์˜ ํŠน์„ฑ ์ƒ ๊ฐ•ํ•œ ๋“œ๋Ÿผ๊ณผ ๋ฒ ์ด์Šค๋กœ ์ธํ•ด ์ €์Œ์ด ๋ถ€๊ฐ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ณ , ๊ณ ์Œ๋ณด๋‹ค๋Š” ์ €์Œ์˜ ๋ฌผ์ฒด ํˆฌ๊ณผ์œจ์ด ๋†’๊ธฐ ๋•Œ๋ฌธ์— ํด๋Ÿฝ ์™ธ๋ถ€์—์„œ๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ๊ณ ์Œ์— ๋น„ํ•ด ๋งŽ์ด ํˆฌ๊ณผ๋œ ์ €์Œ์„ ์œ„์ฃผ๋กœ ๋“ฃ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฌํ•œ ํŒŒ๋™์˜ ํŠน์„ฑ์€ ์†Œ๋ฆฌ์— ํ•œ์ •๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ  ๋น›๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ํŒŒ๋™๋˜ํ•œ ๊ณ ์ฃผํŒŒ์˜ ์—๋„ˆ์ง€ ์†์‹ค๋ฅ ์ด ์ €์ฃผํŒŒ๋ณด๋‹ค ๋†’๋‹ค.

    6:27์ดˆ๋ถ€ํ„ฐ ๋‚ฎ์€ ์ฃผํŒŒ์ˆ˜๋งŒ ํ†ต๊ณผ์‹œํ‚ค๋Š” Lowpass ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ ์†Œ๋ฆฌ๋กœ ๋ณ€ํ•˜๋Š” ๋ถ€๋ถ„์ด ๋‚˜์˜จ๋‹ค

    ์ด๋ ‡๋“ฏ ์‚ฌ์šด๋“œ ์—”์ง€๋‹ˆ์–ด๋“ค์€ ํŠน์ • ์ƒํ™ฉ์˜ ์†Œ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋“ค๋ฆฌ๋Š”์ง€ ๋ถ„์„ํ•˜๊ณ  ํ•„ํ„ฐ๋ฅผ ํฌํ•จํ•œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ดํŽ™ํ„ฐ๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ ์ƒํ™ฉ์˜ ํ˜„์žฅ๊ฐ์„ ๋ถ€์—ฌํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

    ๋‹คํ–‰ํžˆ๋„ HTML5 Audio API๋Š” ์ด๋Ÿฐ ํ•„ํ„ฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” BiquadFilterNode๋ฅผ ์ œ๊ณตํ•ด์ฃผ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์˜ค๋””์˜ค ๋ฒ„ํผ๋ฅผ ๊นŒ์„œ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋ถ„์„ํ•ด์•ผํ•˜๋Š” ์Šฌํ”ˆ ์ƒํ™ฉ์€ ํ”ผํ•  ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ๋…ธ๋“œ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ฐ’๋“ค์ด ์–ด๋–ค ๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š”์ง€๋งŒ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ๋œ๋‹ค.

    ๊ทธ๋Ÿผ BiquadFilterNode๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์†์„ฑ๋“ค์ด ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š”์ง€ ํ•˜๋‚˜ํ•˜๋‚˜ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.

    Frequency

    Frequency๋Š” ์–ด๋–ค ๋Œ€์—ญ์˜ ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ผ ๊ฒƒ์ธ์ง€๋ฅผ ์ •ํ•˜๋Š” ๊ฐ’์ด๋‹ค. ๋‹จ์œ„๋Š” hz(ํ—ค๋ฅด์ธ )๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, 10hz๋ถ€ํ„ฐ ์˜ค๋””์˜ค์˜ ์ƒ˜ํ”Œ๋ ˆ์ดํŠธ์˜ ์ ˆ๋ฐ˜๊นŒ์ง€์˜ ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ ์˜ค๋””์˜ค ์†Œ์Šค์˜ ์ƒ˜ํ”Œ๋ ˆ์ดํŠธ๊ฐ€ 44,100hz๋ผ๋ฉด 22,050๊นŒ์ง€๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

    Q

    ์‹ ํ˜ธ๋ฅผ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค๋Š” ๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์นผ์ฒ˜๋Ÿผ ๋”ฑ! ์ž๋ฅผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ์†Œ๋ฆฌ ์ž์ฒด๋Š” ์•„๋‚ ๋กœ๊ทธ ์‹ ํ˜ธ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋„ค๋ชจ ๋ฐ˜๋“ฏํ•˜๊ฒŒ ์ž˜๋ผ๋‚ผ ์ˆ˜ ์—†๊ณ  ์–ด๋Š ์ •๋„ ๋ฐ”์šด๋”๋ฆฌ๋ฅผ ๊ฐ€์ง€๊ณ  ๊ฑธ๋Ÿฌ๋‚ผ ์ˆ˜๋ฐ–์— ์—†๋Š”๋ฐ, ์ด๋•Œ Q๋Š” ํŠน์ • ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ผ ๋•Œ ์–ผ๋งˆ๋‚˜ ์˜ˆ๋ฏผํ•˜๊ฒŒ ๊ฑธ๋Ÿฌ๋‚ผ ์ˆ˜ ์žˆ๋Š” ์ง€๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

    filter q

    Q์—๋Š” 0.0001 ~ 1000 ์‚ฌ์ด์˜ ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, Q์˜ ๊ฐ’์ด ๋†’์„ ์ˆ˜๋ก ์žก์•„๋‚ธ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋” ์˜ˆ๋ฏผํ•˜๊ฒŒ ๊ฑธ๋Ÿฌ๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜ค๋””์˜ค ์‹ ํ˜ธ๋ฅผ ํ•„ํ„ฐ๋ง ํ•  ๋•Œ Q๊ฐ€ ๋„ˆ๋ฌด ๋†’๋‹ค๋ฉด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋“ค๋ฆฌ์ง€ ์•Š๊ณ  ์ธ์œ„์ ์œผ๋กœ ๋“ค๋ฆด ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ ๋‹นํ•œ ๊ฐ’์„ ์ฐพ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

    Type

    BiquadFilterNode๋กœ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํƒ€์ž…์˜ ํ•„ํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋‚ผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ํฌ๊ฒŒ๋Š” ์ฃผํŒŒ์ˆ˜๋ฅผ ์•„์˜ˆ ๊ฑธ๋Ÿฌ๋‚ด๋ฒ„๋ฆฌ๋Š” ํƒ€์ž…๊ณผ, ํŠน์ • ์ฃผํŒŒ์ˆ˜๋ฅผ ์ฆํญ์‹œํ‚ค๊ฑฐ๋‚˜ ๊ฐ์†Œ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์œผ๋กœ ๋‚˜๋ˆ ์ง„๋‹ค.

    ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ํƒ€์ž…
    • lowpass(highcut): ์ง€์ •ํ•œ ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋†’์€ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค.
    • highpass(lowcut): ์ง€์ •ํ•œ ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋‚ฎ์€ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค.
    • bandpass: ์ง€์ •ํ•œ ์ฃผํŒŒ์ˆ˜๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค.
    • notch: ์ง€์ •ํ•œ ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค.
    ์ฃผํŒŒ์ˆ˜๋ฅผ ์ฆํญ/๊ฐ์†Œํ•˜๋Š” ํƒ€์ž…
    • lowshelf: ์ง€์ •ํ•œ ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋‚ฎ์€ ์ฃผํŒŒ์ˆ˜๋ฅผ ์ฆํญ/๊ฐ์†Œ ์‹œํ‚จ๋‹ค.
    • highshelf: ์ง€์ •ํ•œ ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋†’์€ ์ฃผํŒŒ์ˆ˜๋ฅผ ์ฆํญ/๊ฐ์†Œ ์‹œํ‚จ๋‹ค.
    • peaking: ์ง€์ •ํ•œ ์ฃผํŒŒ์ˆ˜๋ฅผ ์ฆํญ/๊ฐ์†Œ ์‹œํ‚จ๋‹ค.

    ์ด ์ค‘ ์ฃผํŒŒ์ˆ˜๋ฅผ ์ฆํญ/๊ฐ์†Œ์‹œํ‚ค๋Š” ํƒ€์ž…์€ ๋ฐ‘์—์„œ ํ›„์ˆ ํ•  EQ(Equalizer)์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฒˆ์—๋Š” ๋‹จ์ˆœํžˆ ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ํ•„ํ„ฐ๋ฅผ ๋งŒ๋“ค ์˜ˆ์ •์ด๋ฏ€๋กœ ํ•„์ž๋Š” ์ฃผํŒŒ์ˆ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ํƒ€์ž…๋งŒ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•„ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋‹ค.

    ํ•„์ž๋Š” ํŠน์ • ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋†’์€ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ฑธ๋Ÿฌ๋‚ด๋Š” Lowpass ํ•„ํ„ฐ์™€ ํŠน์ • ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋‚ฎ์€ ์ฃผํŒŒ์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ฑธ๋Ÿฌ๋‚ด๋Š” Highpass ํ•„ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿผ ํ•œ๋ฒˆ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•„ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋„๋ก ํ•˜์ž.

    Filter ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

    ์šฐ์„  AudioContext ๊ฐ์ฒด์˜ createBiquadFilter ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ BiquadFilterNode๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ํ•„์ž๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์˜ค๋””์˜ค ์ƒ˜ํ”Œ์€ 44,100hz์˜ ์ƒ˜ํ”Œ๋ ˆ์ดํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ Lowpass ํ•„ํ„ฐ์˜ ์ฃผํŒŒ์ˆ˜๋Š” 1,000hz๋กœ Highpass ํ•„ํ„ฐ์˜ ์ฃผํŒŒ์ˆ˜๋Š” 20,000hz๋กœ ์„ค์ •ํ•˜๊ฒ ๋‹ค.

    const lowpassFilterNode = audioContext.createBiquadFilter();
    lowpassFilterNode.type = 'lowpass';
    lowpassFilterNode.frequency.setValueAtTime(1000, audioContext.currentTime);
    
    const highpassFilterNode = audioContext.createBiquadFilter();
    highpassFilterNode.type = 'highpass';
    highpassFilterNode.frequency.setValueAtTime(20000, audioContext.currentTime);

    Q๊ฐ’์„ ๋”ฐ๋กœ ์„ค์ •ํ•ด์ฃผ์ง€ ์•Š์•˜๋Š”๋ฐ, ๊ทธ๋ž˜๋„ ์‚ฌ์‹ค ์ƒ๊ด€์—†๋‹ค. BiquadFilterNode์˜ Q๋Š” ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ 350์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  ์ด ๊ฐ’์€ ๋„ˆ๋ฌด ๊ณผํ•˜์ง€๋„ ๋ถ€์กฑํ•˜์ง€๋„ ์•Š์€ ์ ๋‹นํ•œ ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ƒฅ ๊ธฐ๋ณธ ๊ฐ’์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.(์กฐ๊ธˆ ๊ท€์ฐฎ๊ธฐ๋„ ํ•˜๋‹ค.)

    ์ด์ œ ์ƒ์„ฑํ•œ ํ•„ํ„ฐ ๋…ธ๋“œ๋“ค์„ ์˜ค๋””์˜ค ์†Œ์Šค์™€ ์—ฐ๊ฒฐํ•ด์ฃผ๋ฉด 1,000hz๋ณด๋‹ค ๋‚ฎ๊ณ  20,000hz๋ณด๋‹ค ๋†’์€ ์ฃผํŒŒ์ˆ˜๊ฐ€ ์ œ๊ฑฐ๋œ ์˜ค๋””์˜ค ์ƒ˜ํ”Œ์„ ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    sourceNode.connect(lowpassFilterNode);
    lowpassFilterNode.connect(highpassFilterNode);
    highpassFilterNode.connect(audioContext.destination);

    ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์œผ์‹  ๋ถ„๋“ค์€ ์Šฌ์Šฌ ๋Š๋ผ๊ธฐ ์‹œ์ž‘ํ–ˆ๊ฒ ์ง€๋งŒ ์‚ฌ์‹ค HTML5 Audio API๊ฐ€ ์›Œ๋‚™ ์ž˜ ๋งŒ๋“ค์–ด์ ธ์žˆ์–ด์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŒ์ ธ์•ผํ•  ๋ถ€๋ถ„์ด ๊ฑฐ์˜ ์—†๋‹ค. ํ•„์ž๋Š” BiquadFilterNode์˜ ์กด์žฌ๋ฅผ ์•Œ๊ธฐ ์ „์—๋Š” โ€œ์™€ ์ด๊ฑฐ ํ•„ํ„ฐ๋Š” ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์ง€โ€ฆ?โ€๋ผ๊ณ  ๊ณ ๋ฏผํ–ˆ์—ˆ๋Š”๋ฐ ์‚ฌ์‹ค ์ œ์ผ ๊ณ ๋ฏผํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๋†ˆ์ด์—ˆ๋‹ค. (๊ทธ๋ž˜์„œ ์•ฝ๊ฐ„ ํ—ˆ๋ฌดํ•˜๊ธฐ๋„ ํ–ˆ๋‹ค.)

    EQ

    parametric eq

    Equalizer(EQ, ์ดํ€„๋ผ์ด์ €)๋Š” ์ด๋ฆ„์—์„œ๋„ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด ์ผ์ข…์˜ ์ฃผํŒŒ์ˆ˜ ํ‰ํƒ„ํ™” ์ž‘์—…(Frequancy Equalizing)์„ ํ•˜๋Š” ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ดํŽ™ํ„ฐ์ด๋‹ค. EQ๋Š” ์ปดํ”„๋ ˆ์„œ์™€ ํ•จ๊ป˜ ์˜ค๋””์˜ค ๋ฏน์‹ฑ์˜ ๊ธฐ๋ณธ์œผ๋กœ ๊น”๊ณ  ๋“ค์–ด๊ฐ€๋Š” ์ดํŽ™ํ„ฐ์ธ๋ฐ, ์›๋ณธ ์†Œ์Šค์—์„œ ์“ธ๋ฐ์—†๋Š” ์†Œ๋ฆฌ๋ฅผ ์—†์• ๊ณ  ๋‹ค๋ฅธ ์†Œ๋ฆฌ๋“ค๊ณผ์˜ ์กฐํ™”๋ฅผ ์ด๋ฃจ๋„๋ก ํ•˜๋Š” ์šฉ๋„๋กœ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. EQ๋Š” ๊ฒฐ๊ตญ ์ฃผํŒŒ์ˆ˜๋ฅผ ์ปจํŠธ๋กคํ•˜๋Š” ์ดํŽ™ํ„ฐ์ด๋ฏ€๋กœ ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ, ์ด๋ฏธ ์šฐ๋ฆฌ๋Š” ํ•„ํ„ฐ๋ฅผ ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด๋ดค๊ธฐ ๋•Œ๋ฌธ์— EQ ์ •๋„๋Š” ๋š๋”ฑ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

    EQ๋Š” ํฌ๊ฒŒ ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ ์ดํ€„๋ผ์ด์ €(Parametric EQ)์™€ ๊ทธ๋ž˜ํ”ฝ ์ดํ€„๋ผ์ด์ €(Graphic EQ) ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜๋กœ ๋‚˜๋ˆ„์–ด์ง€๋Š”๋ฐ, ํ•„์ž๋Š” ์ด ์ค‘ ๊ทธ๋ž˜ํ”ฝ ์ดํ€„๋ผ์ด์ €๋ฅผ ๊ตฌํ˜„ํ•  ์˜ˆ์ •์ด๋‹ค. ์ฐธ๊ณ ๋กœ ์ƒ๋‹จ์˜ ๊ทธ๋ฆผ์€ ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ์ธ๋ฐ, ๊ทธ๋ƒฅ ์ด๋ฏธ์ง€๊ฐ€ ๋” ๋ฉ‹์žˆ์–ด์„œ ๋„ฃ์—ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ๊ทธ๋ž˜ํ”ฝ EQ๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค.

    graphic eq 짙은 아날로그의 향수가 풍겨오는 비주얼

    ๋‘ ๊ฐœ์˜ EQ ๋ชจ๋‘ ์žฅ๋‹จ์ ์ด ์กด์žฌํ•˜๋Š”๋ฐ, ์ผ๋‹จ ๊ทธ๋ž˜ํ”ฝ EQ์˜ ์žฅ์ ์€ ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ์— ๋น„ํ•ด ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผํŒŒ์ˆ˜ ๋Œ€์—ญ์˜ ์ˆ˜๊ฐ€ ๋งŽ๊ณ  ์ง๊ด€์ ์ธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์€ ์ด ๋‹จ๋ฝ์˜ ๋งจ ์œ„์— ์ฒจ๋ถ€๋œ ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ ์ด๋ฏธ์ง€๋ฅผ ๋ณด๊ณ  โ€œ์—ฅ? ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ๋„ ๋‚˜๋ฆ„ ์ง๊ด€์ ์ธ๋ฐ?โ€๋ผ๊ณ  ํ•˜์‹ค ์ˆ˜ ์žˆ๋Š”๋ฐ, ์›๋ž˜ ํ•˜๋“œ์›จ์–ด ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค.

    hardware parametric eq 까만 것은 노브요. 하얀 것은 숫자로다.

    ๊ทธ๋ž˜์„œ ๊ทธ๋ž˜ํ”ฝ EQ๋Š” ๋ณดํ†ต ๋น ๋ฅธ ๋Œ€์‘์ด ํ•„์š”ํ•œ ๊ณต์—ฐ์žฅ๊ฐ™์€ ๊ณณ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์œผ๋ฉฐ, ์งฌ์ด ๋งŽ์ด ์Œ“์ธ ์‹œ๋‹ˆ์–ด ์‚ฌ์šด๋“œ ์—”์ง€๋‹ˆ์–ด๋“ค์€ ๊ณต์—ฐ์žฅ์—์„œ ํ•˜์šธ๋ง(๋…ธ๋ž˜๋ฐฉ์—์„œ ์‚- ํ•˜๋ฉฐ ๊ฐ€๋” ๋‚˜๋Š” ๋‚ ์นด๋กœ์šด ์†Œ๋ฆฌ)์ด ๋ฐœ์ƒํ•˜๋ฉด ๋ฐ”๋กœ ํ•ด๋‹น ์ฃผํŒŒ์ˆ˜๋ฅผ ์บ์น˜ํ•ด์„œ ๊ทธ๋ž˜ํ”ฝ EQ๋กœ ์ฃฝ์—ฌ๋ฒ„๋ฆฌ๋Š” ๋ฌด์„œ์šด ์Šคํ‚ฌ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

    ๊ทธ๋ž˜ํ”ฝ EQ์˜ ๋‹จ์ ์€ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผํŒŒ์ˆ˜ ๋Œ€์—ญ์ด ์ •ํ•ด์ ธ ์žˆ๋‹ค๋Š” ๊ฒƒ๊ณผ ์ฃผํŒŒ์ˆ˜์˜ ์„ธ๋ฐ€ํ•œ ์กฐ์ •์ด ํž˜๋“ค๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋ฐ˜๋ฉด ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ๋Š” ๊ทธ๋ž˜ํ”ฝ EQ์™€ ๋‹ค๋ฅด๊ฒŒ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผํŒŒ์ˆ˜ ๋Œ€์—ญ๊นŒ์ง€ ๋ชจ๋‘ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

    ๊ทธ๋Ÿฌ๋‚˜ ํ•œ๋ฒˆ์— ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผํŒŒ์ˆ˜์˜ ๊ฐœ์ˆ˜๋Š” ๊ทธ๋ž˜ํ”ฝ EQ์— ๋น„ํ•ด ํฌ๊ฒŒ ๋ถ€์กฑํ•˜๋‹ค. ์ผ๋ฐ˜์ ์ธ ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ๊ฐ€ 3~5๊ฐœ์˜ ์ฃผํŒŒ์ˆ˜ ๋Œ€์—ญ์„ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ˜๋ฉด์— ๊ทธ๋ž˜ํ”ฝ EQ๋Š” ํ•œ๋ฒˆ์— ์กฐ์ ˆ ๊ฐ€๋Šฅํ•œ ์ฃผํŒŒ์ˆ˜๊ฐ€ 40๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋Š” ๊ต‡์ˆ˜๋„ ์กด์žฌํ•œ๋‹ค.

    ํ•„์ž ์ƒ๊ฐ์— ํ•˜๋“œ์›จ์–ด ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ์˜ ์ตœ๋Œ€ ๋‹จ์ ์€ ๋ฐ”๋กœ ์ง๊ด€์ ์ด์ง€ ์•Š์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ธ๋ฐ, ์ด ๋‹จ์ ์€ ์†Œํ”„ํŠธ์›จ์–ด๋กœ ๊ตฌํ˜„ํ•˜๋ฉด UI๋กœ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ๋Š” ์˜์—ญ์ด๊ธฐ๋„ ํ•˜๊ณ  ๋Œ€๋ถ€๋ถ„์˜ ๋…น์Œ์‹ค์—์„œ๋Š” ์ฆ‰๊ฐ์ ์ธ ๋Œ€์‘๋ณด๋‹ค๋Š” ๊ณ„์† ์†Œ๋ฆฌ๋ฅผ ๋“ค์–ด๋ณด๋ฉด์„œ ์ดํ€„๋ผ์ด์ง•์„ ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์ด ๋Œ€๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ์†Œํ”„ํŠธ์›จ์–ด EQ๊ฐ€ ์ฃผํŒŒ์ˆ˜ ๋Œ€์—ญ ์กฐ์ ˆ์— ์ž์œ ๋„๊ฐ€ ๋†’์€ ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ๋กœ ๊ตฌํ˜„๋œ๋‹ค.

    ํ•˜์ง€๋งŒ ํ•„์ž๊ฐ€ ๋งŒ๋“œ๋Š” ๋ฐ๋ชจ์ฒ˜๋Ÿผ ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„ํ•ด๋ณด๋Š” ์ƒํ™ฉ์—์„œ๋Š” ์ € ์œ„์— ํ•˜๋“œ์›จ์–ด ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ์™€ ๋น„์Šทํ•œ UI๋กœ ๊ตฌํ˜„๋  ๊ฒƒ์ด ๋ป”ํ•˜๋ฏ€๋กœ ํ•„์ž๋Š” ์ƒ๋Œ€์ ์œผ๋กœ UI ๋งŒ๋“ค๊ธฐ๊ฐ€ ์‰ฌ์šด ๊ทธ๋ž˜ํ”ฝ EQ๋ฅผ ์„ ํƒํ–ˆ๋‹ค.(์ด ๋ง์ด ์ž˜ ์ดํ•ด๊ฐ€ ์•ˆ๋œ๋‹ค๋ฉด EQ ์ฑ•ํ„ฐ์˜ ๊ฐ€์žฅ ์œ„์— ์ฒจ๋ถ€ํ•œ ํŒŒ๋ผ๋ฉ”ํŠธ๋ฆญ EQ๋ฅผ ํ•œ๋ฒˆ ๋ณด๊ณ  ์˜ค์ž)

    ์œ„์—์„œ ํ•œ๋ฒˆ ์ด์•ผ๊ธฐํ–ˆ๋“ฏ์ด EQ๋Š” ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•˜๋ฏ€๋กœ ๊ทธ๋ ‡๊ฒŒ ๋ณต์žกํ•˜์ง€ ์•Š๋‹ค. ๊ทธ๋Ÿผ ์ด์ œ ํ•œ๋ฒˆ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋š๋”ฑ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜์ž.

    ๊ทธ๋ž˜ํ”ฝ EQ ๊ตฌํ˜„ํ•˜๊ธฐ

    ์œ„์—์„œ ๊ทธ๋ž˜ํ”ฝ EQ ์ด๋ฏธ์ง€๋ฅผ ๋ดค๋‹ค๋ฉด ์•Œ๊ฒ ์ง€๋งŒ ์ด ์นœ๊ตฌ๋Š” ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผํŒŒ์ˆ˜ ๋Œ€์—ญ์˜ ๊ฐœ์ˆ˜๊ฐ€ ์ •ํ•ด์ ธ ์žˆ๋Š” ์žฅ๋น„์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํ•„์ž๋„ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผํŒŒ์ˆ˜๊ฐ€ ๋“ค์–ด์žˆ๋Š” ๋ฐฐ์—ด์„ ํ•˜๋‚˜ ์„ ์–ธํ•˜๊ณ  ์ด ๋ฐฐ์—ด์„ ์ดํ„ฐ๋ ˆ์ด์…˜ํ•˜๋ฉด์„œ ํ•„ํ„ฐ๋“ค์„ ์ƒ์„ฑํ•  ๊ฒƒ์ด๋‹ค.

    const frequencies = [
      25, 31, 40, 50, 63, 80, 100, 125, 160, 200,
      250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000,
      2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000
    ];

    ์ด๋•Œ ์ฃผ์˜ํ•ด์•ผํ•  ์ ์ด ์žˆ๋‹ค. EQ๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ ํ•„ํ„ฐ๋ฅผ ์„œ๋กœ ์ฒด์ด๋‹ํ•ด์„œ ์—ฐ๊ฒฐํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค. ์ด๋•Œ ํ•„ํ„ฐ์˜ ๊ฒŒ์ธ์ด 1๋ณด๋‹ค ์กฐ๊ธˆ์ด๋ผ๋„ ๋†’๋‹ค๋ฉด ํ•œ๋ฒˆ ํ•„ํ„ฐ๋ฅผ ํ†ต๊ณผํ•  ๋•Œ๋งˆ๋‹ค ์†Œ๋ฆฌ๊ฐ€ ์กฐ๊ธˆ์”ฉ ์ฆํญ๋˜์–ด ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ท€์— ๋“ค์–ด์˜ฌ ๋•Œ ์ฏค์ด๋ฉด ์—„์ฒญ ํฐ ์†Œ๋ฆฌ๊ฐ€ ๋˜์–ด ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ณ ๋ง‰์„ ์˜์›ํžˆ ์ด๋ณ„์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๋‹ค.

    ๐Ÿšจ ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ํ•„ํ„ฐ๋“ค์˜ ๊ฒŒ์ธ์„ 0์ดํ•˜๋กœ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

    const inputNode = audioContext.createGain();
    sourceNode.connect(inputNode);
    
    const filters = frequencies.map((frequency, index, array) => {
      const filterNode = audioContext.createBiquadFilter();
      filterNode.gain.value = 0;
      filterNode.frequency.setValueAtTime(frequency, audioContext.currentTime);
      
      if (index === 0) {
        filterNode.type = 'lowshelf';
      }
      else if (index === array.length - 1) {
        filterNode.type = 'highshelf';
      }
      else {
        filterNode.type = 'peaking';
      }
      return filterNode;
    });
    
    filters.reduce((prev, current) => {
      prev.connect(currentNode);
      return currentNode;
    }, inputNode);
    
    const outputNode = audioContext.createGain();
    filters[filters.length - 1].connect(outputNode);
    outputNode.connect(audioContext.destination);

    map ๋ฉ”์†Œ๋“œ ๋‚ด๋ถ€์˜ if ๋ฌธ์„ ๋ณด๋ฉด ํ•ด๋‹น ์ฒซ๋ฒˆ์งธ ํ•„ํ„ฐ์™€ ๋งˆ์ง€๋ง‰ ํ•„ํ„ฐ์˜ ํƒ€์ž…๋งŒ ๋‹ค๋ฅด๊ฒŒ ์ฃผ๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” Shelf ํƒ€์ž…์˜ ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒซ๋ฒˆ์งธ ํ•„ํ„ฐ์˜ ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋‚ฎ์€ ์ฃผํŒŒ์ˆ˜์™€ ๋งˆ์ง€๋ง‰ ํ•„ํ„ฐ์˜ ์ฃผํŒŒ์ˆ˜๋ณด๋‹ค ๋†’์€ ์ฃผํŒŒ์ˆ˜๊นŒ์ง€ ๋ชจ๋‘ ์ปค๋ฒ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.(ํ•„ํ„ฐ ํƒ€์ž…์ด ์ž˜ ๊ธฐ์–ต๋‚˜์ง€ ์•Š๋Š”๋‹ค๋ฉด Filter ๋ถ€๋ถ„์„ ๋‹ค์‹œ ๋ณด๊ณ  ์˜ค์ž)

    ๊ทธ ํ›„ ์ƒ์„ฑ๋œ ํ•„ํ„ฐ๋ฅผ reduce ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ชจ๋‘ ์ฒด์ด๋‹ํ•ด์ฃผ๊ณ  outputNode์™€๋„ ์—ฐ๊ฒฐํ•ด์ฃผ์—ˆ๋‹ค. ์—ฌ๊ธฐ๊นŒ์ง€ ์ž‘์„ฑํ•œ ํ›„ sourceNode๋ฅผ ์žฌ์ƒ์‹œ์ผœ๋ณด๋ฉด ์•„๋ฌด ๋ณ€ํ™”๋„ ์—†๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

    ๋‹น์—ฐํžˆ ๋ชจ๋“  ํ•„ํ„ฐ์˜ ๊ฒŒ์ธ์ด 0์ด๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ฌด๋Ÿฐ ๋ณ€ํ™”๊ฐ€ ์—†๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋•Œ ์ € ํ•„ํ„ฐ๋“ค์˜ ๊ฐ’์„ -1 ~ 1์‚ฌ์ด์˜ ๋žœ๋คํ•œ ๋‚œ์ˆ˜๋กœ ํ• ๋‹นํ•˜๋ฉด ์†Œ๋ฆฌ๊ฐ€ ์กฐ๊ธˆ์”ฉ ๋ณ€ํ•˜๋Š” ๊ฒƒ์„ ๋“ค์–ด๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค. ํ•„์ž๋Š” ๊ฐœ์ธ์ ์œผ๋กœ input[type="range"] ์—˜๋ฆฌ๋จผํŠธ์™€ ์—ฐ๋™ํ•˜์—ฌ ํ•„ํ„ฐ๋“ค์˜ ๊ฒŒ์ธ์„ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค๊ณ  ์ง์ ‘ ์ด๊ฒƒ์ €๊ฒƒ ๋งŒ์ ธ๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

    ๋˜ํ•œ ๊ฐ€์žฅ ๋‚ฎ์€ ์ฃผํŒŒ์ˆ˜์™€ ๋†’์€ ์ฃผํŒŒ์ˆ˜์˜ ํ•„ํ„ฐ๋ฅผ Shelf ํƒ€์ž…์œผ๋กœ ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ํ•„ํ„ฐ๋“ค์˜ ๊ฒŒ์ธ์„ ๋‚ฎ์ถ”๋ฉด Lowpass ํ•„ํ„ฐ๋‚˜ Highpass ํ•„ํ„ฐ์™€ ๊ฐ™์€ ํšจ๊ณผ๋„ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

    ๋งˆ์น˜๋ฉฐ

    ์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ๋Œ€ํ‘œ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ดํŽ™ํ„ฐ๋“ค์ธ ์ปดํ”„๋ ˆ์„œ, ๋ฆฌ๋ฒ„๋ธŒ, ๋”œ๋ ˆ์ด, ํ•„ํ„ฐ, EQ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์•˜๋‹ค. ์‚ฌ์‹ค ์ด 5๊ฐœ ์™ธ์—๋„ ์žฌ๋ฐŒ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ดํŽ™ํ„ฐ๊ฐ€ ์žˆ์ง€๋งŒ ๋ถ„๋Ÿ‰์กฐ์ ˆ ๋Œ€์‹คํŒจ๋กœ ์ธํ•ด ์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ๋…ธ๋Š” ๊ฒƒ์œผ๋กœ ํ•˜๊ฒ ๋‹ค.

    ํ•„ํ„ฐ๋ฅผ ๋งŒ๋“ค ๋•Œ ํ•œ๋ฒˆ ์ด์•ผ๊ธฐ ํ–ˆ๋“ฏ์ด HTML5 Audio API๋Š” ๊ต‰์žฅํžˆ ๋†’์€ ์ˆ˜์ค€์˜ ์ถ”์ƒํ™”๋œ ๋…ธ๋“œ๋ฅผ ์ œ๊ณตํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์‹ค ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๊ตฌํ˜„ํ•  ๊ฒƒ๋“ค์ด ๋ณ„๋กœ ์—†๋‹ค. ์ด๋Š” ๋‹ค๋ฅด๊ฒŒ ๋งํ•˜๋ฉด ์„ธ๋ฐ€ํ•œ ์ˆ˜์ค€์˜ ๊ตฌํ˜„์ด ํž˜๋“ค๋‹ค๋Š” ๋œป์ด๊ธฐ๋„ ํ•˜์ง€๋งŒ ํ•„์ž๊ฐ€ ๋ฌด์Šจ ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ ํšŒ์‚ฌ๋ฅผ ์ฐจ๋ฆด ๊ฒƒ๋„ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ˆœ ์žฌ๋ฏธ๋กœ ๋งŒ๋“ค์–ด๋ณด๊ธฐ์—๋Š” ์ถฉ๋ถ„ํ•œ ๊ฒƒ ๊ฐ™๋‹ค.

    ์ด๋ ‡๊ฒŒ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ดํŽ™ํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋ฉฐ ํ•„์ž๋„ ์˜ˆ์ „์— ์‚ฌ์šด๋“œ ์—”์ง€๋‹ˆ์–ด๋กœ ์ผํ•  ๋•Œ์˜ ์ถ”์–ต์ด ์ƒˆ๋ก์ƒˆ๋ก ๋– ์˜ค๋ฅด๊ธฐ๋„ ํ–ˆ๊ณ  ๋˜ ์ดํŽ™ํ„ฐ์— ๋Œ€ํ•ด์„œ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ๋œ ๋‚ด์šฉ๋„ ์žˆ์–ด์„œ ๊ต‰์žฅํžˆ ์žฌ๋ฐŒ๊ฒŒ ์ž‘์—…์„ ํ–ˆ๋‹ค. ํ•„์ž๋Š” ํฌ์ŠคํŒ…์— ์ž‘์„ฑํ•œ ์ดํŽ™ํ„ฐ ์™ธ์—๋„ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ดํŽ™ํ„ฐ๋ฅผ ๊ณ„์† ๊ตฌํ˜„ํ•ด๋ณผ ์˜ˆ์ •์ด๋ฏ€๋กœ ๊ด€์‹ฌ์žˆ๋Š” ๋ถ„๋“ค์€ ํ•„์ž์˜ ๊นƒํ—ˆ๋ธŒ์—์„œ ํ•œ๋ฒˆ ์“ฑ ๋‘˜๋Ÿฌ๋ณด๊ณ  PR์„ ๋‚ ๋ ค๋„ ๋œ๋‹ค.(์ข‹์€ ๊ฑด ๋‚˜๋ˆ„๋ฉด ๋ฐฐ๊ฐ€ ๋˜๋Š” ๋ฒ•์ด๋‹ค.)

    ์ด์ƒ์œผ๋กœ JavaScript๋กœ ์˜ค๋””์˜ค ์ดํŽ™ํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž - ๋‚˜๋งŒ์˜ ์†Œ๋ฆฌ ๋งŒ๋“ค๊ธฐ ํฌ์ŠคํŒ…์„ ๋งˆ์นœ๋‹ค.

    Evan Moon

    ๐Ÿข ๊ฑฐ๋ถ์ด์ฒ˜๋Ÿผ ์‚ด์ž

    ๊ฐœ๋ฐœ์„ ์ž˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๊ฐ€ ์•„๋‹Œ ๊ฐœ๋ฐœ์„ ์ฆ๊ธฐ๊ธฐ ์œ„ํ•ด ๋…ธ๋ ฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. ์‚ฌ์†Œํ•œ ์ƒ๊ฐ ์ •๋ฆฌ๋ถ€ํ„ฐ ํŠœํ† ๋ฆฌ์–ผ, ์‚ฝ์งˆ๊ธฐ ์ •๋„๋ฅผ ์ฃผ๋กœ ๋„์ ์ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.