Minor restructuring done; furthered functionality of the audio system;
further work on thread-safety of the classes.
This commit is contained in:
parent
31b2278e9b
commit
78324045f0
@ -1,29 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
|
||||||
|
|
||||||
import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
|
|
||||||
import zero1hd.rhythmbullet.audio.processor.SampleProcessor;
|
|
||||||
|
|
||||||
class AudioFileReadingThread extends Thread {
|
|
||||||
private boolean function = true;
|
|
||||||
private Array<SampleProcessor> sps;
|
|
||||||
SampleProcessor sp;
|
|
||||||
FloatFFT_1D fft;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (function) {
|
|
||||||
if (sps.size > 0) {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.run();
|
|
||||||
}
|
|
||||||
}
|
|
75
core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudioThread.java
Executable file
75
core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudioThread.java
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
package zero1hd.rhythmbullet.audio;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx;
|
||||||
|
import com.badlogic.gdx.audio.AudioDevice;
|
||||||
|
import com.badlogic.gdx.audio.Music;
|
||||||
|
import com.badlogic.gdx.audio.Music.OnCompletionListener;
|
||||||
|
|
||||||
|
import zero1hd.rhythmbullet.audio.processor.SampleProcessor;
|
||||||
|
|
||||||
|
public class RhythmBulletAudioThread extends Thread {
|
||||||
|
private AudioDevice ad;
|
||||||
|
private SampleProcessor sp;
|
||||||
|
private Music music;
|
||||||
|
private volatile OnCompletionListener ocl;
|
||||||
|
private short[] pcm;
|
||||||
|
private volatile boolean terminated;
|
||||||
|
private volatile boolean playing;
|
||||||
|
|
||||||
|
public RhythmBulletAudioThread(int windowSize, VisualizableMusic vm) {
|
||||||
|
music = vm;
|
||||||
|
this.sp = vm.getSampleProcessor();
|
||||||
|
pcm = new short[sp.getChannels()*windowSize];
|
||||||
|
|
||||||
|
this.ad = Gdx.audio.newAudioDevice(sp.getSampleRate(), (sp.getChannels() > 1 ? false : true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!terminated && sp.readSamples(pcm) > 0) {
|
||||||
|
ad.writeSamples(pcm, 0, pcm.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ocl != null) {
|
||||||
|
ocl.onCompletion(music);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void play() {
|
||||||
|
playing = true;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void pause() {
|
||||||
|
playing = false;
|
||||||
|
while (!playing) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized short[] getPcm() {
|
||||||
|
return pcm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void terminate() {
|
||||||
|
terminated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTerminated() {
|
||||||
|
return terminated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlaying() {
|
||||||
|
return playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnCompletionListener(OnCompletionListener ocl) {
|
||||||
|
this.ocl = ocl;
|
||||||
|
}
|
||||||
|
}
|
113
core/src/zero1hd/rhythmbullet/audio/VisualizableMusic.java
Executable file
113
core/src/zero1hd/rhythmbullet/audio/VisualizableMusic.java
Executable file
@ -0,0 +1,113 @@
|
|||||||
|
package zero1hd.rhythmbullet.audio;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.audio.Music;
|
||||||
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
|
|
||||||
|
import zero1hd.rhythmbullet.audio.processor.MP3SampleProcessor;
|
||||||
|
import zero1hd.rhythmbullet.audio.processor.SampleProcessor;
|
||||||
|
import zero1hd.rhythmbullet.audio.processor.WAVSampleProcessor;
|
||||||
|
|
||||||
|
public class VisualizableMusic implements Music {
|
||||||
|
private int windowSize = 1024;
|
||||||
|
private OnCompletionListener ocl;
|
||||||
|
private boolean looping;
|
||||||
|
private RhythmBulletAudioThread rat;
|
||||||
|
private File musicFile;
|
||||||
|
private SampleProcessor sampleProcessor;
|
||||||
|
|
||||||
|
public VisualizableMusic(FileHandle file) {
|
||||||
|
musicFile = file.file();
|
||||||
|
if (musicFile.getName().toLowerCase().endsWith("wav")) {
|
||||||
|
try {
|
||||||
|
sampleProcessor = new WAVSampleProcessor(musicFile, windowSize);
|
||||||
|
} catch (IOException | UnsupportedAudioFileException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else if (musicFile.getName().toLowerCase().endsWith("mp3")) {
|
||||||
|
sampleProcessor = new MP3SampleProcessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void play() {
|
||||||
|
if (rat == null) {
|
||||||
|
rat = new RhythmBulletAudioThread(windowSize, this);
|
||||||
|
}
|
||||||
|
rat.setOnCompletionListener(ocl);
|
||||||
|
rat.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pause() {
|
||||||
|
rat.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
rat.terminate();
|
||||||
|
rat = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPlaying() {
|
||||||
|
return rat.isPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLooping(boolean isLooping) {
|
||||||
|
looping = isLooping;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLooping() {
|
||||||
|
return looping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVolume(float volume) {
|
||||||
|
sampleProcessor.setVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getVolume() {
|
||||||
|
return sampleProcessor.getVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPan(float pan, float volume) {
|
||||||
|
sampleProcessor.setPan(pan);
|
||||||
|
sampleProcessor.setVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPosition(float position) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getPosition() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnCompletionListener(OnCompletionListener listener) {
|
||||||
|
this.ocl = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SampleProcessor getSampleProcessor() {
|
||||||
|
return sampleProcessor;
|
||||||
|
}
|
||||||
|
}
|
@ -20,5 +20,27 @@ public class MP3SampleProcessor implements SampleProcessor {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVolume(float volume) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getVolume() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPan(float pan) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getPan() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,52 @@
|
|||||||
package zero1hd.rhythmbullet.audio.processor;
|
package zero1hd.rhythmbullet.audio.processor;
|
||||||
|
|
||||||
public interface SampleProcessor {
|
public interface SampleProcessor {
|
||||||
|
/**
|
||||||
|
* @return number of channels
|
||||||
|
*/
|
||||||
public int getChannels();
|
public int getChannels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return sample rate
|
||||||
|
*/
|
||||||
public int getSampleRate();
|
public int getSampleRate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>Should be thread-safe!</b>
|
||||||
|
* 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)
|
||||||
|
* @param pcm the array the samples should fill
|
||||||
|
* @return the amount of samples read.
|
||||||
|
*/
|
||||||
public int readSamples(short[] pcm);
|
public int readSamples(short[] pcm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>Should be thread-safe!</b>
|
||||||
|
* ranges from 0 to 1.
|
||||||
|
* @param volume the volume to set
|
||||||
|
*/
|
||||||
|
public void setVolume(float volume);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>Should be thread-safe!</b>
|
||||||
|
* @return the volume ranging from 0 to 1
|
||||||
|
*/
|
||||||
|
public float getVolume();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>Should be thread-safe!</b>
|
||||||
|
* sets the pan from the left channel to the right.
|
||||||
|
* ranges from -1 to 1.
|
||||||
|
* Default is 0, -1 is left, 1 is right.
|
||||||
|
* @param pan
|
||||||
|
*/
|
||||||
|
public void setPan(float pan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <b>Should be thread-safe!</b>
|
||||||
|
* Returns the pan value from -1 to 1.
|
||||||
|
* see {@link #setPan(float)} for more information.
|
||||||
|
* @return the pan value.
|
||||||
|
*/
|
||||||
|
public float getPan();
|
||||||
}
|
}
|
||||||
|
@ -1,144 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio.processor;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
import com.badlogic.gdx.audio.AudioDevice;
|
|
||||||
import com.badlogic.gdx.audio.Music;
|
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
|
||||||
|
|
||||||
public class VisualizableMusic implements Music {
|
|
||||||
private int windowSize = 1024;
|
|
||||||
private OnCompletionListener ocl;
|
|
||||||
private volatile boolean playing, updatedPCMData;
|
|
||||||
private boolean looping;
|
|
||||||
private volatile short[] pcm;
|
|
||||||
private volatile float[] normalized;
|
|
||||||
private volatile SampleProcessor sp;
|
|
||||||
private volatile float volume = 1f;
|
|
||||||
|
|
||||||
public VisualizableMusic(FileHandle file) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void play() {
|
|
||||||
playing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pause() {
|
|
||||||
playing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPlaying() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLooping(boolean isLooping) {
|
|
||||||
looping = isLooping;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLooping() {
|
|
||||||
return looping;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setVolume(float volume) {
|
|
||||||
this.volume = volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getVolume() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPan(float pan, float volume) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPosition(float position) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getPosition() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setOnCompletionListener(OnCompletionListener listener) {
|
|
||||||
this.ocl = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void normalize(short[] pcm, float[] normalized) {
|
|
||||||
int currentFrame = 0;
|
|
||||||
for (int sampleID = 0; sampleID < pcm.length; sampleID++) {
|
|
||||||
for (int chan = 0; chan < sp.getChannels(); chan++) {
|
|
||||||
if (normalized[currentFrame] > pcm[sampleID]) {
|
|
||||||
normalized[currentFrame] = pcm[sampleID];
|
|
||||||
}
|
|
||||||
sampleID++;
|
|
||||||
}
|
|
||||||
|
|
||||||
normalized[currentFrame] /= Short.MAX_VALUE+1;
|
|
||||||
currentFrame++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RhythmBulletAudioThread extends Thread {
|
|
||||||
private AudioDevice ad;
|
|
||||||
|
|
||||||
public RhythmBulletAudioThread(int windowSize, File file) {
|
|
||||||
if (file.getName().toLowerCase().endsWith("wav")) {
|
|
||||||
try {
|
|
||||||
sp = new WAVSampleProcessor(file, windowSize);
|
|
||||||
} catch (IOException | UnsupportedAudioFileException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else if (file.getName().toLowerCase().endsWith("mp3")) {
|
|
||||||
sp = new MP3SampleProcessor();
|
|
||||||
}
|
|
||||||
|
|
||||||
pcm = new short[sp.getChannels()*windowSize];
|
|
||||||
normalized = new float[windowSize];
|
|
||||||
|
|
||||||
this.ad = Gdx.audio.newAudioDevice(sp.getSampleRate(), (sp.getChannels() > 1 ? true : false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (playing && sp.readSamples(pcm) > 0) {
|
|
||||||
updatedPCMData = true;
|
|
||||||
ad.writeSamples(pcm, 0, pcm.length);
|
|
||||||
}
|
|
||||||
super.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,8 +12,8 @@ public class WAVSampleProcessor implements SampleProcessor {
|
|||||||
private int sampleRate;
|
private int sampleRate;
|
||||||
private byte[] buffer;
|
private byte[] buffer;
|
||||||
private AudioInputStream audioInputStream;
|
private AudioInputStream audioInputStream;
|
||||||
private boolean mergeChannels;
|
private volatile float volume, pan;
|
||||||
private float volume, pan;
|
|
||||||
public WAVSampleProcessor(File musicFile, int windowSize) throws IOException, UnsupportedAudioFileException {
|
public WAVSampleProcessor(File musicFile, int windowSize) throws IOException, UnsupportedAudioFileException {
|
||||||
audioInputStream = AudioSystem.getAudioInputStream(musicFile);
|
audioInputStream = AudioSystem.getAudioInputStream(musicFile);
|
||||||
buffer = new byte[audioInputStream.getFormat().getFrameSize()];
|
buffer = new byte[audioInputStream.getFormat().getFrameSize()];
|
||||||
@ -32,7 +32,7 @@ public class WAVSampleProcessor implements SampleProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readSamples(short[] pcm) {
|
public synchronized int readSamples(short[] pcm) {
|
||||||
int framesRead = 0;
|
int framesRead = 0;
|
||||||
for (int sampleID = 0; sampleID < pcm.length; sampleID++) {
|
for (int sampleID = 0; sampleID < pcm.length; sampleID++) {
|
||||||
try {
|
try {
|
||||||
@ -55,16 +55,23 @@ public class WAVSampleProcessor implements SampleProcessor {
|
|||||||
return framesRead;
|
return framesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioInputStream getAudioInputStream() {
|
@Override
|
||||||
return audioInputStream;
|
public void setVolume(float volume) {
|
||||||
}
|
this.volume = volume;
|
||||||
|
|
||||||
public void setMergeChannels(boolean mergeChannels) {
|
|
||||||
this.mergeChannels = mergeChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMergeChannels() {
|
|
||||||
return mergeChannels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getVolume() {
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPan(float pan) {
|
||||||
|
this.pan = pan;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getPan() {
|
||||||
|
return pan;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user