Finished basic visualizer and mass refactoring on the audio analyzer to
be compatible with the structure
This commit is contained in:
		@@ -8,25 +8,31 @@ import org.jaudiotagger.audio.AudioHeader;
 | 
				
			|||||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
 | 
					import org.jaudiotagger.audio.exceptions.CannotReadException;
 | 
				
			||||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
 | 
					import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
 | 
				
			||||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
 | 
					import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
 | 
				
			||||||
 | 
					import org.jaudiotagger.audio.mp3.MP3AudioHeader;
 | 
				
			||||||
 | 
					import org.jaudiotagger.audio.mp3.MP3File;
 | 
				
			||||||
import org.jaudiotagger.tag.TagException;
 | 
					import org.jaudiotagger.tag.TagException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.badlogic.gdx.files.FileHandle;
 | 
					import com.badlogic.gdx.files.FileHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class MinimalAudioHeader {
 | 
					public class MinimalAudioHeader {
 | 
				
			||||||
	private int sampleRate, channelCount;
 | 
						private int sampleRate, channelCount;
 | 
				
			||||||
	
 | 
						private SupportedFormats format;
 | 
				
			||||||
 | 
						private FileHandle musicFile;
 | 
				
			||||||
	public MinimalAudioHeader(FileHandle musicFile) {
 | 
						public MinimalAudioHeader(FileHandle musicFile) {
 | 
				
			||||||
 | 
							this.musicFile = musicFile;
 | 
				
			||||||
 | 
							format = SupportedFormats.valueOf(musicFile.extension().toUpperCase());
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			AudioFile file = AudioFileIO.read(musicFile.file());
 | 
								AudioFile file = AudioFileIO.read(musicFile.file());
 | 
				
			||||||
			AudioHeader header = file.getAudioHeader();
 | 
								AudioHeader header = file.getAudioHeader();
 | 
				
			||||||
			sampleRate = header.getSampleRateAsNumber();
 | 
								sampleRate = header.getSampleRateAsNumber();
 | 
				
			||||||
			channelCount = (header.getChannels().equals("Mono") ? 1 : 2);
 | 
								channelCount = (header.getChannels().equals("Mono") ? 1 : 2);
 | 
				
			||||||
		} catch (CannotReadException | IOException | TagException | ReadOnlyFileException
 | 
							} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
 | 
				
			||||||
				| InvalidAudioFrameException e) {
 | 
					 | 
				
			||||||
			e.printStackTrace();
 | 
								e.printStackTrace();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	public int getSampleRate() {
 | 
						public int getSampleRate() {
 | 
				
			||||||
		return sampleRate;
 | 
							return sampleRate;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -34,4 +40,21 @@ public class MinimalAudioHeader {
 | 
				
			|||||||
	public int getChannelCount() {
 | 
						public int getChannelCount() {
 | 
				
			||||||
		return channelCount;
 | 
							return channelCount;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public long estimateSampleFrames() {
 | 
				
			||||||
 | 
							switch (format) {
 | 
				
			||||||
 | 
							case MP3:
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									MP3File file = (MP3File) AudioFileIO.read(musicFile.file());
 | 
				
			||||||
 | 
									MP3AudioHeader header = file.getMP3AudioHeader();
 | 
				
			||||||
 | 
									return header.getNumberOfFrames();
 | 
				
			||||||
 | 
								} catch (CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
 | 
				
			||||||
 | 
									e.printStackTrace();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,7 +177,7 @@ public class MusicController extends Observable implements OnCompletionListener,
 | 
				
			|||||||
		if (musicHeader != null) {
 | 
							if (musicHeader != null) {
 | 
				
			||||||
			return musicHeader;
 | 
								return musicHeader;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return musicList.newMinimalAudioHeader(getCurrentMusicFileHandle());
 | 
								return musicHeader = musicList.newMinimalAudioHeader(getCurrentMusicFileHandle());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,13 +41,16 @@ public class MusicList extends Observable {
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @param file
 | 
						 * @param file
 | 
				
			||||||
	 * @return a {@link #zero1hd.rhythmbullet.audio.processor.AudioProcessor()} of the given music file. Will return null if theres a format error.
 | 
						 * @return a {@link AudioProcessor} of the given music file. Will return null if theres a format error.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public AudioProcessor newAudioProcessor(FileHandle file) {
 | 
						public AudioProcessor newAudioProcessor(FileHandle file) {
 | 
				
			||||||
		if (file.extension().equalsIgnoreCase("wav")) {
 | 
							switch (SupportedFormats.valueOf(file.extension().toUpperCase())) {
 | 
				
			||||||
			return new WAVAudioProcessor(file);
 | 
							case MP3:
 | 
				
			||||||
		} else if (file.extension().equalsIgnoreCase("mp3")) {
 | 
					 | 
				
			||||||
			return audioProcFactory.newMP3AudioProcessor(file);
 | 
								return audioProcFactory.newMP3AudioProcessor(file);
 | 
				
			||||||
 | 
							case WAV:
 | 
				
			||||||
 | 
								return new WAVAudioProcessor(file);
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -124,8 +127,11 @@ public class MusicList extends Observable {
 | 
				
			|||||||
				if (files[i].isDirectory()) {
 | 
									if (files[i].isDirectory()) {
 | 
				
			||||||
					musicFiles.addAll(recursiveMusicSearch(files[i]));
 | 
										musicFiles.addAll(recursiveMusicSearch(files[i]));
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					if (files[i].extension().equalsIgnoreCase("wav") || files[i].extension().equalsIgnoreCase("mp3")) {
 | 
										try {
 | 
				
			||||||
 | 
											SupportedFormats.valueOf(files[i].extension().toUpperCase());
 | 
				
			||||||
						musicFiles.add(files[i]);
 | 
											musicFiles.add(files[i]);
 | 
				
			||||||
 | 
										} catch (IllegalArgumentException e) {
 | 
				
			||||||
 | 
											Gdx.app.log("MusicList", "Unsupported file format: " + files[i].name());
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,10 +77,16 @@ public class MusicMetadataController implements Disposable, Observer {
 | 
				
			|||||||
			for (int i = 0; i < musicList.getTotal(); i++) {
 | 
								for (int i = 0; i < musicList.getTotal(); i++) {
 | 
				
			||||||
				FileHandle musicFile = musicList.getMusicArray().get(i);
 | 
									FileHandle musicFile = musicList.getMusicArray().get(i);
 | 
				
			||||||
				synchronized (this) {
 | 
									synchronized (this) {
 | 
				
			||||||
					if (musicFile.extension().equalsIgnoreCase("wav")) {
 | 
										switch (SupportedFormats.valueOf(musicFile.extension().toUpperCase())) {
 | 
				
			||||||
						metadataArray.add(new WAVMetadata(musicFile));
 | 
										case MP3:
 | 
				
			||||||
					} else if (musicFile.extension().equalsIgnoreCase("mp3")) {
 | 
					 | 
				
			||||||
						metadataArray.add(new MP3Metadata(musicFile));
 | 
											metadataArray.add(new MP3Metadata(musicFile));
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
										case WAV:
 | 
				
			||||||
 | 
											metadataArray.add(new WAVMetadata(musicFile));
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
										default:
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
										
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								core/src/zero1hd/rhythmbullet/audio/SupportedFormats.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								core/src/zero1hd/rhythmbullet/audio/SupportedFormats.java
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					package zero1hd.rhythmbullet.audio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum SupportedFormats {
 | 
				
			||||||
 | 
						WAV, MP3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,53 +1,296 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.analyzer;
 | 
					package zero1hd.rhythmbullet.audio.analyzer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.concurrent.ExecutorService;
 | 
					import com.badlogic.gdx.Gdx;
 | 
				
			||||||
import java.util.concurrent.Executors;
 | 
					import com.badlogic.gdx.math.MathUtils;
 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.utils.Disposable;
 | 
					import com.badlogic.gdx.utils.Disposable;
 | 
				
			||||||
 | 
					import com.badlogic.gdx.utils.FloatArray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicManager;
 | 
					import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
 | 
				
			||||||
 | 
					import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class AudioAnalyzer implements Disposable {
 | 
					public class AudioAnalyzer implements Runnable, Disposable {
 | 
				
			||||||
	private ExecutorService exec;
 | 
						private Thread thread;
 | 
				
			||||||
 | 
						private String threadName = "Audio-Analyzer";
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	private SpectralFluxAnalysisRunnable sfar;
 | 
						private volatile boolean work = true;
 | 
				
			||||||
	private ThresholdCalcRunnable tcr;
 | 
						private int windowSize = 1024;
 | 
				
			||||||
	private PruneFluxRunnable pfr;
 | 
					 | 
				
			||||||
	private PeakDetectionRunnable pdr;
 | 
					 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public AudioAnalyzer(MusicManager musicManager) {
 | 
						private FloatArray bassSpectralFlux = new FloatArray();
 | 
				
			||||||
		exec = Executors.newSingleThreadExecutor();
 | 
						private FloatArray mSpectralFlux = new FloatArray();
 | 
				
			||||||
		sfar = new SpectralFluxAnalysisRunnable(musicManager);
 | 
						private FloatArray umSpectralFlux = new FloatArray();
 | 
				
			||||||
		tcr = new ThresholdCalcRunnable(sfar);
 | 
					 | 
				
			||||||
		pfr = new PruneFluxRunnable(tcr);
 | 
					 | 
				
			||||||
		pdr = new PeakDetectionRunnable(pfr, sfar.getPUID());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private FloatArray bassThreshold = new FloatArray();
 | 
				
			||||||
 | 
						private FloatArray mThreshold = new FloatArray();
 | 
				
			||||||
 | 
						private FloatArray umThreshold = new FloatArray();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private FloatArray bassPrunned = new FloatArray();
 | 
				
			||||||
 | 
						private FloatArray mPrunned = new FloatArray();
 | 
				
			||||||
 | 
						private FloatArray umPrunned = new FloatArray();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private FloatArray bassPeaks = new FloatArray();
 | 
				
			||||||
 | 
						private FloatArray mPeaks = new FloatArray();
 | 
				
			||||||
 | 
						private FloatArray umPeaks = new FloatArray();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private float bassMaxValue, mMaxValue, umMaxValue, secondsPerWindow, mAvg, bassAvg, umAvg;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						AudioProcessor processor;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private int PUID;
 | 
				
			||||||
 | 
						private int progress;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public AudioAnalyzer(AudioProcessor audioProcessor) {
 | 
				
			||||||
 | 
							this.processor = audioProcessor;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public void start() {
 | 
						public void start() {
 | 
				
			||||||
		exec.submit(sfar);
 | 
							if (thread == null || !thread.isAlive()) {
 | 
				
			||||||
		exec.submit(tcr);
 | 
								work = true;
 | 
				
			||||||
		exec.submit(pfr);
 | 
								thread = new Thread(this, threadName);
 | 
				
			||||||
		exec.submit(pdr);
 | 
								thread.start();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	private void stop() {
 | 
						@Override
 | 
				
			||||||
		sfar.work = false;
 | 
						public void run() {
 | 
				
			||||||
		tcr.work = false;
 | 
							spectralFluxAnalysis();
 | 
				
			||||||
		pfr.work = false;
 | 
							thresholdCalculation();
 | 
				
			||||||
		pdr.work = false;
 | 
							pruneFluxValues();
 | 
				
			||||||
 | 
							peakDetection();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private void spectralFluxAnalysis() {
 | 
				
			||||||
 | 
							progress = 0;
 | 
				
			||||||
 | 
							int tasksDone = 0;
 | 
				
			||||||
 | 
							long totalTasks = MathUtils.round((float)processor.getSampleFrames()/windowSize);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							float[] audioPCM = new float[windowSize];
 | 
				
			||||||
 | 
							float[] spectrum = new float[(windowSize/2)+1];
 | 
				
			||||||
 | 
							float[] lastSpectrum = new float[(windowSize/2)+1];
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							int bassBinBegin = 1;
 | 
				
			||||||
 | 
							int bassBinEnd = 11;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							int mBinBegin = 50;
 | 
				
			||||||
 | 
							int mBinEnd = 250;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							int umBinBegin = 350;
 | 
				
			||||||
 | 
							int umBinEnd = 513;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							Gdx.app.debug("Read freq", String.valueOf(processor.getSampleRate()));
 | 
				
			||||||
 | 
							Gdx.app.debug("Using following bin ranges", "\nBass freq begin: " + bassBinBegin + "\nBass freq end: " + bassBinEnd + "\nMain freq begin: " + umBinBegin + "\nMain freq end: " + umBinEnd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Gdx.app.debug("Total tasks", String.valueOf(totalTasks));
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							FloatFFT_1D fft = new FloatFFT_1D(windowSize);
 | 
				
			||||||
 | 
							int seedDigit = 0;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							while (processor.readFrames(audioPCM) > 0 && work) {
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								fft.realForward(audioPCM);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								//Building a PUID (Pseudo unique ID)
 | 
				
			||||||
 | 
								if (tasksDone == (seedDigit*totalTasks/9)) {
 | 
				
			||||||
 | 
									float avg = 0;
 | 
				
			||||||
 | 
									for (int frame = 0; frame < spectrum.length; frame++) {
 | 
				
			||||||
 | 
										avg += spectrum[frame];
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									avg /= spectrum.length;
 | 
				
			||||||
 | 
									if (avg < 0) {
 | 
				
			||||||
 | 
										avg *= -1f;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									PUID +=(int) Math.pow(10, 9-seedDigit) * ((int)(avg*1000f)-(int)(avg*100f)*10);
 | 
				
			||||||
 | 
									seedDigit ++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								System.arraycopy(spectrum, 0, lastSpectrum, 0, spectrum.length);
 | 
				
			||||||
 | 
								System.arraycopy(audioPCM, 0, spectrum, 0, spectrum.length);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								float fluxVal;
 | 
				
			||||||
 | 
								//bass detection
 | 
				
			||||||
 | 
								fluxVal = 0;
 | 
				
			||||||
 | 
								for (int i = bassBinBegin; i < bassBinEnd; i++) {
 | 
				
			||||||
 | 
									fluxVal += ((spectrum[i] - lastSpectrum[i])) < 0
 | 
				
			||||||
 | 
											? 0 : (spectrum[i] - lastSpectrum[i]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								bassSpectralFlux.add(fluxVal);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								//m detection
 | 
				
			||||||
 | 
								fluxVal = 0;
 | 
				
			||||||
 | 
								for (int i = mBinBegin; i < mBinEnd; i++) {
 | 
				
			||||||
 | 
									fluxVal += ((spectrum[i] - lastSpectrum[i])) < 0
 | 
				
			||||||
 | 
											? 0 : (spectrum[i] - lastSpectrum[i]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								mSpectralFlux.add(fluxVal);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								//um detection
 | 
				
			||||||
 | 
								fluxVal = 0;
 | 
				
			||||||
 | 
								for (int i = umBinBegin; i < umBinEnd; i++) {
 | 
				
			||||||
 | 
									fluxVal += ((spectrum[i] - lastSpectrum[i])) < 0
 | 
				
			||||||
 | 
											? 0 : (spectrum[i] - lastSpectrum[i]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								umSpectralFlux.add(fluxVal);
 | 
				
			||||||
 | 
								tasksDone++;
 | 
				
			||||||
 | 
								progress = (int) (100f*tasksDone/totalTasks);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if (work) {
 | 
				
			||||||
 | 
								Gdx.app.debug("Audio Analyzer", "Done getting spectral flux.");
 | 
				
			||||||
 | 
								Gdx.app.debug("Audio Analyzer", "window count: " + bassSpectralFlux.size);
 | 
				
			||||||
 | 
								Gdx.app.debug("Audio Analyzer", "USING SEED: " + PUID);
 | 
				
			||||||
 | 
								progress = 100;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private void thresholdCalculation() {
 | 
				
			||||||
 | 
							Gdx.app.debug("Audio Analyzer", "beginning threshold calc.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							float bassThresholdMultiplier = 1.5f;
 | 
				
			||||||
 | 
							float mThresholdMultiplier = 1.4f;
 | 
				
			||||||
 | 
							float umThresholdMultiplier = 1.4f;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							int bassThresholdCalcRange = (int) (0.27/(windowSize/processor.getSampleRate()));
 | 
				
			||||||
 | 
							int mThresholdCalcRange = (int) (0.4/(windowSize/processor.getSampleRate()));
 | 
				
			||||||
 | 
							int umThresholdCalcRange = (int) (0.4/(windowSize/processor.getSampleRate()));
 | 
				
			||||||
 | 
							//threshold calculation
 | 
				
			||||||
 | 
							for (int i = 0; i < umSpectralFlux.size && work; i++) {
 | 
				
			||||||
 | 
								int start = Math.max(0, i - bassThresholdCalcRange/2);
 | 
				
			||||||
 | 
								int end = Math.min(umSpectralFlux.size - 1, i + bassThresholdCalcRange/2);
 | 
				
			||||||
 | 
								float average = 0;
 | 
				
			||||||
 | 
								for (int j = start; j <= end; j++) {
 | 
				
			||||||
 | 
									average += bassSpectralFlux.get(j);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								average /= (end - start);
 | 
				
			||||||
 | 
								bassThreshold.add(average * bassThresholdMultiplier);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								start = Math.max(0, i - mThresholdCalcRange/2);
 | 
				
			||||||
 | 
								end = Math.min(umSpectralFlux.size - 1, i + mThresholdCalcRange/2);
 | 
				
			||||||
 | 
								average = 0;
 | 
				
			||||||
 | 
								for (int j = start; j <= end; j++) {
 | 
				
			||||||
 | 
									average+= mSpectralFlux.get(j);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								average /= (end - start);
 | 
				
			||||||
 | 
								mThreshold.add(average*mThresholdMultiplier);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								start = Math.max(0, i - umThresholdCalcRange/2);
 | 
				
			||||||
 | 
								end = Math.min(umSpectralFlux.size - 1, i + umThresholdCalcRange/2);
 | 
				
			||||||
 | 
								average = 0;
 | 
				
			||||||
 | 
								for (int j = start; j <= end; j++) {
 | 
				
			||||||
 | 
									average+= umSpectralFlux.get(j);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								average /= (end - start);
 | 
				
			||||||
 | 
								umThreshold.add(average*umThresholdMultiplier);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Gdx.app.debug("Audio Analyzer", "Threshold calculated.");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private void pruneFluxValues() {
 | 
				
			||||||
 | 
							//pruning data
 | 
				
			||||||
 | 
							float prunnedCurrentVal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int i = 0; i < umSpectralFlux.size && work; i++) {
 | 
				
			||||||
 | 
								prunnedCurrentVal = bassSpectralFlux.get(i) - bassThreshold.get(i);
 | 
				
			||||||
 | 
								if (prunnedCurrentVal >= 0) {
 | 
				
			||||||
 | 
									bassPrunned.add(prunnedCurrentVal);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									bassPrunned.add(0);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								prunnedCurrentVal = mSpectralFlux.get(i) - mThreshold.get(i);
 | 
				
			||||||
 | 
								if (prunnedCurrentVal >= 0 ) {
 | 
				
			||||||
 | 
									mPrunned.add(prunnedCurrentVal);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									mPrunned.add(0);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								prunnedCurrentVal = umSpectralFlux.get(i) - umThreshold.get(i);
 | 
				
			||||||
 | 
								if (prunnedCurrentVal >= 0 ) {
 | 
				
			||||||
 | 
									umPrunned.add(prunnedCurrentVal);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									umPrunned.add(0);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Gdx.app.debug("Audio Analyzer", "Data prunned.");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private void peakDetection() {
 | 
				
			||||||
 | 
							int lastBeatID = 0;
 | 
				
			||||||
 | 
							float bassBeats = 0;
 | 
				
			||||||
 | 
							float mBeats = 0;
 | 
				
			||||||
 | 
							float umBeats = 0;
 | 
				
			||||||
 | 
							float avgSPB = -1f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int i = 0; i < umPrunned.size-1 && work; i++) {
 | 
				
			||||||
 | 
								bassPeaks.add((bassPrunned.get(i) > bassPrunned.get(i+1) ? bassPrunned.get(i) : 0f));
 | 
				
			||||||
 | 
								if (bassPeaks.get(i) > bassMaxValue) {
 | 
				
			||||||
 | 
									bassMaxValue = bassPeaks.get(i);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								mPeaks.add((mPrunned.get(i) > mPrunned.get(i+1) ? mPrunned.get(i) : 0f));
 | 
				
			||||||
 | 
								if (mPeaks.get(i) > mMaxValue) {
 | 
				
			||||||
 | 
									mMaxValue = mPeaks.get(i);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								umPeaks.add((umPrunned.get(i) > umPrunned.get(i+1) ? umPrunned.get(i) : 0f));
 | 
				
			||||||
 | 
								if (umPeaks.get(i) > umMaxValue) {
 | 
				
			||||||
 | 
									umMaxValue = umPeaks.get(i);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (avgSPB != -1) {
 | 
				
			||||||
 | 
									if (bassPeaks.get(i) == 0) {
 | 
				
			||||||
 | 
										avgSPB ++;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										lastBeatID = i;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (bassPeaks.get(i) != 0) {
 | 
				
			||||||
 | 
									avgSPB = 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (bassPeaks.get(i) != 0) {
 | 
				
			||||||
 | 
									bassAvg += bassPeaks.get(i);
 | 
				
			||||||
 | 
									bassBeats++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								if (mPeaks.get(i) != 0) {
 | 
				
			||||||
 | 
									mAvg += mPeaks.get(i);
 | 
				
			||||||
 | 
									mBeats++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								if (umPeaks.get(i) != 0) {
 | 
				
			||||||
 | 
									umAvg += umPeaks.get(i);
 | 
				
			||||||
 | 
									umBeats++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							secondsPerWindow = windowSize/processor.getSampleRate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//then we minus one from the beats so it actually works out
 | 
				
			||||||
 | 
							avgSPB -= bassPrunned.size-lastBeatID;
 | 
				
			||||||
 | 
							avgSPB *= secondsPerWindow;
 | 
				
			||||||
 | 
							avgSPB /= bassBeats;
 | 
				
			||||||
 | 
							Gdx.app.debug("Audio Analyzer", "Avg SPB: " + avgSPB);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bassAvg /= bassBeats;
 | 
				
			||||||
 | 
							mAvg /= mBeats;
 | 
				
			||||||
 | 
							umAvg /= umBeats;
 | 
				
			||||||
 | 
							Gdx.app.debug("Audio Analyzer", "Avg bass: " + bassAvg);
 | 
				
			||||||
 | 
							Gdx.app.debug("Audio Analyzer", "Avg M: " + mAvg);
 | 
				
			||||||
 | 
							Gdx.app.debug("Audio Analyzer", "Avg UM: " + umAvg);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public int getProgress() {
 | 
				
			||||||
 | 
							return progress;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void dispose() {
 | 
						public void dispose() {
 | 
				
			||||||
		stop();
 | 
							if (thread != null) {
 | 
				
			||||||
		exec.shutdown();
 | 
								work = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public boolean isDone() {
 | 
						public void stop() {
 | 
				
			||||||
		if ((sfar.isDone() && tcr.isDone() && pfr.isDone() && pdr.isDone())) {
 | 
							work = false;
 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,125 +0,0 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.analyzer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.Gdx;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.utils.FloatArray;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.AudioDataPackage;
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class PeakDetectionRunnable implements Runnable {
 | 
					 | 
				
			||||||
	boolean work = true, done;
 | 
					 | 
				
			||||||
	private FloatArray bassPrunned;
 | 
					 | 
				
			||||||
	private FloatArray mPrunned;
 | 
					 | 
				
			||||||
	private FloatArray umPrunned;
 | 
					 | 
				
			||||||
	private FloatArray bassPeaks = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray mPeaks = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray umPeaks = new FloatArray();
 | 
					 | 
				
			||||||
	private float bassMaxValue;
 | 
					 | 
				
			||||||
	private float mMaxValue;
 | 
					 | 
				
			||||||
	private float umMaxValue;
 | 
					 | 
				
			||||||
	private float secondsPerWindow;
 | 
					 | 
				
			||||||
	private float mAvg;
 | 
					 | 
				
			||||||
	private float bassAvg;
 | 
					 | 
				
			||||||
	private float umAvg;
 | 
					 | 
				
			||||||
	private MusicManager musicManager;
 | 
					 | 
				
			||||||
	private AudioDataPackage pack;
 | 
					 | 
				
			||||||
	private int PUID;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public PeakDetectionRunnable(PruneFluxRunnable pfr, int PUID) {
 | 
					 | 
				
			||||||
		bassPrunned = pfr.getBassPrunned();
 | 
					 | 
				
			||||||
		mPrunned = pfr.getmPrunned();
 | 
					 | 
				
			||||||
		umPrunned = pfr.getUmPrunned();
 | 
					 | 
				
			||||||
		musicManager = pfr.getMusicManager();
 | 
					 | 
				
			||||||
		this.PUID = PUID;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void run() {
 | 
					 | 
				
			||||||
		int lastBeatID = 0;
 | 
					 | 
				
			||||||
		float bassBeats = 0;
 | 
					 | 
				
			||||||
		float mBeats = 0;
 | 
					 | 
				
			||||||
		float umBeats = 0;
 | 
					 | 
				
			||||||
		float avgSPB = -1f;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (int i = 0; i < umPrunned.size-1 && work; i++) {
 | 
					 | 
				
			||||||
			bassPeaks.add((bassPrunned.get(i) > bassPrunned.get(i+1) ? bassPrunned.get(i) : 0f));
 | 
					 | 
				
			||||||
			if (bassPeaks.get(i) > bassMaxValue) {
 | 
					 | 
				
			||||||
				bassMaxValue = bassPeaks.get(i);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			mPeaks.add((mPrunned.get(i) > mPrunned.get(i+1) ? mPrunned.get(i) : 0f));
 | 
					 | 
				
			||||||
			if (mPeaks.get(i) > mMaxValue) {
 | 
					 | 
				
			||||||
				mMaxValue = mPeaks.get(i);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			umPeaks.add((umPrunned.get(i) > umPrunned.get(i+1) ? umPrunned.get(i) : 0f));
 | 
					 | 
				
			||||||
			if (umPeaks.get(i) > umMaxValue) {
 | 
					 | 
				
			||||||
				umMaxValue = umPeaks.get(i);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (avgSPB != -1) {
 | 
					 | 
				
			||||||
				if (bassPeaks.get(i) == 0) {
 | 
					 | 
				
			||||||
					avgSPB ++;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					lastBeatID = i;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else if (bassPeaks.get(i) != 0) {
 | 
					 | 
				
			||||||
				avgSPB = 0;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (bassPeaks.get(i) != 0) {
 | 
					 | 
				
			||||||
				bassAvg += bassPeaks.get(i);
 | 
					 | 
				
			||||||
				bassBeats++;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			if (mPeaks.get(i) != 0) {
 | 
					 | 
				
			||||||
				mAvg += mPeaks.get(i);
 | 
					 | 
				
			||||||
				mBeats++;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			if (umPeaks.get(i) != 0) {
 | 
					 | 
				
			||||||
				umAvg += umPeaks.get(i);
 | 
					 | 
				
			||||||
				umBeats++;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		secondsPerWindow = musicManager.getReadWindowSize()/musicManager.getSampleRate();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		//then we minus one from the beats so it actually works out
 | 
					 | 
				
			||||||
		avgSPB -= bassPrunned.size-lastBeatID;
 | 
					 | 
				
			||||||
		avgSPB *= secondsPerWindow;
 | 
					 | 
				
			||||||
		avgSPB /= bassBeats;
 | 
					 | 
				
			||||||
		Gdx.app.debug("Audio Analyzer", "Avg SPB: " + avgSPB);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		bassAvg /= bassBeats;
 | 
					 | 
				
			||||||
		mAvg /= mBeats;
 | 
					 | 
				
			||||||
		umAvg /= umBeats;
 | 
					 | 
				
			||||||
		Gdx.app.debug("Audio Analyzer", "Avg bass: " + bassAvg);
 | 
					 | 
				
			||||||
		Gdx.app.debug("Audio Analyzer", "Avg M: " + mAvg);
 | 
					 | 
				
			||||||
		Gdx.app.debug("Audio Analyzer", "Avg UM: " + umAvg);
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		pack = new AudioDataPackage();
 | 
					 | 
				
			||||||
		pack.setBassData(bassPeaks, bassMaxValue, bassAvg);
 | 
					 | 
				
			||||||
		pack.setmData(mPeaks, mMaxValue, mAvg);
 | 
					 | 
				
			||||||
		pack.setUmData(umPeaks, umMaxValue, umAvg);
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		pack.setPUID(PUID);
 | 
					 | 
				
			||||||
		pack.setAvgSPB(avgSPB);
 | 
					 | 
				
			||||||
		pack.setSecPerWin(secondsPerWindow);
 | 
					 | 
				
			||||||
		pack.setMusicInfo(musicManager);
 | 
					 | 
				
			||||||
		if (work) {
 | 
					 | 
				
			||||||
			Gdx.app.debug("Audio Analyzer", "Peak detection complete. Data ready for map gen.");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		done = true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public AudioDataPackage getPack() {
 | 
					 | 
				
			||||||
		return pack;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public boolean isDone() {
 | 
					 | 
				
			||||||
		return done;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,96 +0,0 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.analyzer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.Gdx;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.utils.FloatArray;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class PruneFluxRunnable implements Runnable {
 | 
					 | 
				
			||||||
	boolean work = true;
 | 
					 | 
				
			||||||
	private boolean done;
 | 
					 | 
				
			||||||
	private FloatArray bassSpectralFlux;
 | 
					 | 
				
			||||||
	private FloatArray mSpectralFlux;
 | 
					 | 
				
			||||||
	private FloatArray umSpectralFlux;
 | 
					 | 
				
			||||||
	private FloatArray bassThreshold, mThreshold, umThreshold;
 | 
					 | 
				
			||||||
	private FloatArray bassPrunned = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray mPrunned = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray umPrunned = new FloatArray();
 | 
					 | 
				
			||||||
	private MusicManager musicManager;
 | 
					 | 
				
			||||||
	public PruneFluxRunnable(ThresholdCalcRunnable tcr) {
 | 
					 | 
				
			||||||
		bassSpectralFlux = tcr.getBassSpectralFlux();
 | 
					 | 
				
			||||||
		mSpectralFlux = tcr.getmSpectralFlux();
 | 
					 | 
				
			||||||
		umSpectralFlux = tcr.getUmSpectralFlux();
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		bassThreshold = tcr.getBassThreshold();
 | 
					 | 
				
			||||||
		umThreshold = tcr.getUmThreshold();
 | 
					 | 
				
			||||||
		mThreshold = tcr.getmThreshold();
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		this.musicManager = tcr.getMusicManager();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void run() {
 | 
					 | 
				
			||||||
		//pruning data
 | 
					 | 
				
			||||||
		float prunnedCurrentVal;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (int i = 0; i < umSpectralFlux.size && work; i++) {
 | 
					 | 
				
			||||||
			prunnedCurrentVal = bassSpectralFlux.get(i) - bassThreshold.get(i);
 | 
					 | 
				
			||||||
			if (prunnedCurrentVal >= 0) {
 | 
					 | 
				
			||||||
				bassPrunned.add(prunnedCurrentVal);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				bassPrunned.add(0);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			prunnedCurrentVal = mSpectralFlux.get(i) - mThreshold.get(i);
 | 
					 | 
				
			||||||
			if (prunnedCurrentVal >= 0 ) {
 | 
					 | 
				
			||||||
				mPrunned.add(prunnedCurrentVal);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				mPrunned.add(0);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			prunnedCurrentVal = umSpectralFlux.get(i) - umThreshold.get(i);
 | 
					 | 
				
			||||||
			if (prunnedCurrentVal >= 0 ) {
 | 
					 | 
				
			||||||
				umPrunned.add(prunnedCurrentVal);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				umPrunned.add(0);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		done = true;
 | 
					 | 
				
			||||||
		Gdx.app.debug("Audio Analyzer", "Data prunned.");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public FloatArray getBassSpectralFlux() {
 | 
					 | 
				
			||||||
		return bassSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getmSpectralFlux() {
 | 
					 | 
				
			||||||
		return mSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getBassThreshold() {
 | 
					 | 
				
			||||||
		return bassThreshold;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getmThreshold() {
 | 
					 | 
				
			||||||
		return mThreshold;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getUmSpectralFlux() {
 | 
					 | 
				
			||||||
		return umSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getUmThreshold() {
 | 
					 | 
				
			||||||
		return umThreshold;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getBassPrunned() {
 | 
					 | 
				
			||||||
		return bassPrunned;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getmPrunned() {
 | 
					 | 
				
			||||||
		return mPrunned;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getUmPrunned() {
 | 
					 | 
				
			||||||
		return umPrunned;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public boolean isDone() {
 | 
					 | 
				
			||||||
		return done;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public MusicManager getMusicManager() {
 | 
					 | 
				
			||||||
		return musicManager;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,141 +0,0 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.analyzer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.Gdx;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.math.MathUtils;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.utils.FloatArray;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class SpectralFluxAnalysisRunnable implements Runnable {
 | 
					 | 
				
			||||||
	boolean work = true;
 | 
					 | 
				
			||||||
	private boolean done;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private FloatArray bassSpectralFlux = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray mSpectralFlux = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray umSpectralFlux = new FloatArray();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	MusicManager musicManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private int PUID;
 | 
					 | 
				
			||||||
	private int progress;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public SpectralFluxAnalysisRunnable(MusicManager mm) {
 | 
					 | 
				
			||||||
		this.musicManager = mm;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void run() {
 | 
					 | 
				
			||||||
		progress = 0;
 | 
					 | 
				
			||||||
		int tasksDone = 0;
 | 
					 | 
				
			||||||
		long totalTasks = MathUtils.round((float)musicManager.getSampleCount()/musicManager.getChannelCount()/musicManager.getReadWindowSize());
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		float[] audioPCM = new float[musicManager.getReadWindowSize()];
 | 
					 | 
				
			||||||
		float[] spectrum = new float[(musicManager.getReadWindowSize()/2)+1];
 | 
					 | 
				
			||||||
		float[] lastSpectrum = new float[(musicManager.getReadWindowSize()/2)+1];
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		int bassBinBegin = 1;
 | 
					 | 
				
			||||||
		int bassBinEnd = 11;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		int mBinBegin = 50;
 | 
					 | 
				
			||||||
		int mBinEnd = 250;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		int umBinBegin = 350;
 | 
					 | 
				
			||||||
		int umBinEnd = 513;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		Gdx.app.debug("Analyzing Song", musicManager.getBasicSongName());
 | 
					 | 
				
			||||||
		Gdx.app.debug("Read freq", String.valueOf(musicManager.getSampleRate()));
 | 
					 | 
				
			||||||
		Gdx.app.debug("Using following bin ranges", "\nBass freq begin: " + bassBinBegin + "\nBass freq end: " + bassBinEnd + "\nMain freq begin: " + umBinBegin + "\nMain freq end: " + umBinEnd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Gdx.app.debug("Total tasks", String.valueOf(totalTasks));
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		FloatFFT_1D fft = new FloatFFT_1D(musicManager.getReadWindowSize());
 | 
					 | 
				
			||||||
		int seedDigit = 0;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		while (musicManager.readSampleFrames(audioPCM) > 0 && work) {
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			fft.realForward(audioPCM);
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//Building a PUID (Pseudo unique ID)
 | 
					 | 
				
			||||||
			if (tasksDone == (seedDigit*totalTasks/9)) {
 | 
					 | 
				
			||||||
				float avg = 0;
 | 
					 | 
				
			||||||
				for (int frame = 0; frame < spectrum.length; frame++) {
 | 
					 | 
				
			||||||
					avg += spectrum[frame];
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				avg /= spectrum.length;
 | 
					 | 
				
			||||||
				if (avg < 0) {
 | 
					 | 
				
			||||||
					avg *= -1f;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				PUID +=(int) Math.pow(10, 9-seedDigit) * ((int)(avg*1000f)-(int)(avg*100f)*10);
 | 
					 | 
				
			||||||
				seedDigit ++;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			System.arraycopy(spectrum, 0, lastSpectrum, 0, spectrum.length);
 | 
					 | 
				
			||||||
			System.arraycopy(audioPCM, 0, spectrum, 0, spectrum.length);
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			float fluxVal;
 | 
					 | 
				
			||||||
			//bass detection
 | 
					 | 
				
			||||||
			fluxVal = 0;
 | 
					 | 
				
			||||||
			for (int i = bassBinBegin; i < bassBinEnd; i++) {
 | 
					 | 
				
			||||||
				fluxVal += ((spectrum[i] - lastSpectrum[i])) < 0
 | 
					 | 
				
			||||||
						? 0 : (spectrum[i] - lastSpectrum[i]);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			bassSpectralFlux.add(fluxVal);
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//m detection
 | 
					 | 
				
			||||||
			fluxVal = 0;
 | 
					 | 
				
			||||||
			for (int i = mBinBegin; i < mBinEnd; i++) {
 | 
					 | 
				
			||||||
				fluxVal += ((spectrum[i] - lastSpectrum[i])) < 0
 | 
					 | 
				
			||||||
						? 0 : (spectrum[i] - lastSpectrum[i]);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			mSpectralFlux.add(fluxVal);
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			//um detection
 | 
					 | 
				
			||||||
			fluxVal = 0;
 | 
					 | 
				
			||||||
			for (int i = umBinBegin; i < umBinEnd; i++) {
 | 
					 | 
				
			||||||
				fluxVal += ((spectrum[i] - lastSpectrum[i])) < 0
 | 
					 | 
				
			||||||
						? 0 : (spectrum[i] - lastSpectrum[i]);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			umSpectralFlux.add(fluxVal);
 | 
					 | 
				
			||||||
			tasksDone++;
 | 
					 | 
				
			||||||
			progress = (int) (100f*tasksDone/totalTasks);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if (work) {
 | 
					 | 
				
			||||||
			Gdx.app.debug("Audio Analyzer", "Done getting spectral flux.");
 | 
					 | 
				
			||||||
			Gdx.app.debug("Audio Analyzer", "window count: " + bassSpectralFlux.size);
 | 
					 | 
				
			||||||
			Gdx.app.debug("Audio Analyzer", "USING SEED: " + PUID);
 | 
					 | 
				
			||||||
			progress = 100;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		done = true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public synchronized int getProgress() {
 | 
					 | 
				
			||||||
		return progress;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getBassSpectralFlux() {
 | 
					 | 
				
			||||||
		return bassSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getmSpectralFlux() {
 | 
					 | 
				
			||||||
		return mSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getUmSpectralFlux() {
 | 
					 | 
				
			||||||
		return umSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public int getPUID() {
 | 
					 | 
				
			||||||
		return PUID;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public boolean isDone() {
 | 
					 | 
				
			||||||
		return done;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public boolean isWorking() {
 | 
					 | 
				
			||||||
		return work;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public void setWork(boolean work) {
 | 
					 | 
				
			||||||
		this.work = work;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public MusicManager getMusicManager() {
 | 
					 | 
				
			||||||
		return musicManager;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,102 +0,0 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.analyzer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.Gdx;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.utils.FloatArray;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class ThresholdCalcRunnable implements Runnable {
 | 
					 | 
				
			||||||
	boolean work = true;
 | 
					 | 
				
			||||||
	private boolean done;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	private MusicManager musicManager;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	private FloatArray bassSpectralFlux;
 | 
					 | 
				
			||||||
	private FloatArray mSpectralFlux;
 | 
					 | 
				
			||||||
	private FloatArray umSpectralFlux;
 | 
					 | 
				
			||||||
	private FloatArray bassThreshold = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray mThreshold = new FloatArray();
 | 
					 | 
				
			||||||
	private FloatArray umThreshold = new FloatArray();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public ThresholdCalcRunnable(SpectralFluxAnalysisRunnable sfar) {
 | 
					 | 
				
			||||||
		this.bassSpectralFlux = sfar.getBassSpectralFlux();
 | 
					 | 
				
			||||||
		this.mSpectralFlux = sfar.getmSpectralFlux();
 | 
					 | 
				
			||||||
		this.umSpectralFlux = sfar.getUmSpectralFlux();
 | 
					 | 
				
			||||||
		this.musicManager = sfar.getMusicManager();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void run() {
 | 
					 | 
				
			||||||
		Gdx.app.debug("Audio Analyzer", "beginning threshold calc.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		float bassThresholdMultiplier = 1.5f;
 | 
					 | 
				
			||||||
		float mThresholdMultiplier = 1.4f;
 | 
					 | 
				
			||||||
		float umThresholdMultiplier = 1.4f;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		int bassThresholdCalcRange = thresholdRangeCalc(0.27f);
 | 
					 | 
				
			||||||
		int mThresholdCalcRange = thresholdRangeCalc(0.4f);
 | 
					 | 
				
			||||||
		int umThresholdCalcRange = thresholdRangeCalc(0.4f);
 | 
					 | 
				
			||||||
		//threshold calculation
 | 
					 | 
				
			||||||
		for (int i = 0; i < umSpectralFlux.size && work; i++) {
 | 
					 | 
				
			||||||
			int start = Math.max(0, i - bassThresholdCalcRange/2);
 | 
					 | 
				
			||||||
			int end = Math.min(umSpectralFlux.size - 1, i + bassThresholdCalcRange/2);
 | 
					 | 
				
			||||||
			float average = 0;
 | 
					 | 
				
			||||||
			for (int j = start; j <= end; j++) {
 | 
					 | 
				
			||||||
				average += bassSpectralFlux.get(j);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			average /= (end - start);
 | 
					 | 
				
			||||||
			bassThreshold.add(average * bassThresholdMultiplier);
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			start = Math.max(0, i - mThresholdCalcRange/2);
 | 
					 | 
				
			||||||
			end = Math.min(umSpectralFlux.size - 1, i + mThresholdCalcRange/2);
 | 
					 | 
				
			||||||
			average = 0;
 | 
					 | 
				
			||||||
			for (int j = start; j <= end; j++) {
 | 
					 | 
				
			||||||
				average+= mSpectralFlux.get(j);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			average /= (end - start);
 | 
					 | 
				
			||||||
			mThreshold.add(average*mThresholdMultiplier);
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			start = Math.max(0, i - umThresholdCalcRange/2);
 | 
					 | 
				
			||||||
			end = Math.min(umSpectralFlux.size - 1, i + umThresholdCalcRange/2);
 | 
					 | 
				
			||||||
			average = 0;
 | 
					 | 
				
			||||||
			for (int j = start; j <= end; j++) {
 | 
					 | 
				
			||||||
				average+= umSpectralFlux.get(j);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			average /= (end - start);
 | 
					 | 
				
			||||||
			umThreshold.add(average*umThresholdMultiplier);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		Gdx.app.debug("Audio Analyzer", "Threshold calculated.");
 | 
					 | 
				
			||||||
		done = true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	private int thresholdRangeCalc(float durationOfRange) {
 | 
					 | 
				
			||||||
		return (int) (durationOfRange/(musicManager.getReadWindowSize()/musicManager.getSampleRate()));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public boolean isDone() {
 | 
					 | 
				
			||||||
		return done;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public FloatArray getBassSpectralFlux() {
 | 
					 | 
				
			||||||
		return bassSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getmSpectralFlux() {
 | 
					 | 
				
			||||||
		return mSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getUmSpectralFlux() {
 | 
					 | 
				
			||||||
		return umSpectralFlux;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getBassThreshold() {
 | 
					 | 
				
			||||||
		return bassThreshold;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getmThreshold() {
 | 
					 | 
				
			||||||
		return mThreshold;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public FloatArray getUmThreshold() {
 | 
					 | 
				
			||||||
		return umThreshold;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public MusicManager getMusicManager() {
 | 
					 | 
				
			||||||
		return musicManager;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,14 +1,9 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.processor;
 | 
					package zero1hd.rhythmbullet.audio.processor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.badlogic.gdx.files.FileHandle;
 | 
				
			||||||
import com.badlogic.gdx.utils.Disposable;
 | 
					import com.badlogic.gdx.utils.Disposable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public interface AudioProcessor extends Disposable {
 | 
					public interface AudioProcessor 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
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
@@ -20,12 +15,30 @@ public interface AudioProcessor extends Disposable {
 | 
				
			|||||||
	public int getSampleRate();
 | 
						public int getSampleRate();
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * <b>Thread safe</b>
 | 
						 * Reads samples 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, Object syncObj);
 | 
						public int readSamples(short[] pcm);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Reads 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 readFrames(float[] pcm);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return The music file's {@link FileHandle}
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public FileHandle getMusicFileHandle();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @return the number of sample frames in the song.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public long getSampleFrames();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,31 +16,23 @@ public class WAVAudioProcessor implements AudioProcessor {
 | 
				
			|||||||
	private byte[] buffer;
 | 
						private byte[] buffer;
 | 
				
			||||||
	private FileHandle fileHandle;
 | 
						private FileHandle fileHandle;
 | 
				
			||||||
	private AudioInputStream audioInputStream;
 | 
						private AudioInputStream audioInputStream;
 | 
				
			||||||
	private boolean initiated;
 | 
						private long sampleFrames;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public WAVAudioProcessor(FileHandle fileHandle) {
 | 
						public WAVAudioProcessor(FileHandle fileHandle) {
 | 
				
			||||||
		this.fileHandle = fileHandle;
 | 
							this.fileHandle = fileHandle;
 | 
				
			||||||
		AudioFormat format;
 | 
							AudioFormat format;
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			format = AudioSystem.getAudioFileFormat(fileHandle.file()).getFormat();
 | 
								audioInputStream = AudioSystem.getAudioInputStream(fileHandle.file());
 | 
				
			||||||
 | 
								format = audioInputStream.getFormat();
 | 
				
			||||||
			stereo = format.getChannels() > 1 ? true : false;
 | 
								stereo = format.getChannels() > 1 ? true : false;
 | 
				
			||||||
			sampleRate = (int) format.getSampleRate();
 | 
								sampleRate = (int) format.getSampleRate();
 | 
				
			||||||
 | 
								sampleFrames = audioInputStream.getFrameLength();
 | 
				
			||||||
		} catch (UnsupportedAudioFileException | IOException e) {
 | 
							} catch (UnsupportedAudioFileException | IOException e) {
 | 
				
			||||||
			Gdx.app.debug("WAVAudioProcessor", "Couldn't instantiate WAVAUdioProcessor due to error.");
 | 
								Gdx.app.debug("WAVAudioProcessor", "Couldn't instantiate WAVAUdioProcessor due to error.");
 | 
				
			||||||
			e.printStackTrace();
 | 
								e.printStackTrace();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void initiate() {
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			audioInputStream = AudioSystem.getAudioInputStream(fileHandle.file());
 | 
					 | 
				
			||||||
		} catch (UnsupportedAudioFileException | IOException e) {
 | 
					 | 
				
			||||||
			e.printStackTrace();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		buffer = new byte[audioInputStream.getFormat().getFrameSize()];
 | 
							buffer = new byte[audioInputStream.getFormat().getFrameSize()];
 | 
				
			||||||
		initiated = true;
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public boolean isStereo() {
 | 
						public boolean isStereo() {
 | 
				
			||||||
@@ -52,9 +44,7 @@ public class WAVAudioProcessor implements AudioProcessor {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public int readSamples(short[] pcm, Object syncObj) {
 | 
						public int readSamples(short[] pcm) {
 | 
				
			||||||
		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 {
 | 
				
			||||||
@@ -74,9 +64,37 @@ public class WAVAudioProcessor implements AudioProcessor {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return framesRead;
 | 
							return framesRead;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		} else {
 | 
						
 | 
				
			||||||
			throw new IllegalStateException("Stream has not been initialized.");
 | 
						@Override
 | 
				
			||||||
 | 
						public int readFrames(float[] 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));
 | 
				
			||||||
 | 
										if (stereo) {
 | 
				
			||||||
 | 
											short secondChan = (short) ((buffer[3] << 8) + (buffer[2] & 0x00ff));
 | 
				
			||||||
 | 
											pcm[sampleID] = secondChan > pcm[sampleID] ? secondChan : pcm[sampleID];
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
										framesRead++;
 | 
				
			||||||
 | 
										pcm[sampleID] /= Short.MAX_VALUE+1;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} catch (IOException e) {
 | 
				
			||||||
 | 
									e.printStackTrace();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return framesRead;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public FileHandle getMusicFileHandle() {
 | 
				
			||||||
 | 
							return fileHandle;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public long getSampleFrames() {
 | 
				
			||||||
 | 
							return sampleFrames;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								core/src/zero1hd/rhythmbullet/audio/visualizer/BasicFFT.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								core/src/zero1hd/rhythmbullet/audio/visualizer/BasicFFT.java
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package zero1hd.rhythmbullet.audio.visualizer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class BasicFFT {
 | 
				
			||||||
 | 
						private FloatFFT_1D fft;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public BasicFFT(int window) {
 | 
				
			||||||
 | 
							fft = new FloatFFT_1D(window);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public void fft(float[] PCM) {
 | 
				
			||||||
 | 
							fft.realForward(PCM);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,75 +0,0 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.visualizer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.graphics.Color;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.graphics.g2d.Batch;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.graphics.g2d.Sprite;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.math.MathUtils;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.math.Vector2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class MirrorVisualizer {
 | 
					 | 
				
			||||||
	private int xPos, yPos;
 | 
					 | 
				
			||||||
	private float rotation;
 | 
					 | 
				
			||||||
	private Sprite[] bars;
 | 
					 | 
				
			||||||
	private boolean flip;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	private Vector2 rectCoordRot;
 | 
					 | 
				
			||||||
	public MirrorVisualizer() {
 | 
					 | 
				
			||||||
		rectCoordRot = new Vector2();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void setup(Sprite[] bars, float xPos, float yPos, float rotation) {
 | 
					 | 
				
			||||||
		this.bars = new Sprite[bars.length];
 | 
					 | 
				
			||||||
		this.xPos = (int) xPos;
 | 
					 | 
				
			||||||
		this.yPos = (int) yPos;
 | 
					 | 
				
			||||||
		this.rotation = rotation;
 | 
					 | 
				
			||||||
		rectCoordRot.set(MathUtils.cosDeg(rotation), MathUtils.sinDeg(rotation));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (int i = 0; i < bars.length; i++) {
 | 
					 | 
				
			||||||
			this.bars[i] = new Sprite(bars[i]);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void render(int renderIndex, Batch batch, float parentAlpha, Sprite[] bars) {
 | 
					 | 
				
			||||||
		this.bars[renderIndex].setSize(bars[renderIndex].getWidth(), bars[renderIndex].getHeight());
 | 
					 | 
				
			||||||
		this.bars[renderIndex].draw(batch);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void position(int positionIndex, int barWidth, int spaceBetweenBars) {
 | 
					 | 
				
			||||||
		if (flip) {
 | 
					 | 
				
			||||||
			bars[positionIndex].setRotation(rotation+180);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			bars[positionIndex].setRotation(rotation);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		int barSpace = positionIndex*(barWidth+spaceBetweenBars);
 | 
					 | 
				
			||||||
		bars[positionIndex].setPosition(xPos + barSpace*rectCoordRot.x, yPos + barSpace*rectCoordRot.y);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void setColor(Color color) {
 | 
					 | 
				
			||||||
		for (int i = 0; i < bars.length; i++) {
 | 
					 | 
				
			||||||
			bars[i].setColor(color);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void setColor(float r, float g, float b, float a) {
 | 
					 | 
				
			||||||
		for (int i = 0; i < bars.length; i++) {
 | 
					 | 
				
			||||||
			bars[i].setColor(r, g, b, a);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public float getRotation() {
 | 
					 | 
				
			||||||
		return rotation;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void setRotation(float rotation) {
 | 
					 | 
				
			||||||
		this.rotation = rotation;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void setyPos(int yPos) {
 | 
					 | 
				
			||||||
		this.yPos = yPos;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void setxPos(int xPos) {
 | 
					 | 
				
			||||||
		this.xPos = xPos;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,51 +0,0 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.visualizer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.concurrent.locks.ReentrantLock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.utils.Disposable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class MusicManagerFFT implements Disposable {
 | 
					 | 
				
			||||||
	protected MusicManager mm;
 | 
					 | 
				
			||||||
	private FloatFFT_1D fft;
 | 
					 | 
				
			||||||
	private boolean calc;
 | 
					 | 
				
			||||||
	private ReentrantLock lock;
 | 
					 | 
				
			||||||
	protected float[] audioPCM;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public MusicManagerFFT() {
 | 
					 | 
				
			||||||
		lock = new ReentrantLock();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void calculate() {
 | 
					 | 
				
			||||||
		if (mm != null && calc && mm.isPlaying()) {
 | 
					 | 
				
			||||||
			lock.lock();
 | 
					 | 
				
			||||||
			fft.realForward(audioPCM);
 | 
					 | 
				
			||||||
			lock.unlock();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public void setMM(MusicManager mm) {
 | 
					 | 
				
			||||||
		lock.lock();
 | 
					 | 
				
			||||||
		calc = false;
 | 
					 | 
				
			||||||
		if (audioPCM == null || audioPCM.length != mm.getReadWindowSize()) {
 | 
					 | 
				
			||||||
			audioPCM = new float[mm.getReadWindowSize()];
 | 
					 | 
				
			||||||
			fft = new FloatFFT_1D(mm.getReadWindowSize());
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		this.mm = mm;
 | 
					 | 
				
			||||||
		calc = true;
 | 
					 | 
				
			||||||
		lock.unlock();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void dispose() {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	public MusicManager getMm() {
 | 
					 | 
				
			||||||
		return mm;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	public float[] getAudioPCM() {
 | 
					 | 
				
			||||||
		return audioPCM;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,21 +0,0 @@
 | 
				
			|||||||
package zero1hd.rhythmbullet.audio.visualizer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.badlogic.gdx.graphics.g2d.Batch;
 | 
					 | 
				
			||||||
import com.badlogic.gdx.utils.Disposable;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicManager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public interface Visualizer extends Disposable {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void calcPCMData();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void setMM(MusicManager mm);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	MusicManager getMM();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	void render(Batch batch, float delta);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float[] getAudioPCMData();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	void fft();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -3,11 +3,12 @@ package zero1hd.rhythmbullet.desktop.audio.processor;
 | 
				
			|||||||
import com.badlogic.gdx.files.FileHandle;
 | 
					import com.badlogic.gdx.files.FileHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import zero1hd.rhythmbullet.audio.AudioProcessorFactory;
 | 
					import zero1hd.rhythmbullet.audio.AudioProcessorFactory;
 | 
				
			||||||
 | 
					import zero1hd.rhythmbullet.audio.MinimalAudioHeader;
 | 
				
			||||||
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
 | 
					import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class DesktopAudioProcessorFactory implements AudioProcessorFactory {
 | 
					public class DesktopAudioProcessorFactory implements AudioProcessorFactory {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public AudioProcessor newMP3AudioProcessor(FileHandle fileHandle) {
 | 
						public AudioProcessor newMP3AudioProcessor(FileHandle fileHandle) {
 | 
				
			||||||
		return new MP3AudioProcessor(fileHandle);
 | 
							return new MP3AudioProcessor(fileHandle, new MinimalAudioHeader(fileHandle));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,6 @@ package zero1hd.rhythmbullet.desktop.audio.processor;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.badlogic.gdx.Gdx;
 | 
					import com.badlogic.gdx.Gdx;
 | 
				
			||||||
import com.badlogic.gdx.files.FileHandle;
 | 
					import com.badlogic.gdx.files.FileHandle;
 | 
				
			||||||
import com.badlogic.gdx.utils.GdxRuntimeException;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javazoom.jl.decoder.Bitstream;
 | 
					import javazoom.jl.decoder.Bitstream;
 | 
				
			||||||
import javazoom.jl.decoder.BitstreamException;
 | 
					import javazoom.jl.decoder.BitstreamException;
 | 
				
			||||||
@@ -10,12 +9,14 @@ import javazoom.jl.decoder.DecoderException;
 | 
				
			|||||||
import javazoom.jl.decoder.Header;
 | 
					import javazoom.jl.decoder.Header;
 | 
				
			||||||
import javazoom.jl.decoder.MP3Decoder;
 | 
					import javazoom.jl.decoder.MP3Decoder;
 | 
				
			||||||
import javazoom.jl.decoder.OutputBuffer;
 | 
					import javazoom.jl.decoder.OutputBuffer;
 | 
				
			||||||
 | 
					import zero1hd.rhythmbullet.audio.MinimalAudioHeader;
 | 
				
			||||||
import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
 | 
					import zero1hd.rhythmbullet.audio.processor.AudioProcessor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class MP3AudioProcessor implements AudioProcessor {
 | 
					public class MP3AudioProcessor implements AudioProcessor {
 | 
				
			||||||
	private boolean stereo;
 | 
						private boolean stereo;
 | 
				
			||||||
	private int sampleRate;
 | 
						private int sampleRate;
 | 
				
			||||||
 | 
						private long sampleFrames;
 | 
				
			||||||
	private FileHandle fileHandle;
 | 
						private FileHandle fileHandle;
 | 
				
			||||||
	private byte[] currentByteSet;
 | 
						private byte[] currentByteSet;
 | 
				
			||||||
	private byte[] workset;
 | 
						private byte[] workset;
 | 
				
			||||||
@@ -25,30 +26,16 @@ public class MP3AudioProcessor implements AudioProcessor {
 | 
				
			|||||||
	private int indexHead = -1;
 | 
						private int indexHead = -1;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public MP3AudioProcessor(FileHandle fileHandle) {
 | 
						public MP3AudioProcessor(FileHandle fileHandle, MinimalAudioHeader minimalAudioHeader) {
 | 
				
			||||||
		this.fileHandle = fileHandle;
 | 
							this.fileHandle = fileHandle;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		bitstream = new Bitstream(fileHandle.read());
 | 
							bitstream = new Bitstream(fileHandle.read());
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			Header header = bitstream.readFrame();
 | 
					 | 
				
			||||||
			if (header == null) throw new GdxRuntimeException("Empty MP3");
 | 
					 | 
				
			||||||
			stereo = header.mode() == Header.DUAL_CHANNEL;
 | 
					 | 
				
			||||||
			sampleRate = header.getSampleRate();
 | 
					 | 
				
			||||||
		} catch (BitstreamException e) {
 | 
					 | 
				
			||||||
			throw new GdxRuntimeException("error while preloading mp3", e);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		try {
 | 
							stereo = minimalAudioHeader.getChannelCount() == 1 ? false : true;
 | 
				
			||||||
			bitstream.close();
 | 
							sampleRate = minimalAudioHeader.getSampleRate();
 | 
				
			||||||
		} catch (BitstreamException e) {
 | 
							sampleFrames = minimalAudioHeader.estimateSampleFrames();
 | 
				
			||||||
			e.printStackTrace();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public void initiate() {
 | 
					 | 
				
			||||||
		bitstream = new Bitstream(fileHandle.read());
 | 
					 | 
				
			||||||
		decoder = new MP3Decoder();
 | 
							decoder = new MP3Decoder();
 | 
				
			||||||
		sampleBuffer = new OutputBuffer(stereo ? 2 : 1, false);
 | 
							sampleBuffer = new OutputBuffer(stereo ? 2 : 1, false);
 | 
				
			||||||
		decoder.setOutputBuffer(sampleBuffer);
 | 
							decoder.setOutputBuffer(sampleBuffer);
 | 
				
			||||||
@@ -67,8 +54,8 @@ public class MP3AudioProcessor implements AudioProcessor {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public int readSamples(short[] pcm, Object syncObj) {
 | 
						public int readSamples(short[] pcm) {
 | 
				
			||||||
		int framesRead = 0;
 | 
							int samplesRead = 0;
 | 
				
			||||||
		for (int sid = 0; sid < pcm.length; sid++) {
 | 
							for (int sid = 0; sid < pcm.length; sid++) {
 | 
				
			||||||
			for (int wsid = 0; wsid < workset.length; wsid++) {
 | 
								for (int wsid = 0; wsid < workset.length; wsid++) {
 | 
				
			||||||
				workset[wsid] = nextByte();
 | 
									workset[wsid] = nextByte();
 | 
				
			||||||
@@ -78,9 +65,28 @@ public class MP3AudioProcessor implements AudioProcessor {
 | 
				
			|||||||
				if (stereo) {
 | 
									if (stereo) {
 | 
				
			||||||
					short altChan = (short) ((workset[3] << 8) + (workset[2] & 0x00ff));
 | 
										short altChan = (short) ((workset[3] << 8) + (workset[2] & 0x00ff));
 | 
				
			||||||
					sid++;
 | 
										sid++;
 | 
				
			||||||
 | 
										pcm[sid] = altChan;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									samplesRead ++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return samplesRead;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public int readFrames(float[] pcm) {
 | 
				
			||||||
 | 
							int framesRead = 0;
 | 
				
			||||||
 | 
							for (int sid = 0; sid < pcm.length; sid++) {
 | 
				
			||||||
 | 
								for (int wsid = 0; wsid < workset.length; wsid++) {
 | 
				
			||||||
 | 
									workset[wsid] = nextByte();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (currentByteSet != null) {
 | 
				
			||||||
 | 
									pcm[sid] += (workset[1] << 8) + (workset[0] & 0x00ff);
 | 
				
			||||||
 | 
									if (stereo) {
 | 
				
			||||||
 | 
										short altChan = (short) ((workset[3] << 8) + (workset[2] & 0x00ff));
 | 
				
			||||||
					pcm[sid] = altChan > pcm[sid] ? altChan : pcm[sid];
 | 
										pcm[sid] = altChan > pcm[sid] ? altChan : pcm[sid];
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				framesRead ++;
 | 
									framesRead++;
 | 
				
			||||||
				pcm[sid] /= Short.MAX_VALUE+1;
 | 
									pcm[sid] /= Short.MAX_VALUE+1;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -129,6 +135,16 @@ public class MP3AudioProcessor implements AudioProcessor {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public FileHandle getMusicFileHandle() {
 | 
				
			||||||
 | 
							return fileHandle;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public long getSampleFrames() {
 | 
				
			||||||
 | 
							return sampleFrames;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void dispose() {
 | 
						public void dispose() {
 | 
				
			||||||
		Gdx.app.debug("MP3Manager", "Disposing...");
 | 
							Gdx.app.debug("MP3Manager", "Disposing...");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					package zero1hd.rhythmbullet.desktop.audio.visualizer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.badlogic.gdx.graphics.g2d.Batch;
 | 
				
			||||||
 | 
					import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
 | 
				
			||||||
 | 
					import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import zero1hd.rhythmbullet.audio.MusicController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DoubleHorizontalVisualizer {
 | 
				
			||||||
 | 
						private int width, height, barCount, barWidth, spaceBetweenBars;
 | 
				
			||||||
 | 
						private int x, y;
 | 
				
			||||||
 | 
						private float barRate = 0.75f;
 | 
				
			||||||
 | 
						private ShapeRenderer shapeRenderer;
 | 
				
			||||||
 | 
						private PCMMachine pcm;
 | 
				
			||||||
 | 
						private int smoothRange;
 | 
				
			||||||
 | 
						private int multiplier = 300;
 | 
				
			||||||
 | 
						private int[] amplitudes;
 | 
				
			||||||
 | 
						private int[] barHeights;
 | 
				
			||||||
 | 
						private int binsPerBar;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * 
 | 
				
			||||||
 | 
						 * @param barCount amount of bars this visualizer should have.
 | 
				
			||||||
 | 
						 * @param width the width of the visualizer.
 | 
				
			||||||
 | 
						 * @param spacePercentage the percentage of a bar that should be space.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public DoubleHorizontalVisualizer(int barCount, int width, float spacePercentage, int height, MusicController musicController) {
 | 
				
			||||||
 | 
							this.barCount = barCount;
 | 
				
			||||||
 | 
							this.barWidth = width/barCount;
 | 
				
			||||||
 | 
							this.spaceBetweenBars = (int) (barWidth * spacePercentage);
 | 
				
			||||||
 | 
							this.barWidth -= spaceBetweenBars;
 | 
				
			||||||
 | 
							if (barWidth < 1) throw new IllegalArgumentException("The arguments you passed caused the bar width to be 0.");
 | 
				
			||||||
 | 
							binsPerBar = (pcm.getWindowSize()/barCount);
 | 
				
			||||||
 | 
							this.width = width;
 | 
				
			||||||
 | 
							this.height = height;
 | 
				
			||||||
 | 
							pcm = new PCMMachine(musicController);
 | 
				
			||||||
 | 
							amplitudes = new int[barCount];
 | 
				
			||||||
 | 
							barHeights = new int[barCount];
 | 
				
			||||||
 | 
							shapeRenderer = new ShapeRenderer();
 | 
				
			||||||
 | 
							shapeRenderer.set(ShapeType.Filled);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public void act(float delta) {
 | 
				
			||||||
 | 
							for (int bar = 0; bar < amplitudes.length; bar++) {
 | 
				
			||||||
 | 
								float normalizedAmplitude = 0;
 | 
				
			||||||
 | 
								for (int freq = bar*binsPerBar; freq < bar*binsPerBar + binsPerBar; freq++) {
 | 
				
			||||||
 | 
									normalizedAmplitude += Math.abs(pcm.getFrequencyBins()[freq]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								amplitudes[bar] = (int) (normalizedAmplitude*multiplier);
 | 
				
			||||||
 | 
								amplitudes[bar] /= binsPerBar;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								float cappedDelta = Math.max(0, delta);
 | 
				
			||||||
 | 
								cappedDelta = Math.min(1f, delta);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								barHeights[bar] += Math.max(0, (amplitudes[bar] - barHeights[bar]) * barRate * cappedDelta);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for (int bar = 1; bar <= barHeights.length; bar++) {
 | 
				
			||||||
 | 
								int smoothCount = 1;
 | 
				
			||||||
 | 
								for (int range = 0; range < smoothRange; range++) {
 | 
				
			||||||
 | 
									if (bar+range < amplitudes.length) {
 | 
				
			||||||
 | 
										barHeights[bar] += amplitudes[bar+range];
 | 
				
			||||||
 | 
										smoothCount++;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (bar-range > 0) {
 | 
				
			||||||
 | 
										barHeights[bar] += amplitudes[bar-range];
 | 
				
			||||||
 | 
										smoothCount++;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								barHeights[bar] /= smoothCount;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public void draw(Batch batch, float parentAlpha) {
 | 
				
			||||||
 | 
							shapeRenderer.begin();
 | 
				
			||||||
 | 
							int beginX = x + spaceBetweenBars/2, beginY = y;
 | 
				
			||||||
 | 
							for (int bar = 0; bar < barCount; bar++) {
 | 
				
			||||||
 | 
								shapeRenderer.rect(beginX + spaceBetweenBars*bar, beginY+height, beginX+barWidth, beginY+barHeights[bar]+height);
 | 
				
			||||||
 | 
								shapeRenderer.rect(beginX + spaceBetweenBars*bar, beginY, beginX+barWidth, beginY+barHeights[barHeights.length - 1 - bar]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							shapeRenderer.end();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public int getWidth() {
 | 
				
			||||||
 | 
							return width;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public int getHeight() {
 | 
				
			||||||
 | 
							return height;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public void setX(int x) {
 | 
				
			||||||
 | 
							this.x = x;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public void setY(int y) {
 | 
				
			||||||
 | 
							this.y = y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public void setPosition(int x, int y) {
 | 
				
			||||||
 | 
							this.x = x;
 | 
				
			||||||
 | 
							this.y = y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public int getX() {
 | 
				
			||||||
 | 
							return x;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public int getY() {
 | 
				
			||||||
 | 
							return y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,17 +11,20 @@ import org.lwjgl.openal.AL11;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.badlogic.gdx.Gdx;
 | 
					import com.badlogic.gdx.Gdx;
 | 
				
			||||||
import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic;
 | 
					import com.badlogic.gdx.backends.lwjgl.audio.OpenALMusic;
 | 
				
			||||||
 | 
					import com.badlogic.gdx.utils.Disposable;
 | 
				
			||||||
import com.badlogic.gdx.utils.TimeUtils;
 | 
					import com.badlogic.gdx.utils.TimeUtils;
 | 
				
			||||||
import com.badlogic.gdx.utils.reflect.ClassReflection;
 | 
					import com.badlogic.gdx.utils.reflect.ClassReflection;
 | 
				
			||||||
import com.badlogic.gdx.utils.reflect.Field;
 | 
					import com.badlogic.gdx.utils.reflect.Field;
 | 
				
			||||||
import com.badlogic.gdx.utils.reflect.ReflectionException;
 | 
					import com.badlogic.gdx.utils.reflect.ReflectionException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MinimalAudioHeader;
 | 
					 | 
				
			||||||
import zero1hd.rhythmbullet.audio.MusicController;
 | 
					import zero1hd.rhythmbullet.audio.MusicController;
 | 
				
			||||||
 | 
					import zero1hd.rhythmbullet.audio.visualizer.BasicFFT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class DesktopVisualizer implements Observer {
 | 
					public class PCMMachine implements Observer, Disposable {
 | 
				
			||||||
	private int windowSize = 1024;
 | 
						private int windowSize = 1024;
 | 
				
			||||||
	private float[] PCM = new float[windowSize];
 | 
						private float[] PCM = new float[windowSize];
 | 
				
			||||||
 | 
						private float[] frequencyBins = new float[windowSize/2];
 | 
				
			||||||
 | 
						private BasicFFT fft = new BasicFFT(windowSize);
 | 
				
			||||||
	private ShortBuffer playingBuffer;
 | 
						private ShortBuffer playingBuffer;
 | 
				
			||||||
	private ShortBuffer compareBuffer;
 | 
						private ShortBuffer compareBuffer;
 | 
				
			||||||
	private ShortBuffer buffer;
 | 
						private ShortBuffer buffer;
 | 
				
			||||||
@@ -32,8 +35,11 @@ public class DesktopVisualizer implements Observer {
 | 
				
			|||||||
	private BufferStreamReadThread streamReadThread;
 | 
						private BufferStreamReadThread streamReadThread;
 | 
				
			||||||
	private int windowsRead;
 | 
						private int windowsRead;
 | 
				
			||||||
	private int currentPlaybackWindow;
 | 
						private int currentPlaybackWindow;
 | 
				
			||||||
 | 
						private volatile boolean updated;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public DesktopVisualizer() {
 | 
						public PCMMachine(MusicController musicController) {
 | 
				
			||||||
 | 
							this.mc = musicController;
 | 
				
			||||||
 | 
							mc.addObserver(this);
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Field bufferField = ClassReflection.getDeclaredField(OpenALMusic.class, "tempBuffer");
 | 
								Field bufferField = ClassReflection.getDeclaredField(OpenALMusic.class, "tempBuffer");
 | 
				
			||||||
			bufferField.setAccessible(true);
 | 
								bufferField.setAccessible(true);
 | 
				
			||||||
@@ -109,7 +115,7 @@ public class DesktopVisualizer implements Observer {
 | 
				
			|||||||
		windowsRead = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize);
 | 
							windowsRead = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public void setMusic(MinimalAudioHeader header) {
 | 
						private void setMusic() {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID");
 | 
								Field sourceIDField = ClassReflection.getDeclaredField(OpenALMusic.class, "sourceID");
 | 
				
			||||||
			sourceIDField.setAccessible(true);
 | 
								sourceIDField.setAccessible(true);
 | 
				
			||||||
@@ -117,8 +123,9 @@ public class DesktopVisualizer implements Observer {
 | 
				
			|||||||
		} catch (ReflectionException e) {
 | 
							} catch (ReflectionException e) {
 | 
				
			||||||
			e.printStackTrace();
 | 
								e.printStackTrace();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		channelCount = header.getChannelCount();
 | 
							
 | 
				
			||||||
		sampleRate = header.getSampleRate();
 | 
							channelCount = mc.getCurrentMusicHeader().getChannelCount();
 | 
				
			||||||
 | 
							sampleRate = mc.getCurrentMusicHeader().getSampleRate();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		playingBuffer = ShortBuffer.allocate(buffer.capacity()*2);
 | 
							playingBuffer = ShortBuffer.allocate(buffer.capacity()*2);
 | 
				
			||||||
		buffer.rewind();
 | 
							buffer.rewind();
 | 
				
			||||||
@@ -133,15 +140,26 @@ public class DesktopVisualizer implements Observer {
 | 
				
			|||||||
		buffer.rewind();
 | 
							buffer.rewind();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public synchronized float[] getAudioPCM() {
 | 
						public float[] getFrequencyBins() {
 | 
				
			||||||
		return PCM;
 | 
							if (updated) {
 | 
				
			||||||
 | 
								synchronized (PCM) {
 | 
				
			||||||
 | 
									fft.fft(PCM);
 | 
				
			||||||
 | 
									System.arraycopy(PCM, 0, frequencyBins, 0, frequencyBins.length);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return frequencyBins;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	public class BufferStreamReadThread implements Runnable {
 | 
						public int getWindowSize() {
 | 
				
			||||||
 | 
							return windowSize;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						private class BufferStreamReadThread implements Runnable {
 | 
				
			||||||
		private String name = "PCM-Audio-Processing";
 | 
							private String name = "PCM-Audio-Processing";
 | 
				
			||||||
		private Thread thread;
 | 
							private Thread thread;
 | 
				
			||||||
		private boolean run, paused;
 | 
							private volatile boolean run;
 | 
				
			||||||
		private volatile long timeOfLastRead;
 | 
							private boolean paused;
 | 
				
			||||||
 | 
							private long timeOfLastRead;
 | 
				
			||||||
		private int waitTime;
 | 
							private int waitTime;
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
@@ -155,10 +173,10 @@ public class DesktopVisualizer implements Observer {
 | 
				
			|||||||
					waitTime = sampleRate/windowSize/Gdx.graphics.getFramesPerSecond();
 | 
										waitTime = sampleRate/windowSize/Gdx.graphics.getFramesPerSecond();
 | 
				
			||||||
					if (TimeUtils.timeSinceMillis(timeOfLastRead) >= waitTime) {
 | 
										if (TimeUtils.timeSinceMillis(timeOfLastRead) >= waitTime) {
 | 
				
			||||||
						calcPCMData();
 | 
											calcPCMData();
 | 
				
			||||||
 | 
											updated = true;
 | 
				
			||||||
						windowsRead++;
 | 
											windowsRead++;
 | 
				
			||||||
						timeOfLastRead = TimeUtils.millis();
 | 
											timeOfLastRead = TimeUtils.millis();
 | 
				
			||||||
						
 | 
											
 | 
				
			||||||
						
 | 
					 | 
				
			||||||
						currentPlaybackWindow = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize);
 | 
											currentPlaybackWindow = (int) ((mc.getCurrentPosition()*sampleRate)/windowSize);
 | 
				
			||||||
						if (windowsRead != currentPlaybackWindow) {
 | 
											if (windowsRead != currentPlaybackWindow) {
 | 
				
			||||||
							synchronizeBufferWithPlayback();
 | 
												synchronizeBufferWithPlayback();
 | 
				
			||||||
@@ -177,22 +195,28 @@ public class DesktopVisualizer implements Observer {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		public synchronized void start() {
 | 
							public void start() {
 | 
				
			||||||
			if (thread == null && !thread.isAlive()) {
 | 
								if (thread == null && !thread.isAlive()) {
 | 
				
			||||||
				thread = new Thread(this, name);
 | 
									thread = new Thread(this, name);
 | 
				
			||||||
				thread.start();
 | 
									thread.start();
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
 | 
									synchronized (this) {
 | 
				
			||||||
					notify();
 | 
										notify();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
 | 
							public void stop() {
 | 
				
			||||||
 | 
								run = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void update(Observable o, Object arg) {
 | 
						public void update(Observable o, Object arg) {
 | 
				
			||||||
		if (o == mc) {
 | 
							if (o == mc) {
 | 
				
			||||||
			switch ((MusicController.States) arg) {
 | 
								switch ((MusicController.States) arg) {
 | 
				
			||||||
			case Loaded:
 | 
								case Loaded:
 | 
				
			||||||
				setMusic(mc.getCurrentMusicHeader());
 | 
									setMusic();
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case Playing:
 | 
								case Playing:
 | 
				
			||||||
				streamReadThread.start();
 | 
									streamReadThread.start();
 | 
				
			||||||
@@ -202,4 +226,9 @@ public class DesktopVisualizer implements Observer {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void dispose() {
 | 
				
			||||||
 | 
							streamReadThread.stop();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -8,7 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Widget;
 | 
				
			|||||||
import com.badlogic.gdx.utils.Disposable;
 | 
					import com.badlogic.gdx.utils.Disposable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import zero1hd.rhythmbullet.audio.visualizer.HorizontalVisualizer;
 | 
					import zero1hd.rhythmbullet.audio.visualizer.HorizontalVisualizer;
 | 
				
			||||||
import zero1hd.rhythmbullet.desktop.audio.visualizer.DesktopVisualizer;
 | 
					import zero1hd.rhythmbullet.desktop.audio.visualizer.PCMMachine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class HorizontalVisualizerWidget extends Widget implements Disposable {
 | 
					public class HorizontalVisualizerWidget extends Widget implements Disposable {
 | 
				
			||||||
	private HorizontalVisualizer vis;
 | 
						private HorizontalVisualizer vis;
 | 
				
			||||||
@@ -18,7 +18,7 @@ public class HorizontalVisualizerWidget extends Widget implements Disposable {
 | 
				
			|||||||
	private float visRefreshRate;
 | 
						private float visRefreshRate;
 | 
				
			||||||
	private float timer;
 | 
						private float timer;
 | 
				
			||||||
	public HorizontalVisualizerWidget() {
 | 
						public HorizontalVisualizerWidget() {
 | 
				
			||||||
		vis = new HorizontalVisualizer(new DesktopVisualizer());
 | 
							vis = new HorizontalVisualizer(new PCMMachine());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ import com.badlogic.gdx.utils.viewport.ExtendViewport;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import zero1hd.rhythmbullet.RhythmBullet;
 | 
					import zero1hd.rhythmbullet.RhythmBullet;
 | 
				
			||||||
import zero1hd.rhythmbullet.audio.visualizer.CircularVisualizer;
 | 
					import zero1hd.rhythmbullet.audio.visualizer.CircularVisualizer;
 | 
				
			||||||
import zero1hd.rhythmbullet.desktop.audio.visualizer.DesktopVisualizer;
 | 
					import zero1hd.rhythmbullet.desktop.audio.visualizer.PCMMachine;
 | 
				
			||||||
import zero1hd.rhythmbullet.game.GameController;
 | 
					import zero1hd.rhythmbullet.game.GameController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class GameScreen extends ScreenAdapter {
 | 
					public class GameScreen extends ScreenAdapter {
 | 
				
			||||||
@@ -25,7 +25,7 @@ public class GameScreen extends ScreenAdapter {
 | 
				
			|||||||
		this.assets = assets;
 | 
							this.assets = assets;
 | 
				
			||||||
		batch = new SpriteBatch();
 | 
							batch = new SpriteBatch();
 | 
				
			||||||
		viewport = new ExtendViewport(RhythmBullet.WORLD_WIDTH, RhythmBullet.WORLD_HEIGHT);
 | 
							viewport = new ExtendViewport(RhythmBullet.WORLD_WIDTH, RhythmBullet.WORLD_HEIGHT);
 | 
				
			||||||
		circleVisualizer = new CircularVisualizer(new DesktopVisualizer());
 | 
							circleVisualizer = new CircularVisualizer(new PCMMachine());
 | 
				
			||||||
		circleVisualizer.setCenter(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2);
 | 
							circleVisualizer.setCenter(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2);
 | 
				
			||||||
		circleVisualizer.setCamera(viewport.getCamera());
 | 
							circleVisualizer.setCamera(viewport.getCamera());
 | 
				
			||||||
		circleVisualizer.setColor(Color.CYAN.toFloatBits());
 | 
							circleVisualizer.setColor(Color.CYAN.toFloatBits());
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user