proper synchronization for reading samples; change to sample processor
interface to avoid creating unnessecary io streams; minor cleanup;
This commit is contained in:
parent
78324045f0
commit
55cac998ed
@ -20,13 +20,14 @@ public class RhythmBulletAudioThread extends Thread {
|
|||||||
music = vm;
|
music = vm;
|
||||||
this.sp = vm.getSampleProcessor();
|
this.sp = vm.getSampleProcessor();
|
||||||
pcm = new short[sp.getChannels()*windowSize];
|
pcm = new short[sp.getChannels()*windowSize];
|
||||||
|
|
||||||
this.ad = Gdx.audio.newAudioDevice(sp.getSampleRate(), (sp.getChannels() > 1 ? false : true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (!terminated && sp.readSamples(pcm) > 0) {
|
sp.initiate();
|
||||||
|
this.ad = Gdx.audio.newAudioDevice(sp.getSampleRate(), (sp.getChannels() > 1 ? false : true));
|
||||||
|
|
||||||
|
while (!terminated && sp.readSamples(pcm, this) > 0) {
|
||||||
ad.writeSamples(pcm, 0, pcm.length);
|
ad.writeSamples(pcm, 0, pcm.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ public class VisualizableMusic implements Music {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} else if (musicFile.getName().toLowerCase().endsWith("mp3")) {
|
} else if (musicFile.getName().toLowerCase().endsWith("mp3")) {
|
||||||
sampleProcessor = new MP3SampleProcessor();
|
sampleProcessor = new MP3SampleProcessor(musicFile, windowSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,8 +38,10 @@ public class VisualizableMusic implements Music {
|
|||||||
if (rat == null) {
|
if (rat == null) {
|
||||||
rat = new RhythmBulletAudioThread(windowSize, this);
|
rat = new RhythmBulletAudioThread(windowSize, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
rat.setOnCompletionListener(ocl);
|
rat.setOnCompletionListener(ocl);
|
||||||
rat.play();
|
rat.play();
|
||||||
|
rat.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -105,6 +107,9 @@ public class VisualizableMusic implements Music {
|
|||||||
@Override
|
@Override
|
||||||
public void setOnCompletionListener(OnCompletionListener listener) {
|
public void setOnCompletionListener(OnCompletionListener listener) {
|
||||||
this.ocl = listener;
|
this.ocl = listener;
|
||||||
|
if (rat != null) {
|
||||||
|
rat.setOnCompletionListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SampleProcessor getSampleProcessor() {
|
protected SampleProcessor getSampleProcessor() {
|
||||||
|
@ -1,6 +1,25 @@
|
|||||||
package zero1hd.rhythmbullet.audio.processor;
|
package zero1hd.rhythmbullet.audio.processor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class MP3SampleProcessor implements SampleProcessor {
|
public class MP3SampleProcessor implements SampleProcessor {
|
||||||
|
private int channels;
|
||||||
|
|
||||||
|
public MP3SampleProcessor(File musicFile, int windowSize) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initiate() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getChannels() {
|
public int getChannels() {
|
||||||
@ -15,7 +34,7 @@ public class MP3SampleProcessor implements SampleProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readSamples(short[] pcm) {
|
public int readSamples(short[] pcm, Object syncObj) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -43,4 +62,5 @@ public class MP3SampleProcessor implements SampleProcessor {
|
|||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
package zero1hd.rhythmbullet.audio.processor;
|
package zero1hd.rhythmbullet.audio.processor;
|
||||||
|
|
||||||
public interface SampleProcessor {
|
import com.badlogic.gdx.utils.Disposable;
|
||||||
|
|
||||||
|
public interface SampleProcessor extends Disposable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once, contains the initiation to the stream, only called when play-back begins.
|
||||||
|
* Not thread safe as it should be the first thing to be called during read process.
|
||||||
|
*/
|
||||||
|
public void initiate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return number of channels
|
* @return number of channels
|
||||||
*/
|
*/
|
||||||
@ -12,29 +21,30 @@ public interface SampleProcessor {
|
|||||||
public int getSampleRate();
|
public int getSampleRate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Should be thread-safe!</b>
|
* <b>Thread safe</b>
|
||||||
* Reads samples (NOT FRAMES) with interwoven data for stereo.
|
* Reads samples (NOT FRAMES) with interwoven data for stereo.
|
||||||
* stored in 16 bit format (first 8 are the first byte of data while the second 8 are the second byte of data that composes a short value)
|
* stored in 16 bit format (first 8 are the first byte of data while the second 8 are the second byte of data that composes a short value)
|
||||||
* @param pcm the array the samples should fill
|
* @param pcm the array the samples should fill
|
||||||
|
* @param syncObj the object that this object should use to synchronize multiple threads.
|
||||||
* @return the amount of samples read.
|
* @return the amount of samples read.
|
||||||
*/
|
*/
|
||||||
public int readSamples(short[] pcm);
|
public int readSamples(short[] pcm, Object syncObj);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Should be thread-safe!</b>
|
* <b>Thread safe</b>
|
||||||
* ranges from 0 to 1.
|
* ranges from 0 to 1.
|
||||||
* @param volume the volume to set
|
* @param volume the volume to set
|
||||||
*/
|
*/
|
||||||
public void setVolume(float volume);
|
public void setVolume(float volume);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Should be thread-safe!</b>
|
* <b>Thread safe</b>
|
||||||
* @return the volume ranging from 0 to 1
|
* @return the volume ranging from 0 to 1
|
||||||
*/
|
*/
|
||||||
public float getVolume();
|
public float getVolume();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Should be thread-safe!</b>
|
* <b>Thread safe</b>
|
||||||
* sets the pan from the left channel to the right.
|
* sets the pan from the left channel to the right.
|
||||||
* ranges from -1 to 1.
|
* ranges from -1 to 1.
|
||||||
* Default is 0, -1 is left, 1 is right.
|
* Default is 0, -1 is left, 1 is right.
|
||||||
@ -43,7 +53,7 @@ public interface SampleProcessor {
|
|||||||
public void setPan(float pan);
|
public void setPan(float pan);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Should be thread-safe!</b>
|
* <b>Thread safe</b>
|
||||||
* Returns the pan value from -1 to 1.
|
* Returns the pan value from -1 to 1.
|
||||||
* see {@link #setPan(float)} for more information.
|
* see {@link #setPan(float)} for more information.
|
||||||
* @return the pan value.
|
* @return the pan value.
|
||||||
|
@ -3,6 +3,7 @@ package zero1hd.rhythmbullet.audio.processor;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioFormat;
|
||||||
import javax.sound.sampled.AudioInputStream;
|
import javax.sound.sampled.AudioInputStream;
|
||||||
import javax.sound.sampled.AudioSystem;
|
import javax.sound.sampled.AudioSystem;
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
@ -11,18 +12,30 @@ public class WAVSampleProcessor implements SampleProcessor {
|
|||||||
private int channels;
|
private int channels;
|
||||||
private int sampleRate;
|
private int sampleRate;
|
||||||
private byte[] buffer;
|
private byte[] buffer;
|
||||||
|
private File file;
|
||||||
private AudioInputStream audioInputStream;
|
private AudioInputStream audioInputStream;
|
||||||
private volatile float volume, pan;
|
private volatile float volume, pan;
|
||||||
|
private volatile boolean initiated;
|
||||||
|
|
||||||
public WAVSampleProcessor(File musicFile, int windowSize) throws IOException, UnsupportedAudioFileException {
|
public WAVSampleProcessor(File musicFile, int windowSize) throws IOException, UnsupportedAudioFileException {
|
||||||
audioInputStream = AudioSystem.getAudioInputStream(musicFile);
|
this.file = musicFile;
|
||||||
buffer = new byte[audioInputStream.getFormat().getFrameSize()];
|
AudioFormat format = AudioSystem.getAudioFileFormat(file).getFormat();
|
||||||
|
channels = format.getChannels();
|
||||||
channels = audioInputStream.getFormat().getChannels();
|
sampleRate = (int) format.getSampleRate();
|
||||||
sampleRate = (int) audioInputStream.getFormat().getSampleRate();
|
|
||||||
volume = 1f;
|
volume = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initiate() {
|
||||||
|
try {
|
||||||
|
audioInputStream = AudioSystem.getAudioInputStream(file);
|
||||||
|
} catch (UnsupportedAudioFileException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
buffer = new byte[audioInputStream.getFormat().getFrameSize()];
|
||||||
|
initiated = true;
|
||||||
|
}
|
||||||
|
|
||||||
public int getChannels() {
|
public int getChannels() {
|
||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
@ -32,7 +45,9 @@ public class WAVSampleProcessor implements SampleProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized int readSamples(short[] pcm) {
|
public int readSamples(short[] pcm, Object syncObj) {
|
||||||
|
if (initiated) {
|
||||||
|
synchronized (syncObj) {
|
||||||
int framesRead = 0;
|
int framesRead = 0;
|
||||||
for (int sampleID = 0; sampleID < pcm.length; sampleID++) {
|
for (int sampleID = 0; sampleID < pcm.length; sampleID++) {
|
||||||
try {
|
try {
|
||||||
@ -54,6 +69,10 @@ public class WAVSampleProcessor implements SampleProcessor {
|
|||||||
}
|
}
|
||||||
return framesRead;
|
return framesRead;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Stream has not been initialized.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVolume(float volume) {
|
public void setVolume(float volume) {
|
||||||
@ -74,4 +93,10 @@ public class WAVSampleProcessor implements SampleProcessor {
|
|||||||
public float getPan() {
|
public float getPan() {
|
||||||
return pan;
|
return pan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user