1. 준비
MP3를 다룰 수 있는 여러 라이브러리가 존재하지만, Java에서는 대표적으로 JLayer가 사용된다.
1.1 JLayer 라이브러리 추가
<http://www.javazoom.net/javalayer/javalayer.html>
위 사이트에서 소스코드, jar 파일 등을 다운받은 뒤, Java Build Path에 폴더 내 jar 파일을 추가해주면 된다.
2. 음악 데이터를 담을 클래스 정의
JLayer 라이브러리 내 Player 클래스를 사용하면 손 쉽게 음악을 재생할 수 있다. 그러나 음악 파일을 분석할 수 있도록 해야 하므로 라이브러리의 기존 코드를 참고하여 클래스를 재정의한다.
2.1 음악 파일 내부
음악은 영상처럼 여러 개의 프레임으로 이루어져 있으며, 한 프레임은 여러 개의 sample 값들로 이루어져 있다.
Sample 클래스 정의
한 프레임의 Sample 값들을 저장하기 위한 클래스를 정의한다.
xxxxxxxxxx
class Sample {
private short[] buffer;
private int size;
public Sample(short[] buf, int s) {
buffer = buf.clone();
size = s;
}
public short[] GetBuffer() {
return buffer;
}
public int GetSize() {
return size;
}
}
Frame의 VO 클래스로, sample 값들을 생성자로 입력받아 저장한다.
Player 클래스 정의
음성 파일을 불러와 해당 음성 파일에 대한 정보를 저장하고 재생할 수 있는 클래스를 정의한다.
xxxxxxxxxx
class MyPlayer {
public static final int BUFFER_SIZE = 44100000;
private Decoder decoder;
private AudioDevice out;
private ArrayList<Sample> samples;
private short[][] musicbuffers;
private int size;
public MyPlayer(String path) {
Open(path);
}
public boolean IsInvalid() {
return (decoder == null || out == null || samples == null || !out.isOpen());
}
.. ..
}
생성자로부터 파일의 경로를 받아 해당 파일을 decoding하여 samples 변수에 저장한다.
xxxxxxxxxx
public boolean Open(String path) {
Close();
try {
decoder = new Decoder();
out = FactoryRegistry.systemRegistry().createAudioDevice();
samples = new ArrayList<Sample>(BUFFER_SIZE);
size = 0;
out.open(decoder);
GetSamples(path);
musicbuffers = GetMusicBuffers();
} catch(JavaLayerException e) {
decoder = null;
out = null;
return false;
}
return true;
}
public void Close() {
if((out != null) && !out.isOpen())
out.close();
size = 0;
samples = null;
out = null;
decoder = null;
}
아래 코드는 JLayer의 decoder 함수를 이용하여 decoding한 뒤 Buffer 값을 얻어낸다.
xxxxxxxxxx
protected boolean GetSamples(String path) {
if(IsInvalid())
return false;
try {
Header header;
SampleBuffer sb;
FileInputStream in = new FileInputStream(path);
Bitstream bitstream = new Bitstream(in);
if((header = bitstream.readFrame()) == null)
return false;
while(size < BUFFER_SIZE && header != null) {
sb = (SampleBuffer)decoder.decodeFrame(header, bitstream);
samples.add(new Sample(sb.getBuffer(), sb.getBufferLength()));
size++;
bitstream.closeFrame();
header = bitstream.readFrame();
}
} catch(FileNotFoundException e) {
return false;
} catch(BitstreamException e) {
return false;
} catch(DecoderException e) {
return false;
}
return true;
}
3. 음악 데이터 출력하기
위에서 정의한 MyPlayer 클래스 내에 음악을 재생하는 함수를 정의한다.
xxxxxxxxxx
public void Play() {
if(IsInvalid())
return;
try {
for(int i=0; i < size; i++) {
short[] buffers = samples.get(i).GetBuffer();
out.write(samples.get(i).GetBuffer(), 0, samples.get(i).GetSize());
}
out.flush();
} catch(JavaLayerException e) { }
}
미리 담아둔 sample 데이터를 AudioDevice 객체를 통해 출력하면 음악을 재생할 수 있다. 이 때 세부적으로 데이터를 처리하기 위해 MyPlayer의 멤버변수 musicbuffers을 초기화하여 사용할 수 있다.
xxxxxxxxxx
.. ..
short[] buffers = musicbuffers[i];
out.write(buffers, 0, samples.get(i).GetSize());
.. ..
이제 메인함수에서 객체를 생성한 뒤 Play 함수를 호출하면 음악이 재생된다.
xxxxxxxxxx
public static void main(String[] args) {
MyPlayer mp = new MyPlayer("C:\\psycho.mp3");
mp.Play();
mp.Close()
}
4. Thread를 사용하여 음악 재생
위의 방법은 음악 재생 중간에 음악을 중단시키거나 다른 일을 할 수 없다. 따라서 Thread 클래스를 정의하여 이를 해결하자.
xxxxxxxxxx
class MusicPlayer extends Thread {
MyPlayer mp;
public MusicPlayer(MyPlayer player) {
mp = player;
}
public void run() {
mp.Play();
}
}
public class MainClass {
public static void main(String[] args) {
MyPlayer mp = new MyPlayer("C:\\psycho.mp3");
MusicPlayer player = new MusicPlayer(mp);
player.start();
Scanner scan = new Scanner(System.in);
scan.nextLine();
mp.Close();
}
}
댓글