From 78324045f075a985837eb9cc2547db917ef8e798 Mon Sep 17 00:00:00 2001 From: Recrown Date: Thu, 14 Jun 2018 00:48:29 -0500 Subject: [PATCH] Minor restructuring done; furthered functionality of the audio system; further work on thread-safety of the classes. --- .../rhythmbullet/audio/RhythmBulletAudio.java | 29 ---- .../audio/RhythmBulletAudioThread.java | 75 +++++++++ .../rhythmbullet/audio/VisualizableMusic.java | 113 ++++++++++++++ .../audio/processor/MP3SampleProcessor.java | 22 +++ .../audio/processor/SampleProcessor.java | 45 ++++++ .../audio/processor/VisualizableMusic.java | 144 ------------------ .../audio/processor/WAVSampleProcessor.java | 33 ++-- 7 files changed, 275 insertions(+), 186 deletions(-) delete mode 100755 core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudio.java create mode 100755 core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudioThread.java create mode 100755 core/src/zero1hd/rhythmbullet/audio/VisualizableMusic.java delete mode 100755 core/src/zero1hd/rhythmbullet/audio/processor/VisualizableMusic.java diff --git a/core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudio.java b/core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudio.java deleted file mode 100755 index aa00e40..0000000 --- a/core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudio.java +++ /dev/null @@ -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 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(); - } -} diff --git a/core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudioThread.java b/core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudioThread.java new file mode 100755 index 0000000..ad744c6 --- /dev/null +++ b/core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudioThread.java @@ -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; + } +} \ No newline at end of file diff --git a/core/src/zero1hd/rhythmbullet/audio/VisualizableMusic.java b/core/src/zero1hd/rhythmbullet/audio/VisualizableMusic.java new file mode 100755 index 0000000..06bd956 --- /dev/null +++ b/core/src/zero1hd/rhythmbullet/audio/VisualizableMusic.java @@ -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; + } +} diff --git a/core/src/zero1hd/rhythmbullet/audio/processor/MP3SampleProcessor.java b/core/src/zero1hd/rhythmbullet/audio/processor/MP3SampleProcessor.java index 9469894..119a0d1 100755 --- a/core/src/zero1hd/rhythmbullet/audio/processor/MP3SampleProcessor.java +++ b/core/src/zero1hd/rhythmbullet/audio/processor/MP3SampleProcessor.java @@ -20,5 +20,27 @@ public class MP3SampleProcessor implements SampleProcessor { 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; + } } diff --git a/core/src/zero1hd/rhythmbullet/audio/processor/SampleProcessor.java b/core/src/zero1hd/rhythmbullet/audio/processor/SampleProcessor.java index 0e672a5..40ef69d 100755 --- a/core/src/zero1hd/rhythmbullet/audio/processor/SampleProcessor.java +++ b/core/src/zero1hd/rhythmbullet/audio/processor/SampleProcessor.java @@ -1,7 +1,52 @@ package zero1hd.rhythmbullet.audio.processor; public interface SampleProcessor { + /** + * @return number of channels + */ public int getChannels(); + + /** + * @return sample rate + */ public int getSampleRate(); + + /** + * Should be thread-safe! + * 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); + + /** + * Should be thread-safe! + * ranges from 0 to 1. + * @param volume the volume to set + */ + public void setVolume(float volume); + + /** + * Should be thread-safe! + * @return the volume ranging from 0 to 1 + */ + public float getVolume(); + + /** + * Should be thread-safe! + * 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); + + /** + * Should be thread-safe! + * Returns the pan value from -1 to 1. + * see {@link #setPan(float)} for more information. + * @return the pan value. + */ + public float getPan(); } diff --git a/core/src/zero1hd/rhythmbullet/audio/processor/VisualizableMusic.java b/core/src/zero1hd/rhythmbullet/audio/processor/VisualizableMusic.java deleted file mode 100755 index 17f6184..0000000 --- a/core/src/zero1hd/rhythmbullet/audio/processor/VisualizableMusic.java +++ /dev/null @@ -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(); - } - - } -} diff --git a/core/src/zero1hd/rhythmbullet/audio/processor/WAVSampleProcessor.java b/core/src/zero1hd/rhythmbullet/audio/processor/WAVSampleProcessor.java index 7b6887c..6923e4d 100755 --- a/core/src/zero1hd/rhythmbullet/audio/processor/WAVSampleProcessor.java +++ b/core/src/zero1hd/rhythmbullet/audio/processor/WAVSampleProcessor.java @@ -12,8 +12,8 @@ public class WAVSampleProcessor implements SampleProcessor { private int sampleRate; private byte[] buffer; private AudioInputStream audioInputStream; - private boolean mergeChannels; - private float volume, pan; + private volatile float volume, pan; + public WAVSampleProcessor(File musicFile, int windowSize) throws IOException, UnsupportedAudioFileException { audioInputStream = AudioSystem.getAudioInputStream(musicFile); buffer = new byte[audioInputStream.getFormat().getFrameSize()]; @@ -32,7 +32,7 @@ public class WAVSampleProcessor implements SampleProcessor { } @Override - public int readSamples(short[] pcm) { + public synchronized int readSamples(short[] pcm) { int framesRead = 0; for (int sampleID = 0; sampleID < pcm.length; sampleID++) { try { @@ -55,16 +55,23 @@ public class WAVSampleProcessor implements SampleProcessor { return framesRead; } - public AudioInputStream getAudioInputStream() { - return audioInputStream; - } - - public void setMergeChannels(boolean mergeChannels) { - this.mergeChannels = mergeChannels; - } - - public boolean isMergeChannels() { - return mergeChannels; + @Override + public void setVolume(float volume) { + this.volume = volume; } + @Override + public float getVolume() { + return volume; + } + + @Override + public void setPan(float pan) { + this.pan = pan; + } + + @Override + public float getPan() { + return pan; + } } \ No newline at end of file