Media Art ProgrammingWeek 14 · Period 01 Handout

p5.js / Media Art Programming

14주차 1교시 — p5.sound: 사운드 로드, 재생, 음량 분석

Sound, Media, and Final Project 주제의 학생용 강의 자료입니다.

WEEK 14 PERIOD 01 P5.JS MEDIA ART

14주차 1교시 — p5.sound: 사운드 로드, 재생, 음량 분석

강의 아웃라인: week-14-outline 다음 교시: week-14-period2-student (FFT + 이미지/웹캠 + 기말 안내)

항목 내용
대상 P5.js 입문자 (테크아트 전공, 13주차 파티클 시스템 학습 완료)
소요 시간 45분
핵심 도구 P5.js Web Editor, p5.sound 라이브러리, MP3 음악 파일
학습 목표
  • p5.sound 라이브러리의 역할과 기능을 설명할 수 있습니다
  • loadSound()로 로드하고 재생/일시정지를 제어할 수 있습니다
  • p5.Amplitude로 음량을 분석해 map()으로 비주얼에 매핑할 수 있습니다
  • p5.AudioIn으로 마이크 입력을 같은 패턴으로 분석할 수 있습니다

도입

“파티클에 소리를 입히면?”

지난 시간에 파티클 시스템을 만들었습니다. 수백 개의 입자가 분출되고 사라지는 작품 — 직접 Emitter를 만들고, 파티클에 수명을 주고, 페이드아웃까지 구현했습니다.

그런데 만약 여기에 음악이 흘러나오면서, 소리의 크기에 따라 파티클이 폭발하듯 쏟아진다면 어떨까요?

이런 작품을 만들려면 세 가지가 필요합니다.

  1. 소리를 P5.js에 가져오기
  2. 소리의 크기를 숫자로 바꾸기
  3. 그 숫자를 파티클의 개수나 크기에 연결하기

오늘은 이 세 가지를 배웁니다. P5.js에서 소리를 다루는 방법 — 소리를 로드하고, 재생하고, 분석해서 지금까지 만들어 온 비주얼에 연결합니다.

오늘 할 일

이번 시간이 끝나면 다음 네 가지를 할 수 있게 됩니다.

  1. p5.sound 라이브러리가 무엇이고 어떤 기능을 제공하는지 설명할 수 있다
  2. loadSound()로 사운드 파일을 로드하고 재생/일시정지를 제어할 수 있다
  3. p5.Amplitude + getLevel()로 음량을 숫자로 받아 map()으로 비주얼에 연결할 수 있다
  4. p5.AudioIn으로 파일 대신 마이크 소리를 같은 패턴으로 분석할 수 있다

동기부여 — 미디어아트 사운드 비주얼

사운드 비주얼라이제이션은 미디어아트의 핵심 영역 중 하나입니다.

이런 작업의 공통점은 소리를 숫자로 바꾸고, 그 숫자를 시각적 요소에 연결하는 것입니다. 오늘 우리가 그것을 합니다.


핵심 개념 1 — p5.sound 라이브러리 소개

P5.js 본체만으로는 소리를 다룰 수 없습니다. p5.sound라는 별도 라이브러리가 필요합니다.

p5.sound는 P5.js의 공식 사운드 라이브러리입니다. P5.js 본체(p5.min.js)와 별도의 파일(p5.sound.min.js)이지만, P5.js Web Editor에서는 이미 포함되어 있어 별도 설치나 설정이 필요 없습니다.

p5.sound가 제공하는 주요 기능은 다음과 같습니다.

기능 클래스/함수 설명
사운드 로드/재생 loadSound(), p5.SoundFile 음악 파일 불러오기 + 재생 제어
음량 분석 p5.Amplitude 전체 볼륨 레벨 측정
주파수 분석 p5.FFT 저음/중음/고음 분리 분석
마이크 입력 p5.AudioIn 실시간 마이크 소리 캡처
사운드 생성 p5.Oscillator 사인파, 사각파 등 소리 직접 생성

1교시에서는 로드/재생 + Amplitude + 마이크 입력까지, 2교시에서는 FFT + 이미지/웹캠 + 기말 프로젝트 안내를 다룹니다.

브라우저 보안 정책 — 가장 중요한 주의사항

최신 브라우저(Chrome, Firefox, Safari)는 사용자가 화면을 클릭하거나 키를 누르기 전에는 소리를 재생하지 않습니다. 보안 정책입니다 — 웹사이트에 들어갔는데 갑자기 소리가 나면 불쾌하기 때문입니다.

해결 방법은 한 줄입니다.

function mousePressed() {
  userStartAudio(); // 오디오 컨텍스트 활성화
}

userStartAudio() — 이 한 줄이 ’사용자가 클릭했으니 소리 재생해도 괜찮다’고 브라우저에 알려줍니다. 화면을 한 번 클릭해야 소리가 나온다 — 잊지 마세요.


핵심 개념 2 — 사운드 로드와 재생

preload()로 사운드 로드하기

11주차에서 JSON 파일을 불러올 때 preload()를 썼습니다. loadJSON()preload() 안에 넣으면 파일이 완전히 로드된 후에 setup()이 실행됐습니다. 사운드도 똑같습니다. 음악 파일은 용량이 크므로(수 MB), setup() 전에 완전히 로드해야 합니다.

let song;

function preload() {
  song = loadSound('assets/music.mp3');
}

P5.js Web Editor에서 파일 업로드는 다음 순서로 합니다.

  1. 왼쪽 상단의 > 화살표를 클릭하여 파일 탭을 연다
  2. Sketch Files 아래의 화살표 메뉴에서 Upload file 선택
  3. 음악 파일(MP3, WAV, OGG)을 드래그하거나 선택
  4. 코드에서는 '파일명.mp3' 형식으로 참조 (assets 폴더에 넣으면 'assets/파일명.mp3')

기본 재생 제어 — 클릭으로 재생/일시정지

let song;

function preload() {
  song = loadSound('assets/music.mp3');
}

function setup() {
  createCanvas(400, 400);
  background(0);
  fill(255);
  textAlign(CENTER, CENTER);
  textSize(20);
  text('클릭하면 재생/일시정지', width / 2, height / 2);
}

function mousePressed() {
  userStartAudio(); // 브라우저 오디오 활성화

  if (song.isPlaying()) {
    song.pause(); // 재생 중이면 일시정지
  } else {
    song.play(); // 정지 상태면 재생
  }
}

4주차에서 배운 if/elsemousePressed()가 여기서 그대로 쓰입니다. song.isPlaying()true면 재생 중이니까 pause(), 아니면 play().

주요 메서드.

메서드 기능 예시
song.play() 재생 (처음부터 또는 일시정지 위치에서) song.play()
song.pause() 일시정지 (현재 위치 기억) song.pause()
song.stop() 정지 (처음으로 돌아감) song.stop()
song.loop() 반복 재생 song.loop()
song.isPlaying() 재생 중인지 확인 (boolean) if (song.isPlaying()) { ... }
song.setVolume(v) 볼륨 조절 (0.0~1.0) song.setVolume(0.5)
play vs loop

play()는 한 번만 재생, loop()는 끝까지 가면 다시 처음부터 반복. 비주얼라이제이션할 때는 보통 loop()를 씁니다.

위 코드를 실행하면 캔버스를 클릭할 때마다 재생/일시정지/재개가 토글됩니다.

확인 질문

사운드 파일을 setup() 안에서 loadSound()로 불러오면 안 될까요? 왜 preload()에 넣어야 하죠?

: 파일 로드가 끝나기 전에 setup()이 실행될 수 있어서. 11주차 loadJSON()과 같은 이유.


핵심 개념 3 — p5.Amplitude로 음량 분석

이제 소리를 재생할 수 있게 됐습니다. 목표는 소리에 반응하는 비주얼을 만드는 것이므로 소리를 숫자로 바꿔야 합니다. 지금 이 순간의 음량이 얼마인지 숫자로 알아야 도형의 크기나 색상에 연결할 수 있습니다.

p5.Amplitude는 현재 재생 중인 사운드의 전체 음량(볼륨 레벨)을 실시간으로 측정해 숫자로 알려줍니다.

  • amp.getLevel() → 현재 프레임의 음량을 0.0(무음) ~ 1.0(최대) 사이 값으로 반환
  • 보통 조용한 부분은 0.01~0.1, 큰 부분은 0.3~0.8 정도

기본 사용법 — 음량에 따라 원 크기 변하기

let song;
let amp;

function preload() {
  song = loadSound('assets/music.mp3');
}

function setup() {
  createCanvas(400, 400);
  amp = new p5.Amplitude(); // 음량 분석기 생성
}

function draw() {
  background(0);

  let level = amp.getLevel(); // 현재 음량 (0.0 ~ 1.0)

  // 음량을 원의 크기로 변환
  let size = map(level, 0, 1, 10, 400);

  fill(255);
  noStroke();
  ellipse(width / 2, height / 2, size, size);
}

function mousePressed() {
  userStartAudio();
  song.loop();
}

코드 한 줄씩.

코드 설명
let amp; 음량 분석기를 담을 변수
amp = new p5.Amplitude(); 음량 분석기 객체 생성 (7주차 new 키워드)
let level = amp.getLevel(); 현재 프레임의 음량을 0~1로 받아옴
let size = map(level, 0, 1, 10, 400); 음량(0~1) → 크기(10~400)로 변환
ellipse(width/2, height/2, size, size); 변환된 크기로 원 그리기

5주차에서 배운 map()이 여기서 활용됩니다. 음량이라는 숫자(0~1)를 원의 크기(10~400)로 번역하는 것입니다. 그리고 new p5.Amplitude() — 7주차에서 new Ball()로 객체를 만들었듯, p5.sound가 미리 만들어놓은 클래스를 new로 생성합니다.

위 코드를 실행하면 음량에 따라 원이 커졌다 작아집니다. console.log(level)을 추가하면 실제 숫자값도 같이 확인할 수 있습니다.

음량 → 색상도 같이 매핑하기

function draw() {
  background(0);

  let level = amp.getLevel();

  // 음량 → 크기
  let size = map(level, 0, 1, 10, 400);
  // 음량 → 빨간색 강도
  let r = map(level, 0, 1, 50, 255);
  // 음량 → 파란색 강도 (반대로)
  let b = map(level, 0, 1, 255, 50);

  fill(r, 50, b);
  noStroke();
  ellipse(width / 2, height / 2, size, size);
}

음량이 크면 빨간색, 작으면 파란색. map()을 여러 번 쓰면 하나의 숫자(음량)를 여러 가지 시각적 속성에 동시에 연결할 수 있습니다.

13주차 파티클과의 연결 (개념만)

13주차 파티클 시스템에서 Emitter가 매 프레임 파티클을 생성했는데, 그 개수를 음량에 따라 조절하면 됩니다.

// draw() 안에서
let level = amp.getLevel();
let particleCount = int(map(level, 0, 1, 0, 20));

for (let i = 0; i < particleCount; i++) {
  emitter.addParticle();
}

음량이 클 때 파티클이 20개씩 뿜어져 나오고, 조용할 때는 0~2개만. 도입에서 본 데모가 이렇게 만들어집니다. 이 코드는 2교시 실전 사례에서 직접 다룹니다.

확인 질문

amp.getLevel()0.3을 반환했습니다. map(0.3, 0, 1, 10, 400)의 결과는 대략 얼마일까요?

: 약 127 — 10 + (400-10) * 0.3 = 127.


핵심 개념 4 — 완성 예제

지금까지 배운 걸 처음부터 조립합니다.

Step 1 — 빈 스케치에서 사운드 로드

let song;

function preload() {
  song = loadSound('assets/music.mp3');
}

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
}

아직 아무것도 안 보입니다. 검은 캔버스. 하지만 사운드 파일은 로드됐습니다.

Step 2 — 클릭으로 재생

function mousePressed() {
  userStartAudio();
  song.loop();
}

클릭하면 음악이 시작됩니다. 아직 비주얼은 없지만, 소리는 들립니다.

Step 3 — Amplitude 추가, 숫자 확인

let amp;

function setup() {
  createCanvas(400, 400);
  amp = new p5.Amplitude();
}

function draw() {
  background(0);
  let level = amp.getLevel();
  text('Level: ' + nf(level, 1, 3), 20, 30); // 확인용 출력
}

nf(level, 1, 3) — 소수점 3자리까지 표시. 화면에 숫자가 실시간으로 바뀝니다.

Step 4 — 원 크기 + 색상 매핑

function draw() {
  background(0);

  let level = amp.getLevel();
  let size = map(level, 0, 1, 10, 400);
  let r = map(level, 0, 1, 50, 255);
  let b = map(level, 0, 1, 255, 50);

  fill(r, 50, b);
  noStroke();
  ellipse(width / 2, height / 2, size, size);
}

완성. 코드 총 25줄 정도로 소리에 반응하는 비주얼이 만들어졌습니다. 조용한 부분은 작고 파란 원, 큰 부분은 크고 빨간 원. 소리가 눈에 보이게 됐습니다.

전체 완성 코드.

let song;
let amp;

function preload() {
  song = loadSound('assets/music.mp3');
}

function setup() {
  createCanvas(400, 400);
  amp = new p5.Amplitude();
}

function draw() {
  background(0);

  let level = amp.getLevel();
  let size = map(level, 0, 1, 10, 400);
  let r = map(level, 0, 1, 50, 255);
  let b = map(level, 0, 1, 255, 50);

  fill(r, 50, b);
  noStroke();
  ellipse(width / 2, height / 2, size, size);
}

function mousePressed() {
  userStartAudio();
  song.loop();
}

핵심 개념 5 — 마이크 입력 (p5.AudioIn): 같은 패턴, 입력만 바꾸기

지금까지는 음악 파일을 재생해서 분석했습니다. 그런데 파일 없이, 지금 이 교실의 소리에 반응하게 만들 수도 있습니다. 입력 소스를 마이크로 바꾸면 됩니다.

핵심은 단 두 줄입니다. p5.AudioIn을 만들고, amp.setInput(mic)로 Amplitude의 분석 대상을 마이크로 지정하면 나머지는 4번 완성 예제와 완전히 같습니다.

let mic;
let amp;

function setup() {
  createCanvas(400, 400);
  mic = new p5.AudioIn();        // 마이크 입력 생성
  amp = new p5.Amplitude();
  amp.setInput(mic);             // 핵심 — Amplitude의 입력을 마이크로
}

function mousePressed() {
  userStartAudio();
  mic.start();                   // 마이크 입력 시작
}

function draw() {
  background(0);
  let level = amp.getLevel();
  let size = map(level, 0, 1, 10, 400);
  fill(255);
  noStroke();
  ellipse(width / 2, height / 2, size, size);
}

핵심 포인트.

  • new p5.AudioIn() — 마이크 입력 객체 생성
  • mic.start() — 마이크 입력 시작 (역시 사용자 인터랙션 후에)
  • amp.setInput(mic) — Amplitude가 분석할 입력을 마이크로 지정
  • draw() 안의 비주얼 코드는 4번 완성 예제와 동일

캔버스를 클릭하고 손뼉을 치면 원이 커지고, 조용하면 작아집니다.

핵심

파일이든 마이크든 Amplitude 패턴은 똑같습니다. setInput() 한 줄로 입력 소스만 바꾸면, 같은 시각화 코드가 다른 작품이 됩니다.

주의

브라우저가 마이크 권한을 요청합니다. ’허용’을 눌러야 동작합니다. 노이즈가 많은 환경에서는 파일 기반이 더 안정적입니다.

확인 질문

4번 완성 예제 코드를 마이크 입력으로 바꾸려면 어디를 고쳐야 할까요?

: preload/song 부분을 빼고, setupmic = new p5.AudioIn(); amp.setInput(mic); 추가, mousePressed에서 song.loop() 대신 mic.start().


정리

핵심 3줄 요약

  1. p5.sound = P5.js의 사운드 확장 라이브러리. Web Editor에서는 이미 포함되어 있음
  2. preload() + loadSound()로 사운드 로드, userStartAudio()로 브라우저 보안 해제 후 재생
  3. p5.Amplitude + getLevel()로 음량을 숫자(0~1)로 받아 map()으로 크기/색상에 연결. 입력만 바꾸면 마이크도 동일 패턴 (setInput())

이전 주차와의 연결

2교시 예고

1교시에서는 전체 음량을 분석했습니다. 소리가 크면 원이 크고, 작으면 원이 작고. 그런데 음악에는 저음(베이스 드럼), 중음(보컬), 고음(심벌)이 있습니다. 이걸 따로 분석하면 훨씬 풍부한 비주얼이 가능합니다.

2교시에서는 FFT 주파수 분석, 13주차 파티클 + 사운드 통합 사례, 이미지와 웹캠 활용, 그리고 기말 프로젝트 안내까지 다룹니다.