/** Resample track
*
* Created : Apr 23, 2014
*
* @author pquiring
*/
import java.io.*;
import javaforce.*;
public class FxResample extends javax.swing.JDialog {
/**
* Creates new form FxResample
*/
public FxResample(java.awt.Frame parent, boolean modal, TrackPanel track) {
super(parent, modal);
initComponents();
JF.centerWindow(this);
this.track = track;
freq.setSelectedItem("" + track.rate);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
apply = new javax.swing.JButton();
cancel = new javax.swing.JButton();
jLabel2 = new javax.swing.JLabel();
freq = new javax.swing.JComboBox();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle("Resample");
setResizable(false);
apply.setText("Apply");
apply.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
applyActionPerformed(evt);
}
});
cancel.setText("Cancel");
cancel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelActionPerformed(evt);
}
});
jLabel2.setText("Frequency:");
freq.setEditable(true);
freq.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "8000", "11025", "16000", "22050", "32000", "44100", "48000", "64000", "96000" }));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 132, Short.MAX_VALUE)
.addComponent(cancel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(apply))
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(freq, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(freq, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(apply)
.addComponent(cancel))
.addContainerGap())
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void cancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelActionPerformed
dispose();
}//GEN-LAST:event_cancelActionPerformed
private void applyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_applyActionPerformed
apply();
dispose();
}//GEN-LAST:event_applyActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton apply;
private javax.swing.JButton cancel;
private javax.swing.JComboBox freq;
private javax.swing.JLabel jLabel2;
// End of variables declaration//GEN-END:variables
private TrackPanel track;
private void apply() {
int newFreq = JF.atoi((String)freq.getSelectedItem());
if (newFreq < 8000) newFreq = 8000;
if (newFreq > 96000) newFreq = 96000;
if (newFreq == track.rate) {
//no change
dispose();
return;
}
track.selectAll();
track.createModifyUndo();
String toPath = track.project.path + "/temp";
new File(toPath).mkdir();
double step = ((double)track.rate) / ((double)newFreq);
FileOutputStream fos;
long totalLength = 0;
try {
long length = track.totalLength;
long offset = 0;
double foffset = 0.0;
double coffset = 0;
int cid[] = new int[track.channels];
int last[] = new int[track.channels];
ByteArrayOutputStream baos[] = new ByteArrayOutputStream[track.channels];
for(int ch=0;ch<track.channels;ch++) {
baos[ch] = new ByteArrayOutputStream();
}
int size[] = new int[track.channels];
short samples16[] = null;
int samples32[] = null;
int pos, sample = 0;
double s1, s2, m1, m2;
while (length > 0) {
int read = track.maxChunkSize;
if (read > length) read = (int)length;
for(int ch=0;ch<track.channels;ch++) {
coffset = foffset;
byte samples[] = track.getSamples(offset, read, ch);
pos = 0;
switch (track.bits) {
case 16: samples16 = LE.byteArray2shortArray(samples, null); break;
case 32: samples32 = LE.byteArray2intArray(samples, null); break;
}
while (pos < read-1) {
if (coffset == 0.0) {
switch (track.bits) {
case 16: sample = samples16[pos]; break;
case 32: sample = samples32[pos]; break;
}
} else if (coffset < 0.0) {
//interpolate between pos-1(last) and pos
coffset += 1.0;
m1 = 1.0 - coffset;
m2 = coffset;
coffset -= 1.0;
switch (track.bits) {
case 16:
s1 = last[ch];
s2 = samples16[pos];
sample = (int)((s1 * m1) + (s2 * m2));
break;
case 32:
s1 = last[ch];
s2 = samples32[pos];
sample = (int)((s1 * m1) + (s2 * m2));
break;
}
} else {
//interpolate between pos and pos+1
m1 = 1.0 - coffset;
m2 = coffset;
switch (track.bits) {
case 16:
s1 = samples16[pos];
s2 = samples16[pos+1];
sample = (int)((s1 * m1) + (s2 * m2));
break;
case 32:
s1 = samples32[pos];
s2 = samples32[pos+1];
sample = (int)((s1 * m1) + (s2 * m2));
break;
}
}
switch (track.bits) {
case 16:
baos[ch].write(sample & 0xff);
sample >>= 8;
baos[ch].write(sample & 0xff);
break;
case 32:
baos[ch].write(sample & 0xff);
sample >>= 8;
baos[ch].write(sample & 0xff);
sample >>= 8;
baos[ch].write(sample & 0xff);
sample >>= 8;
baos[ch].write(sample & 0xff);
break;
}
size[ch]++;
coffset += step;
while (coffset >= 1.0) {
coffset -= 1.0;
pos++;
}
if (size[ch] == track.maxChunkSize) {
if (ch == 0) totalLength += track.maxChunkSize;
fos = new FileOutputStream(toPath + "/c" + cid[ch] + "-" + ch + ".dat");
TrackPanel.ChunkHeader chunk = new TrackPanel.ChunkHeader();
chunk.cid = cid[ch]++;
chunk.length = size[ch];
chunk.next_cid = cid[ch];
chunk.write(fos);
fos.write(baos[ch].toByteArray());
fos.close();
size[ch] = 0;
baos[ch].reset();
}
}
switch (track.bits) {
case 16: last[ch] = samples16[read-1]; break;
case 32: last[ch] = samples32[read-1]; break;
}
}
foffset = coffset - 1.0;
length -= read;
}
for(int ch=0;ch<track.channels;ch++) {
if (size[ch] > 0) {
if (ch == 0) totalLength += size[ch];
fos = new FileOutputStream(toPath + "/c" + cid[ch] + "-" + ch + ".dat");
TrackPanel.ChunkHeader chunk = new TrackPanel.ChunkHeader();
chunk.cid = cid[ch]++;
chunk.length = size[ch];
chunk.next_cid = 0;
chunk.write(fos);
fos.write(baos[ch].toByteArray());
fos.close();
size[ch] = 0;
baos[ch].reset();
} else {
//need to patch last chunk (clear next_cid)
RandomAccessFile raf = new RandomAccessFile(toPath + "/c" + (cid[ch]-1) + "-" + ch + ".dat", "rw");
raf.seek(4); //skip length
raf.write(new byte[4]); //write zero next_cid
raf.close();
}
}
//write clip header
fos = new FileOutputStream(toPath + "/clip.dat");
TrackPanel.ClipHeader clip = new TrackPanel.ClipHeader();
clip.offset = 0;
clip.length = totalLength;
clip.tid = track.tid;
clip.channels = track.channels;
clip.bits = track.bits;
clip.bytes = track.bytes;
clip.rate = newFreq;
clip.write(fos);
fos.close();
} catch (Exception e) {
JFLog.log(e);
dispose();
return;
}
track.delete(false);
track.undoRate = track.rate;
track.rate = newFreq;
track.paste(toPath, false);
track.writeMainHeader();
//now delete /temp contents
File files[] = new File(toPath).listFiles();
for(int a=0;a<files.length;a++) {
files[a].delete();
}
}
}