/** 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(); } } }