package be.tarsos.dsp.example; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.lang.reflect.InvocationTargetException; import javax.sound.sampled.LineUnavailableException; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import be.tarsos.dsp.AudioDispatcher; import be.tarsos.dsp.AudioEvent; import be.tarsos.dsp.AudioProcessor; import be.tarsos.dsp.PitchShifter; import be.tarsos.dsp.io.jvm.AudioDispatcherFactory; import be.tarsos.dsp.io.jvm.AudioPlayer; import be.tarsos.dsp.resample.Resampler; public class TimeStrechingBasedOnPitchShifting extends JFrame implements TarsosDSPDemo { /** * */ private static final long serialVersionUID = -7188163235158960778L; private final JFileChooser fileChooser; private final JSlider factorSlider; private final JLabel factorLabel; private double currentFactor = 1.2;// pitch shift factor private AudioDispatcher dispatcher; private PitchShifter pitchShifter; private float[] buffer; private ChangeListener parameterSettingChangedListener = new ChangeListener(){ @Override public void stateChanged(ChangeEvent arg0) { currentFactor = factorSlider.getValue() / 100.0; factorLabel.setText("Factor " + Math.round(currentFactor * 100) + "%"); if (TimeStrechingBasedOnPitchShifting.this.dispatcher != null) { pitchShifter.setPitchShiftFactor((float) currentFactor); } }}; public TimeStrechingBasedOnPitchShifting(){ this.setLayout(new BorderLayout()); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setTitle("Pitch shifting: change the tempo of your audio."); currentFactor = 1.; JPanel fileChooserPanel = new JPanel(new BorderLayout()); fileChooserPanel.setBorder(new TitledBorder("1... Or choose your audio (wav mono)")); fileChooser = new JFileChooser(); JButton chooseFileButton = new JButton("Choose a file..."); chooseFileButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { int returnVal = fileChooser.showOpenDialog(TimeStrechingBasedOnPitchShifting.this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = fileChooser.getSelectedFile(); startFile(file); } else { //canceled } } }); fileChooserPanel.add(chooseFileButton); fileChooser.setLayout(new BoxLayout(fileChooser, BoxLayout.PAGE_AXIS)); this.add(fileChooserPanel,BorderLayout.NORTH); JPanel params = new JPanel(new BorderLayout()); params.setBorder(new TitledBorder("2. Set the algorithm parameters")); factorSlider = new JSlider(20, 250); factorSlider.setValue((int) (currentFactor*100)); factorSlider.setPaintLabels(true); factorSlider.addChangeListener(parameterSettingChangedListener); JLabel label = new JLabel("Factor 100%"); label.setText("Factor " + Math.round(currentFactor * 100) + "%"); label.setToolTipText("The tempo factor in % (100 is no change, 50 is double tempo, 200 half)."); factorLabel = label; params.add(label,BorderLayout.NORTH); params.add(factorSlider,BorderLayout.CENTER); this.add(params,BorderLayout.CENTER); } private void startFile(File file) { final int size = 2048; final int overlap = 2048 - 128; int samplerate = 44100; final AudioDispatcher d = AudioDispatcherFactory.fromPipe(file.getAbsolutePath(), samplerate, size, overlap); pitchShifter = new PitchShifter(1.0/currentFactor, samplerate, size, overlap); d.addAudioProcessor(new AudioProcessor() { @Override public void processingFinished() { // TODO Auto-generated method stub } @Override public boolean process(AudioEvent audioEvent) { buffer = audioEvent.getFloatBuffer(); return true; } }); d.addAudioProcessor(pitchShifter); d.addAudioProcessor(new AudioProcessor() { Resampler r= new Resampler(false,0.1,4.0); @Override public void processingFinished() { } @Override public boolean process(AudioEvent audioEvent) { float factor = (float) (currentFactor); float[] src = audioEvent.getFloatBuffer(); float[] out = new float[(int) ((size-overlap) * factor)]; r.process(factor, src, overlap,size-overlap, false, out, 0, out.length); //The size of the output buffer changes (according to factor). d.setStepSizeAndOverlap(out.length, 0); audioEvent.setFloatBuffer(out); audioEvent.setOverlap(0); return true; } }); //d.addAudioProcessor(rateTransposer); try { d.addAudioProcessor(new AudioPlayer(d.getFormat())); } catch (LineUnavailableException e) { e.printStackTrace(); } d.addAudioProcessor(new AudioProcessor() { @Override public void processingFinished() { } @Override public boolean process(AudioEvent audioEvent) { d.setStepSizeAndOverlap(size, overlap); d.setAudioFloatBuffer(buffer); audioEvent.setFloatBuffer(buffer); audioEvent.setOverlap(overlap); return true; } }); dispatcher = d ; new Thread(d).start(); } public static void main(String... args){ new TimeStrechingBasedOnPitchShifting().start(); } @Override public String name() { return "TimeStrechingBasedOnPitchShifting"; } @Override public String description() { return "Shows how to do time stretching by pitch shifting and resampling"; } @Override public void start() { try { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { //ignore failure to set default look en feel; } JFrame frame = new TimeStrechingBasedOnPitchShifting(); frame.pack(); frame.setSize(400,450); frame.setVisible(true); } }); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }