be.tarsos.dsp.ui.layers.ConstantQLayer Maven / Gradle / Ivy
/*
* _______ _____ _____ _____
* |__ __| | __ \ / ____| __ \
* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
*
* -------------------------------------------------------------
*
* TarsosDSP is developed by Joren Six at IPEM, University Ghent
*
* -------------------------------------------------------------
*
* Info: http://0110.be/tag/TarsosDSP
* Github: https://github.com/JorenSix/TarsosDSP
* Releases: http://0110.be/releases/TarsosDSP/
*
* TarsosDSP includes modified source code by various authors,
* for credits and info, see README.
*
*/
package be.tarsos.dsp.ui.layers;
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import javax.sound.sampled.UnsupportedAudioFileException;
import be.tarsos.dsp.AudioDispatcher;
import be.tarsos.dsp.AudioEvent;
import be.tarsos.dsp.AudioProcessor;
import be.tarsos.dsp.ConstantQ;
import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;
import be.tarsos.dsp.ui.Axis;
import be.tarsos.dsp.ui.CoordinateSystem;
import be.tarsos.dsp.util.PitchConverter;
public class ConstantQLayer implements Layer, Runnable{
private TreeMap features;
private final CoordinateSystem cs;
private final File audioFile;
private float maxSpectralEnergy = 0;
private float minSpectralEnergy = 100000;
private float[] binStartingPointsInCents;
private float binWith;// in seconds
private float binHeight;// in seconds
/**
* The default minimum pitch, in absolute cents (+-66 Hz)
*/
private int minimumFrequencyInCents = 4000;
/**
* The default maximum pitch, in absolute cents (+-4200 Hz)
*/
private int maximumFrequencyInCents = 10500;
/**
* The default number of bins per octave.
*/
private int binsPerOctave = 48;
/**
* The default increment in samples.
*/
private int increment;
public ConstantQLayer(CoordinateSystem cs, File audioFile, int increment, int minFreqInCents,int maxFreqInCents, int binsPerOctave) {
this.cs = cs;
this.audioFile = audioFile;
this.increment = increment;
this.minimumFrequencyInCents = minFreqInCents;
this.maximumFrequencyInCents = maxFreqInCents;
new Thread(this, "Constant Q Initialization").start();
}
public void draw(Graphics2D graphics) {
if(features != null){
Map spectralInfoSubMap = features.subMap(cs.getMin(Axis.X) / 1000.0, cs.getMax(Axis.X) / 1000.0);
double currentMaxSpectralEnergy = 0;
for (Map.Entry column : spectralInfoSubMap.entrySet()) {
float[] spectralEnergy = column.getValue();
for (int i = 0; i < spectralEnergy.length; i++) {
currentMaxSpectralEnergy = Math.max(currentMaxSpectralEnergy, spectralEnergy[i]);
}
}
for (Map.Entry column : spectralInfoSubMap.entrySet()) {
double timeStart = column.getKey();// in seconds
float[] spectralEnergy = column.getValue();// in cents
// draw the pixels
for (int i = 0; i < spectralEnergy.length; i++) {
Color color = Color.black;
float centsStartingPoint = binStartingPointsInCents[i];
// only draw the visible frequency range
if (centsStartingPoint >= cs.getMin(Axis.Y)
&& centsStartingPoint <= cs.getMax(Axis.Y)) {
int greyValue = 255 - (int) (Math.log1p(spectralEnergy[i])
/ Math.log1p(currentMaxSpectralEnergy) * 255);
greyValue = Math.max(0, greyValue);
color = new Color(greyValue, greyValue, greyValue);
graphics.setColor(color);
graphics.fillRect((int) Math.round(timeStart * 1000),
Math.round(centsStartingPoint),
(int) Math.round(binWith * 1000),
(int) Math.ceil(binHeight));
}
}
}
}
}
public void run() {
try {
float minimumFrequencyInHertz = (float) PitchConverter.absoluteCentToHertz(minimumFrequencyInCents);
float maximumFrequencyInHertz = (float) PitchConverter.absoluteCentToHertz(maximumFrequencyInCents);
final float sampleRate = AudioDispatcherFactory.fromFile(audioFile, 2048,0).getFormat().getFrameRate();
final ConstantQ constantQ = new ConstantQ(sampleRate,minimumFrequencyInHertz,maximumFrequencyInHertz, binsPerOctave);
binWith = increment / sampleRate;
binHeight = 1200 / (float) binsPerOctave;
float[] startingPointsInHertz = constantQ.getFreqencies();
binStartingPointsInCents = new float[startingPointsInHertz.length];
for (int i = 0; i < binStartingPointsInCents.length; i++) {
binStartingPointsInCents[i] = (float) PitchConverter
.hertzToAbsoluteCent(startingPointsInHertz[i]);
}
int size = constantQ.getFFTlength();
AudioDispatcher adp = AudioDispatcherFactory.fromFile(audioFile, size,size-increment);
final double constantQLag = size / adp.getFormat().getSampleRate() - binWith / 2.0;// in seconds
final TreeMap fe = new TreeMap();
adp.addAudioProcessor(constantQ);
adp.addAudioProcessor(new AudioProcessor() {
public void processingFinished() {
float minValue = 5 / 1000000.0f;
for (float[] magnitudes : fe.values()) {
for (int i = 0; i < magnitudes.length; i++) {
magnitudes[i] = Math.max(minValue, magnitudes[i]);
magnitudes[i] = (float) Math.log1p(magnitudes[i]);
maxSpectralEnergy = Math.max(magnitudes[i],
maxSpectralEnergy);
minSpectralEnergy = Math.min(magnitudes[i],
minSpectralEnergy);
}
}
minSpectralEnergy = Math.abs(minSpectralEnergy);
ConstantQLayer.this.features = fe;
}
public boolean process(AudioEvent audioEvent) {
fe.put(audioEvent.getTimeStamp() - constantQLag,
constantQ.getMagnitudes().clone());
return true;
}
});
new Thread(adp,"Constant Q Calculation").start();
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e2){
e2.printStackTrace();
}
}
@Override
public String getName() {
return "Constant-Q Layer";
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy