custom audio system tailored for music based game now has the WAV sample
reader with pan and volume controls.
This commit is contained in:
parent
45e3b55d84
commit
31b2278e9b
@ -75,7 +75,7 @@ project(":core") {
|
|||||||
compile "org.apache.commons:commons-math3:3.2"
|
compile "org.apache.commons:commons-math3:3.2"
|
||||||
|
|
||||||
compile "com.github.rwl:jtransforms:2.4.0"
|
compile "com.github.rwl:jtransforms:2.4.0"
|
||||||
|
compile group: 'fr.delthas', name: 'javamp3', version: '1.0.1'
|
||||||
compile "org:jaudiotagger:2.0.3"
|
compile "org:jaudiotagger:2.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import com.badlogic.gdx.audio.Music.OnCompletionListener;
|
|||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.utils.Disposable;
|
import com.badlogic.gdx.utils.Disposable;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public interface MusicManager extends Disposable {
|
public interface MusicManager extends Disposable {
|
||||||
/**
|
/**
|
||||||
* sets a integer variable to the current window of audio data the playback is at.
|
* sets a integer variable to the current window of audio data the playback is at.
|
||||||
|
29
core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudio.java
Executable file
29
core/src/zero1hd/rhythmbullet/audio/RhythmBulletAudio.java
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
24
core/src/zero1hd/rhythmbullet/audio/processor/MP3SampleProcessor.java
Executable file
24
core/src/zero1hd/rhythmbullet/audio/processor/MP3SampleProcessor.java
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
package zero1hd.rhythmbullet.audio.processor;
|
||||||
|
|
||||||
|
public class MP3SampleProcessor implements SampleProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getChannels() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSampleRate() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readSamples(short[] pcm) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
7
core/src/zero1hd/rhythmbullet/audio/processor/SampleProcessor.java
Executable file
7
core/src/zero1hd/rhythmbullet/audio/processor/SampleProcessor.java
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
package zero1hd.rhythmbullet.audio.processor;
|
||||||
|
|
||||||
|
public interface SampleProcessor {
|
||||||
|
public int getChannels();
|
||||||
|
public int getSampleRate();
|
||||||
|
public int readSamples(short[] pcm);
|
||||||
|
}
|
144
core/src/zero1hd/rhythmbullet/audio/processor/VisualizableMusic.java
Executable file
144
core/src/zero1hd/rhythmbullet/audio/processor/VisualizableMusic.java
Executable file
@ -0,0 +1,144 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
70
core/src/zero1hd/rhythmbullet/audio/processor/WAVSampleProcessor.java
Executable file
70
core/src/zero1hd/rhythmbullet/audio/processor/WAVSampleProcessor.java
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
package zero1hd.rhythmbullet.audio.processor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioInputStream;
|
||||||
|
import javax.sound.sampled.AudioSystem;
|
||||||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
|
||||||
|
public class WAVSampleProcessor implements SampleProcessor {
|
||||||
|
private int channels;
|
||||||
|
private int sampleRate;
|
||||||
|
private byte[] buffer;
|
||||||
|
private AudioInputStream audioInputStream;
|
||||||
|
private boolean mergeChannels;
|
||||||
|
private float volume, pan;
|
||||||
|
public WAVSampleProcessor(File musicFile, int windowSize) throws IOException, UnsupportedAudioFileException {
|
||||||
|
audioInputStream = AudioSystem.getAudioInputStream(musicFile);
|
||||||
|
buffer = new byte[audioInputStream.getFormat().getFrameSize()];
|
||||||
|
|
||||||
|
channels = audioInputStream.getFormat().getChannels();
|
||||||
|
sampleRate = (int) audioInputStream.getFormat().getSampleRate();
|
||||||
|
volume = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChannels() {
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSampleRate() {
|
||||||
|
return sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readSamples(short[] pcm) {
|
||||||
|
int framesRead = 0;
|
||||||
|
for (int sampleID = 0; sampleID < pcm.length; sampleID++) {
|
||||||
|
try {
|
||||||
|
if (audioInputStream.read(buffer) > 0) {
|
||||||
|
pcm[sampleID] = (short) ((buffer[1] << 8) + (buffer[0] & 0x00ff));
|
||||||
|
pcm[sampleID] *= volume * (Math.min(1-pan, 1));
|
||||||
|
if (audioInputStream.getFormat().getChannels() > 1) {
|
||||||
|
short secondChan = (short) ((buffer[3] << 8) + (buffer[2] & 0x00ff));
|
||||||
|
secondChan *= volume * (Math.min(1+pan, 1));
|
||||||
|
sampleID++;
|
||||||
|
pcm[sampleID] = secondChan;
|
||||||
|
}
|
||||||
|
framesRead++;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return framesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AudioInputStream getAudioInputStream() {
|
||||||
|
return audioInputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMergeChannels(boolean mergeChannels) {
|
||||||
|
this.mergeChannels = mergeChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMergeChannels() {
|
||||||
|
return mergeChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,6 +7,8 @@ import com.badlogic.gdx.utils.Disposable;
|
|||||||
import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
|
import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
|
||||||
import zero1hd.rhythmbullet.audio.MusicManager;
|
import zero1hd.rhythmbullet.audio.MusicManager;
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class MusicManagerFFT implements Disposable {
|
public class MusicManagerFFT implements Disposable {
|
||||||
protected MusicManager mm;
|
protected MusicManager mm;
|
||||||
private FloatFFT_1D fft;
|
private FloatFFT_1D fft;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user