switched back to old system dependent audio method; updated libgdx
framework; added ashley
This commit is contained in:
parent
4321a07f66
commit
bf94851e06
32
build.gradle
32
build.gradle
@ -42,10 +42,10 @@ project(":desktop") {
|
|||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":core")
|
compile project(":core")
|
||||||
implementation "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
|
compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
|
||||||
implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||||
implementation "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,15 +56,15 @@ project(":android") {
|
|||||||
configurations { natives }
|
configurations { natives }
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":core")
|
compile project(":core")
|
||||||
implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
|
compile "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
|
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
|
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
|
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
|
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
|
||||||
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
|
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
|
||||||
implementation "com.badlogicgames.ashley:ashley:$ashleyVersion"
|
compile "com.badlogicgames.ashley:ashley:$ashleyVersion"
|
||||||
implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
||||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi"
|
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi"
|
||||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
|
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
|
||||||
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
|
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
|
||||||
@ -79,16 +79,12 @@ project(":core") {
|
|||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "com.badlogicgames.gdx:gdx:$gdxVersion"
|
compile "com.badlogicgames.gdx:gdx:$gdxVersion"
|
||||||
implementation "com.badlogicgames.ashley:ashley:$ashleyVersion"
|
compile "com.badlogicgames.ashley:ashley:$ashleyVersion"
|
||||||
implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
|
||||||
|
compile "com.github.rwl:jtransforms:2.4.0"
|
||||||
implementation "org.apache.commons:commons-math3:3.2"
|
compile "org.apache.commons:commons-math3:3.2"
|
||||||
implementation "com.github.rwl:jtransforms:2.4.0"
|
compile group: 'org.apache.tika', name: 'tika-parsers', version: '1.18'
|
||||||
implementation group: 'fr.delthas', name: 'javamp3', version: '1.0.1'
|
|
||||||
implementation "org:jaudiotagger:2.0.3"
|
|
||||||
implementation group: 'org.apache.tika', name: 'tika-parsers', version: '1.18'
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio;
|
|
||||||
|
|
||||||
import java.security.InvalidParameterException;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.FloatArray;
|
|
||||||
|
|
||||||
public class AudioDataPackage {
|
|
||||||
private FloatArray bassPeaks;
|
|
||||||
private FloatArray mPeaks;
|
|
||||||
private FloatArray umPeaks;
|
|
||||||
|
|
||||||
private MusicManager musicInfo;
|
|
||||||
|
|
||||||
private float bassMaxVal, bassAvg;
|
|
||||||
private float mMaxVal, mAvg;
|
|
||||||
private float uMMaxval, uMAvg;
|
|
||||||
|
|
||||||
private int PUID;
|
|
||||||
private float secPerWin;
|
|
||||||
private float avgSPB;
|
|
||||||
/**
|
|
||||||
* Sets the bass data
|
|
||||||
* @param bassPeaks
|
|
||||||
* @param bassMaxVal
|
|
||||||
* @param bassAvg
|
|
||||||
*/
|
|
||||||
public void setBassData(FloatArray bassPeaks, float bassMaxVal, float bassAvg) {
|
|
||||||
if (this.bassPeaks != null) {
|
|
||||||
throw new InvalidParameterException("The bass peaks of this audio pack has already been set.");
|
|
||||||
}
|
|
||||||
this.bassPeaks = bassPeaks;
|
|
||||||
this.bassMaxVal = bassMaxVal;
|
|
||||||
this.bassAvg = bassAvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the midrange data
|
|
||||||
* @param mPeaks
|
|
||||||
* @param mMaxVal
|
|
||||||
* @param mAvg
|
|
||||||
*/
|
|
||||||
public void setmData(FloatArray mPeaks, float mMaxVal, float mAvg) {
|
|
||||||
if (this.mPeaks != null) {
|
|
||||||
throw new InvalidParameterException("The midrange peaks of this audio pack has already been set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mPeaks = mPeaks;
|
|
||||||
this.mMaxVal = mMaxVal;
|
|
||||||
this.mAvg = mAvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the upper-midrange data
|
|
||||||
* @param umPeaks
|
|
||||||
* @param uMMaxVal
|
|
||||||
* @param uMAvg
|
|
||||||
*/
|
|
||||||
public void setUmData(FloatArray umPeaks, float uMMaxVal, float uMAvg) {
|
|
||||||
if (this.umPeaks != null) {
|
|
||||||
throw new InvalidParameterException("The upper midrange peaks have already been set.");
|
|
||||||
}
|
|
||||||
this.umPeaks = umPeaks;
|
|
||||||
this.uMMaxval = uMMaxVal;
|
|
||||||
this.uMAvg = uMAvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMusicInfo(MusicManager musicInfo) {
|
|
||||||
if (this.musicInfo != null) {
|
|
||||||
throw new InvalidParameterException("There is already music information in this package.");
|
|
||||||
}
|
|
||||||
this.musicInfo = musicInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPUID(int pUID) {
|
|
||||||
PUID = pUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAvgSPB(float avgSPB) {
|
|
||||||
this.avgSPB = avgSPB;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSecPerWin(float secPerWin) {
|
|
||||||
this.secPerWin = secPerWin;
|
|
||||||
}
|
|
||||||
|
|
||||||
//All the get methods...
|
|
||||||
public FloatArray getBassPeaks() {
|
|
||||||
if (bassPeaks != null) {
|
|
||||||
return bassPeaks;
|
|
||||||
} else {
|
|
||||||
throw new NullPointerException("Bass peaks currently null. This pack hasn't been properly packed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FloatArray getmPeaks() {
|
|
||||||
if (mPeaks != null) {
|
|
||||||
return mPeaks;
|
|
||||||
} else {
|
|
||||||
throw new NullPointerException("midrange peaks currently null. This pack hasn't been properly packed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FloatArray getuMPeaks() {
|
|
||||||
if (umPeaks != null) {
|
|
||||||
return umPeaks;
|
|
||||||
} else {
|
|
||||||
throw new NullPointerException("Upper midrange peaks currently null. This pack hasn't been properly packed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MusicManager getMusicInfo() {
|
|
||||||
if (musicInfo == null) {
|
|
||||||
throw new NullPointerException("Music info hasn't been baked in...");
|
|
||||||
}
|
|
||||||
return musicInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getBassMaxVal() {
|
|
||||||
return bassMaxVal;
|
|
||||||
}
|
|
||||||
public float getBassAvg() {
|
|
||||||
return bassAvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getmAvg() {
|
|
||||||
return mAvg;
|
|
||||||
}
|
|
||||||
public float getmMaxVal() {
|
|
||||||
return mMaxVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getuMMaxval() {
|
|
||||||
return uMMaxval;
|
|
||||||
}
|
|
||||||
public float getuMAvg() {
|
|
||||||
return uMAvg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPUID() {
|
|
||||||
return PUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAvgSPB() {
|
|
||||||
return avgSPB;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getSecPerWin() {
|
|
||||||
return secPerWin;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.jaudiotagger.audio.AudioFile;
|
|
||||||
import org.jaudiotagger.audio.AudioFileIO;
|
|
||||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
|
||||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
|
||||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
|
||||||
import org.jaudiotagger.audio.mp3.MP3File;
|
|
||||||
import org.jaudiotagger.audio.wav.WavTag;
|
|
||||||
import org.jaudiotagger.tag.FieldKey;
|
|
||||||
import org.jaudiotagger.tag.TagException;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
|
||||||
import com.badlogic.gdx.Preferences;
|
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
|
||||||
import com.badlogic.gdx.graphics.Pixmap;
|
|
||||||
import com.badlogic.gdx.graphics.PixmapIO;
|
|
||||||
import com.badlogic.gdx.graphics.Texture;
|
|
||||||
import com.badlogic.gdx.utils.Disposable;
|
|
||||||
|
|
||||||
public class MusicInfo implements Disposable {
|
|
||||||
|
|
||||||
private long durationInSeconds;
|
|
||||||
private String songName;
|
|
||||||
private String author;
|
|
||||||
private int previousTop;
|
|
||||||
private int ratedDifficulty;
|
|
||||||
private boolean invalidMusic;
|
|
||||||
|
|
||||||
private boolean containsInfo;
|
|
||||||
|
|
||||||
private FileHandle musicFile;
|
|
||||||
private Preferences musicAnnotation;
|
|
||||||
|
|
||||||
public MusicInfo(FileHandle musicFile, Preferences musicAnnotation) {
|
|
||||||
this.musicFile = musicFile;
|
|
||||||
this.musicAnnotation = musicAnnotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* loads the information for this song.
|
|
||||||
* should be called in non-render thread as is blocking and depends on IO speed.
|
|
||||||
*/
|
|
||||||
public void loadInfo() {
|
|
||||||
if (musicFile.extension().toLowerCase().equals("mp3")) {
|
|
||||||
MP3File mp3File;
|
|
||||||
try {
|
|
||||||
mp3File = new MP3File(musicFile.file());
|
|
||||||
durationInSeconds = mp3File.getAudioHeader().getTrackLength();
|
|
||||||
|
|
||||||
if (mp3File.getTag() != null && mp3File.getTag().getFirstArtwork() != null) {
|
|
||||||
if (!Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png").exists()) {
|
|
||||||
byte[] albumWorkBytes = mp3File.getTag().getFirstArtwork().getBinaryData();
|
|
||||||
Pixmap pixmap = new Pixmap(albumWorkBytes, 0, albumWorkBytes.length);
|
|
||||||
PixmapIO.writePNG(Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png"), pixmap);
|
|
||||||
pixmap.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
songName = mp3File.getTag().getFirst(FieldKey.TITLE);
|
|
||||||
author = mp3File.getTag().getFirst(FieldKey.ARTIST);
|
|
||||||
} catch (IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
AudioFile audioFile = AudioFileIO.read(musicFile.file());
|
|
||||||
WavTag wavTag = (WavTag) AudioFileIO.read(musicFile.file()).getTag();
|
|
||||||
songName = wavTag.getFirst(FieldKey.TITLE);
|
|
||||||
author = wavTag.getFirst(FieldKey.ARTIST);
|
|
||||||
durationInSeconds = audioFile.getAudioHeader().getTrackLength();
|
|
||||||
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException
|
|
||||||
| InvalidAudioFrameException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (durationInSeconds > 60 * 5) {
|
|
||||||
invalidMusic = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (songName == null || songName.isEmpty()) {
|
|
||||||
songName = musicFile.nameWithoutExtension();
|
|
||||||
}
|
|
||||||
|
|
||||||
previousTop = musicAnnotation.getInteger(songName + ":previous top", -1);
|
|
||||||
ratedDifficulty = musicAnnotation.getInteger(songName + ":difficulty", -1);
|
|
||||||
|
|
||||||
if (author == null || author.isEmpty()) {
|
|
||||||
author = "N/A";
|
|
||||||
}
|
|
||||||
|
|
||||||
containsInfo = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The album art as a texture.
|
|
||||||
* Can return null if contains info is false.
|
|
||||||
* Not automatically disposed.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Texture loadTexture() {
|
|
||||||
Gdx.app.debug("MusicInfo", "Texture loading for music: " + getMusicName());
|
|
||||||
if (Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png").exists()) {
|
|
||||||
return new Texture(Gdx.files.external("RhythmBullet/AlbumArt/" + musicFile.name() + ".png"));
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDurationInSeconds() {
|
|
||||||
return durationInSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMusicName() {
|
|
||||||
return songName.replace('_', ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAuthor() {
|
|
||||||
return author;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPreviousTop() {
|
|
||||||
return previousTop == -1 ? 0 : previousTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRatedDifficulty() {
|
|
||||||
return ratedDifficulty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInvalidMusic() {
|
|
||||||
return invalidMusic;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asks if this contains information.
|
|
||||||
* @return whether this contains data
|
|
||||||
*/
|
|
||||||
public boolean hasInformation() {
|
|
||||||
return containsInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileHandle getMusicFile() {
|
|
||||||
return musicFile;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
|
|
||||||
package zero1hd.rhythmbullet.audio;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.audio.Music;
|
|
||||||
import com.badlogic.gdx.audio.Music.OnCompletionListener;
|
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
|
||||||
import com.badlogic.gdx.utils.Disposable;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public interface MusicManager extends Disposable {
|
|
||||||
/**
|
|
||||||
* sets a integer variable to the current window of audio data the playback is at.
|
|
||||||
* Useful for efficiency because we compute once for that frame then get the values every time it is required instead of calculating every time we get the index.
|
|
||||||
* @return the playback window index.
|
|
||||||
*/
|
|
||||||
public int playbackIndexUpdate();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current position in seconds
|
|
||||||
* @return the current frame of audio.
|
|
||||||
*/
|
|
||||||
public int getPlaybackIndexPosition();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the read window size.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public int getReadWindowSize();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* read in samples and fills the array.
|
|
||||||
* @param samples the array that should contain said samples
|
|
||||||
* @return amount read. Will return 0 if end of stream.
|
|
||||||
*/
|
|
||||||
public int readSampleFrames(float[] samples);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns sample count.
|
|
||||||
* Can be inaccurate.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public long getSampleCount();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns duration of song in seconds
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public float getDuration();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns sample rate
|
|
||||||
* @return sample rate as float
|
|
||||||
*/
|
|
||||||
public float getSampleRate();
|
|
||||||
|
|
||||||
public void pause();
|
|
||||||
|
|
||||||
public void play();
|
|
||||||
|
|
||||||
public boolean isPlaying();
|
|
||||||
|
|
||||||
public float getPositionInSeconds();
|
|
||||||
|
|
||||||
public void setPosition(float position);
|
|
||||||
|
|
||||||
public void setOnCompletionListener(OnCompletionListener listener);
|
|
||||||
|
|
||||||
public void setVolume(float percent);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the amount of channels this audio file has.
|
|
||||||
*/
|
|
||||||
public int getChannelCount();
|
|
||||||
/**
|
|
||||||
* If the the properties of the song are done loading
|
|
||||||
* @return whether its done loading
|
|
||||||
*/
|
|
||||||
public boolean isFinishedLoading();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic song name is the name of the file of the song, meaning we do not check tags for proper song name.
|
|
||||||
* Thats left up to the song info object.
|
|
||||||
* @return basic song name
|
|
||||||
*/
|
|
||||||
public String getBasicSongName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the song filehandle.
|
|
||||||
*/
|
|
||||||
public FileHandle getMusicFile();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the amount of windows that have been read.
|
|
||||||
*/
|
|
||||||
public int framesRead();
|
|
||||||
|
|
||||||
public Music getMusic();
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
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.PCMProcessor;
|
|
||||||
|
|
||||||
public class RhythmBulletAudioThread extends Thread {
|
|
||||||
private AudioDevice ad;
|
|
||||||
private PCMProcessor sp;
|
|
||||||
private Music music;
|
|
||||||
private volatile OnCompletionListener ocl;
|
|
||||||
private short[] pcm;
|
|
||||||
private volatile boolean terminated;
|
|
||||||
private volatile boolean playing;
|
|
||||||
private volatile long framesPlayed;
|
|
||||||
private volatile float framesToSkip;
|
|
||||||
|
|
||||||
public RhythmBulletAudioThread(int windowSize, VisualizableMusic vm) {
|
|
||||||
music = vm;
|
|
||||||
this.sp = vm.getSampleProcessor();
|
|
||||||
pcm = new short[(sp.isStereo() ? 2 : 1)*windowSize];
|
|
||||||
setName("RhythmBullet-AudioPlayback-Thread");
|
|
||||||
play();
|
|
||||||
start();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
sp.initiate();
|
|
||||||
this.ad = Gdx.audio.newAudioDevice(sp.getSampleRate(), !sp.isStereo());
|
|
||||||
|
|
||||||
while (!terminated && sp.readSamples(pcm, this) > 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void jumpToFrame(long frame) {
|
|
||||||
if (framesPlayed > frame) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.audio.Music;
|
|
||||||
|
|
||||||
import zero1hd.rhythmbullet.audio.processor.MP3PCMProcessor;
|
|
||||||
import zero1hd.rhythmbullet.audio.processor.PCMProcessor;
|
|
||||||
import zero1hd.rhythmbullet.audio.processor.WAVPCMProcessor;
|
|
||||||
|
|
||||||
public class VisualizableMusic implements Music {
|
|
||||||
private int windowSize = 1024;
|
|
||||||
private OnCompletionListener ocl;
|
|
||||||
private boolean looping;
|
|
||||||
private RhythmBulletAudioThread rat;
|
|
||||||
private String fileLoc;
|
|
||||||
private PCMProcessor sampleProcessor;
|
|
||||||
|
|
||||||
public VisualizableMusic(String fileLoc) {
|
|
||||||
this.fileLoc = fileLoc;
|
|
||||||
if (fileLoc.toLowerCase().endsWith("wav")) {
|
|
||||||
try {
|
|
||||||
sampleProcessor = new WAVPCMProcessor(Paths.get(fileLoc), windowSize);
|
|
||||||
} catch (IOException | UnsupportedAudioFileException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else if (fileLoc.toLowerCase().endsWith("mp3")) {
|
|
||||||
sampleProcessor = new MP3PCMProcessor(Paths.get(fileLoc), windowSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void play() {
|
|
||||||
if (rat == null) {
|
|
||||||
rat = new RhythmBulletAudioThread(windowSize, this);
|
|
||||||
rat.setOnCompletionListener(ocl);
|
|
||||||
rat.start();
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
if (rat != null) {
|
|
||||||
rat.setOnCompletionListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PCMProcessor getSampleProcessor() {
|
|
||||||
return sampleProcessor;
|
|
||||||
}
|
|
||||||
}
|
|
4
core/src/zero1hd/rhythmbullet/audio/processor/AudioProcessor.java
Executable file
4
core/src/zero1hd/rhythmbullet/audio/processor/AudioProcessor.java
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
package zero1hd.rhythmbullet.audio.processor;
|
||||||
|
|
||||||
|
public interface AudioProcessor {
|
||||||
|
}
|
@ -1,107 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio.processor;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import fr.delthas.javamp3.Sound;
|
|
||||||
|
|
||||||
public class MP3PCMProcessor implements PCMProcessor {
|
|
||||||
private boolean stereo;
|
|
||||||
private int sampleRate;
|
|
||||||
private volatile float volume, pan;
|
|
||||||
private boolean initiated;
|
|
||||||
private Sound audioInputStream;
|
|
||||||
private Path path;
|
|
||||||
private byte[] buffer;
|
|
||||||
|
|
||||||
public MP3PCMProcessor(Path path, int windowSize) {
|
|
||||||
try(Sound sound = new Sound(new BufferedInputStream(Files.newInputStream(path)))) {
|
|
||||||
sampleRate = sound.getSamplingFrequency();
|
|
||||||
stereo = sound.isStereo();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initiate() {
|
|
||||||
try {
|
|
||||||
audioInputStream = new Sound(new BufferedInputStream(Files.newInputStream(path)));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
buffer = new byte[audioInputStream.getAudioFormat().getFrameSize()];
|
|
||||||
initiated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isStereo() {
|
|
||||||
return stereo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSampleRate() {
|
|
||||||
return sampleRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readSamples(short[] pcm, Object syncObj) {
|
|
||||||
if (initiated) {
|
|
||||||
synchronized (syncObj) {
|
|
||||||
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 (stereo) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Stream has not been initialized.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
try {
|
|
||||||
audioInputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio.processor;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.utils.Disposable;
|
|
||||||
|
|
||||||
public interface PCMProcessor 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
|
|
||||||
*/
|
|
||||||
public boolean isStereo();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return sample rate
|
|
||||||
*/
|
|
||||||
public int getSampleRate();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>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
|
|
||||||
* @param syncObj the object that this object should use to synchronize multiple threads.
|
|
||||||
* @return the amount of samples read.
|
|
||||||
*/
|
|
||||||
public int readSamples(short[] pcm, Object syncObj);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>Thread safe</b>
|
|
||||||
* ranges from 0 to 1.
|
|
||||||
* @param volume the volume to set
|
|
||||||
*/
|
|
||||||
public void setVolume(float volume);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>Thread safe</b>
|
|
||||||
* @return the volume ranging from 0 to 1
|
|
||||||
*/
|
|
||||||
public float getVolume();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>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>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();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* resets the current input stream to the beginning.
|
|
||||||
* Not thread safe and should be called in the same thread as the one doing the playback.
|
|
||||||
*/
|
|
||||||
public void resetStream();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skip this amount of frames.
|
|
||||||
* Not thread safe since this should be called in the same thread as the one doing the playback.
|
|
||||||
* @param framesToSkip
|
|
||||||
*/
|
|
||||||
public void skipFrames(long framesToSkip);
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
package zero1hd.rhythmbullet.audio.processor;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
import javax.sound.sampled.AudioFormat;
|
|
||||||
import javax.sound.sampled.AudioInputStream;
|
|
||||||
import javax.sound.sampled.AudioSystem;
|
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
|
||||||
|
|
||||||
public class WAVPCMProcessor implements PCMProcessor {
|
|
||||||
private boolean stereo;
|
|
||||||
private int sampleRate;
|
|
||||||
private byte[] buffer;
|
|
||||||
private Path path;
|
|
||||||
private AudioInputStream audioInputStream;
|
|
||||||
private volatile float volume, pan;
|
|
||||||
private boolean initiated;
|
|
||||||
|
|
||||||
public WAVPCMProcessor(Path path, int windowSize) throws IOException, UnsupportedAudioFileException {
|
|
||||||
this.path = path;
|
|
||||||
AudioFormat format = AudioSystem.getAudioFileFormat(path.toFile()).getFormat();
|
|
||||||
stereo = format.getChannels() > 1 ? true : false;
|
|
||||||
sampleRate = (int) format.getSampleRate();
|
|
||||||
volume = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initiate() {
|
|
||||||
try {
|
|
||||||
audioInputStream = AudioSystem.getAudioInputStream(path.toFile());
|
|
||||||
} catch (UnsupportedAudioFileException | IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
buffer = new byte[audioInputStream.getFormat().getFrameSize()];
|
|
||||||
initiated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isStereo() {
|
|
||||||
return stereo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSampleRate() {
|
|
||||||
return sampleRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readSamples(short[] pcm, Object syncObj) {
|
|
||||||
if (initiated) {
|
|
||||||
synchronized (syncObj) {
|
|
||||||
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 (stereo) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Stream has not been initialized.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
try {
|
|
||||||
audioInputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,8 +7,6 @@ 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;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import org.apache.tools.ant.taskdefs.condition.Os
|
|
||||||
|
|
||||||
apply plugin: "java"
|
apply plugin: "java"
|
||||||
|
|
||||||
sourceCompatibility = 1.6
|
sourceCompatibility = 1.6
|
||||||
@ -14,9 +12,6 @@ task run(dependsOn: classes, type: JavaExec) {
|
|||||||
standardInput = System.in
|
standardInput = System.in
|
||||||
workingDir = project.assetsDir
|
workingDir = project.assetsDir
|
||||||
ignoreExitValue = true
|
ignoreExitValue = true
|
||||||
|
|
||||||
if(Os.isFamily(Os.FAMILY_MAC))
|
|
||||||
jvmArgs += "-XstartOnFirstThread"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task debug(dependsOn: classes, type: JavaExec) {
|
task debug(dependsOn: classes, type: JavaExec) {
|
||||||
|
Loading…
Reference in New Issue
Block a user