플래시 플레이어 9 API에 사운드의 스펙트럼을 얻을 수 있는 기능이 추가되었습니다. SoundMixer 클래스를 보면 정적 메서드 computeSpectrum()이 정의되어 있습니다. 이 메서드가 핵심입니다.

computeSpectrum(outputArray:ByteArray, FFTMode:Boolean = false, stretchFactor:int = 0):void

[static] 현재 사운드 웨이브의 스냅샷을 취하여 지정된 ByteArray 객체에 배치합니다.

FFTMode는 고속 푸리에 변환(Fast Fourier Transform) 여부를 결정합니다. 푸리에 변환은 신호를 주파수 성분으로 분해하는 조작이라는데 저도 잘 모르겠으니 전문 자료를 참고하세요. FFTMode를 사용하지 않으면(false) 처리 속도가 느린 듯합니다.

stretchFactor는 사운드 샘플의 해상도입니다. 값에 따른 샘플링 해상도는 다음과 같습니다.
0 : 44.1KHz
1 : 22.05KHz
2 : 11.025KHz
잠깐 샘플링률을 짚어 보겠습니다. (이 내용은 찰스 페졸드의 [프로그래밍 윈도우즈 5판]에서 부분 발췌) 사람의 귀는 20hz~20khz 사이의 사인파를 감지할 수 있습니다. 하지만 샘플링률은 샘플링할 사운드의 최대 주파수보다 적어도 두 배는 되어야 합니다. 이것도 자세히는 모르고 나이퀴스트 주파수(Nyquist Frequency) 관련 자료를 찾아 보세요.

너무 낮은 샘플링률로 샘플하면 원래 것보다 낮은 주파수를 가지게 됩니다. 이런 현상을 앨리어스(alias)라고 합니다. 이를 피하려면 저대역 통과 필터를 사용해 샘플링률의 1/2보다 큰 모든 주파수를 막아야 합니다.

사람의 귀는 20khz까지 들을 수 있으므로, 사운드의 전체 범위를 캡쳐하려면 40khz의 샘플링률이 필요합니다. 하지만 저대역 통과 필터에는 감쇠 현상(roll-off effect)이 있어서 샘플링률이 10%는 높아야 합니다. 또, 혹시라도 디지털 오디오를 비디오와 더불어 녹화하는 경우에 대비하려면, 샘플링률은 미국과 유럽 텔레비전 프레임률(각각 30hz와 25hz)의 정수배가 되어야 합니다. 그래서 샘플링률은 44.1khz가 되었습니다. 발췌 끝.

computeSpectrum의 첫번째 매개변수 outputArray에는 사운드 웨이브 데이터, 즉 왼쪽 스피커의 사운드 정보 256개, 오른쪽 스피커의 사운드 정보 256개가 들어갑니다. 사운드 데이터의 총 길이는 512인데, outputArray에 들어온 데이터의 길이(length)는 2048입니다. 그러므로 readFloat()으로 4개씩 끊어 읽습니다. 다음은 간단한 예제입니다.

example 1-1.zip



사용자 삽입 이미지


FFTMode를 사용하지 않으면 왼쪽, 오른쪽 스피커에서 얻어오는 데이터의 수치가 거의 같기 때문에 루프를 256번 반복해서 왼쪽 스피커의 정보만 얻어왔습니다. 스펙트럼 값을 x라고 치면, x의 범위는 -1 ≤ x ≤ 1 입니다. FFTMode를 사용하지 않으면 512개의 값이 모두 -1 ~ 1 사이입니다. FFTMode를 사용한다면 왼쪽 스피커에서 얻어오는 값은 0~1 사이고, 오른쪽 스피커에서 얻어오는 값은 -1~0 사이입니다.

연산을 줄이려고 가로 크기를 256으로 맞췄습니다. 만약 가로 길이가 300이고, 가로 길이에 맞춰서 선을 그리고 싶다면 다음과 같이 합니다.

g.moveTo(i*(300/256), -num);
g.lineTo(i*(300/256), -num);


다음 예제는 위의 코드에서 FFTMode를 사용하게 바꾸기만 한 것입니다.

example 1-2.zip


사용자 삽입 이미지



256개의 정보를 모두 표현하고 싶지 않은 상황도 있을 것입니다. 예를 들어 시각화를 막대로 표현한다면

사용자 삽입 이미지



256개의 막대를 표현한다면 막대의 가로가 매우 짧아야 하고, 이건 앞의 예제 같이 선으로 표현하는 것과 다를 것이 없습니다. 막대를 16개 정도만 그리고 싶다면? 16개만 읽으면 됩니다.

example 1-3.zip


크기를 맞추기 위해 가로는 160으로 줄였습니다. 이렇게 하면 앞의 16개만 읽어옵니다. 앞의 것만 읽어오는 것이 찜찜하다면 position을 알맞게 더하면 되지만, 글쎄 그렇게 할 이유가 있을까요?

사용자 삽입 이미지


최대값만을 이용한 시각화도 만들 수 있습니다. 최대값을 얻기 위해 루프를 256번 돌려서 모든 값을 검사할 필요는 없습니다. SoundChannel 객체의 leftPeak, rightPeak 속성은 각각 왼쪽, 오른쪽 스피커에서 나는 음의 최대 값을 반환합니다. SoundChannel 객체는 Sound객체.play()를 호출하면 반환됩니다.

example 1-4.zip


사용자 삽입 이미지





Posted by codeonwort
,