특정 주파수의 음을 사인파(Sine Wave)로 재생하고 싶다면, javax.sound.sampled.* 라이브러리를 사용합니다. javax 시리즈는 자바에서 기본 제공됩니다.

 

1) new AudioFormat으로 새로운 오디오 형식 생성
public AudioFormat(float sampleRate:샘플수,
           int sampleSizeInBits:비트당샘플사이즈,
           int channels:채널수,
           boolean signed:부호여부,
           boolean bigEndian:빅엔디안여부)

 

2) 생성한 AudioFormat에서 소스 데이터 라인(Source Data Line)을 추출 후 SourceDataLine 자료형 변수에 저장
AudioFormat af = new AudioFormat(SAMPLE_RATE, BITS, CHANNELS, IS_BIG_ENDIAN);
SourceDataLine sdl = AudioSystem.getSourceDataLine(af);

 

3) sdl 오픈(open) 및 시작(start)
sdl.open(af);
sdl.start();

 

4) 재생 정보에 대한 byte형 배열 생성 후 해당 배열을 sld.write()를 이용하여 기록
// Speicify playback time from SAMPLE_RATE
int sizePerMs = (int)(SAMPLE_RATE / 1000);

for(int i = 0; i < ms * sizePerMs; i++) {
  double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI;
  buf[0] = (byte) (Math.sin(angle) * 127.0 * vol);
  sdl.write(buf, 0, 1);
}

배열에는 특정 주파수에 대한 사인파 정보를 재생시간(ms) * ms당 사이즈 만큼 기록합니다. i가 1이고, 샘플수가 44000, 주파수는 440인 경우 angle0.0628이며, 이것에 대한 사인(sine)각도을 구하면 약 13.92입니다. 구해진 사인각은 그래프에서 y축의 높이 정보에 활용됩니다(자세한 사항은 푸리에 변환 참조). 여기에 127을 곱하는 이유는 바이트형 배열의 범위가 -128~127(참고사이트)이기 때문입니다. vol(볼륨)의 기본값은 1.0입니다. 재생시간이 1초(1000ms)인 경우, 44000 / 1000 * 1000 으로 i43999까지 진행합니다.

 

5) sd의 버퍼를 비우고(drain) 정지(stop), 닫기(close) 실행
sdl.drain();
sdl.stop();
sdl.close();

drain()을 하지 않으면 소리가 재생되지 않습니다.

 

추가 예제: 제이콥 콜리어(Jaco Collier)의 미분음 게임 (Microtonal Game)

위 영상의 과정을 프로그래밍으로 나타내 봤습니다. 미 ~ 솔을 일정 수로 2등분부터 8등분까지 나눠서 노래를 부르고 있는데 이 사람은 외계인이라 가능한거고 다른 사람들은 절대 이런 식으로 할 수 없습니다. 원래는 평균율 기준으로 파#3등분입니다.


package blog.freq;
import java.util.ArrayList;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class Freq {
public static float SAMPLE_RATE = 8000f;
private AudioFormat af;
private SourceDataLine sdl;
// Constructor
public Freq() throws LineUnavailableException {
af = new AudioFormat(SAMPLE_RATE, 8, 1, true, false);
sdl = AudioSystem.getSourceDataLine(af);
}
public void tone(double hz, double ms) throws LineUnavailableException{
tone(hz, ms, 1.0);
}
public void tone(double hz, double ms, double vol) throws LineUnavailableException {
byte[] buf = new byte[1];
sdl.open(af);
sdl.start();
// Speicify playback time from SAMPLE_RATE
int sizePerMs = (int)(SAMPLE_RATE / 1000);
for(int i = 0; i < ms * sizePerMs; i++) {
double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI;
buf[0] = (byte) (Math.sin(angle) * 127.0 * vol);
sdl.write(buf, 0, 1);
}
sdl.drain();
}
public void tones(List<Double> freqs, double ms) throws LineUnavailableException {
tones(freqs, ms, 1.0);
}
public void tones(List<Double> freqs, double ms, double vol) throws LineUnavailableException {
byte[] buf = new byte[1];
sdl.open(af);
sdl.start();
// Speicify playback time from SAMPLE_RATE
int sizePerMs = (int)(SAMPLE_RATE / 1000);
for(double hz : freqs) {
for(int i = 0; i < ms * sizePerMs; i++) {
double angle = i / (SAMPLE_RATE / hz) * 2.0 * Math.PI;
buf[0] = (byte) (Math.sin(angle) * 127.0 * vol);
sdl.write(buf, 0, 1);
}
}
sdl.drain();
}
public void close() {
sdl.stop();
sdl.close();
}
public static void main(String[] args) throws Exception {
Freq f = new Freq();
// double a4 = 452;
// for(int i = 1; i <= 16; i++) {
// System.out.println(a4 / 6.727 * i);
// f.tone(a4 / 6.727 * i, 500);
// }
System.out.println("== Microtonal Game (Ascending) ==\n");
double startFreq = 329.63, // E4 ~ G4
endFreq = 392;
int startDiv = 2, endDiv = 8;
for(int i = startDiv; i <= endDiv; i++) {
System.out.printf("–[%d]–\n", i);
double eachFreq = (endFreq – startFreq) / i;
List<Double> freqs = new ArrayList<Double>();
for(int j = 0; j <= i; j++) {
double freq = startFreq + eachFreq * j;
freqs.add(freq);
}
// Display each frequency on time
new Thread(() -> {
try {
for(Double hz : freqs) {
System.out.printf("%.2f\t", hz);
Thread.sleep(200);
}
}catch(InterruptedException e) {}
}).start();
f.tones(freqs, 200);
Thread.sleep(500);
System.out.print("\n\n");
}
}
}

view raw

Freq.java

hosted with ❤ by GitHub

문의 | 코멘트 또는 yoonbumtae@gmail.com


카테고리: JavaMedia Art


3개의 댓글

KIM TAE WOONG · 2021년 3월 18일 10:23 오전

검색하다 좋은정보 얻고갑니다.

그리고 자바에서 mp3를 1초단위든 5초단위든 볼륨크기를 출력 할 수 있을까요? 열심이 검색중인데 잘 보이지 않네요

    yoonbumtae (BGSMM) · 2021년 3월 18일 4:39 오후

    죄송합니다 저도 잘 모르겠습니다..
    외부 라이브러리를 사용한 방법이라면
    http://yoonbumtae.com/?p=865
    이 글이 음악 파일의 시간별 주파수를 분석하는 방법인데
    주파수 대신 볼륨 정보를 얻는 것으로 가능하지 않을까 합니다.

자바스크립트 예제: 특정 주파수의 소리 재생 + 음악 평균율 주파수 테이블 - BGSMM · 2019년 11월 4일 12:35 오전

[…] 주파수를 재생하는 글 자바 예제: 특정 주파수의 소리 재생 을 자바스크립트로 바꾼 예제입니다. 외부 온라인 소스나 라이브러리 […]

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다