/** * Created : Jun 15, 2012 * * @author pquiring */ import java.awt.*; import java.io.*; import java.util.*; import javax.swing.*; import javaforce.*; import javaforce.media.*; public class ProjectPanel extends javax.swing.JPanel { /** * Creates new form ProjectPanel */ public ProjectPanel(String filename) { initComponents(); tracks.setLayout(new TracksLayout()); if (filename == null) { Random r = new Random(); this.path = Paths.getNewProjectPath(); new File(this.path).mkdirs(); new File(this.path + "/" + "undo").mkdirs(); new File(this.path + "/" + "cb").mkdirs(); //clipboard } else { this.filename = filename; int idx = filename.lastIndexOf("."); this.path = filename.substring(0, idx) + "_data"; loadTracks(); deleteUndo(); } JFLog.log("project:" + path); calcMaxLength(); timeLine.add(new TimeLine()); } /** * 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() { timeScroll = new javax.swing.JScrollBar(); tracks = new javax.swing.JPanel(); timeLine = new javax.swing.JPanel(); tracksScroll = new javax.swing.JScrollBar(); addComponentListener(new java.awt.event.ComponentAdapter() { public void componentResized(java.awt.event.ComponentEvent evt) { formComponentResized(evt); } }); timeScroll.setOrientation(javax.swing.JScrollBar.HORIZONTAL); timeScroll.addAdjustmentListener(new java.awt.event.AdjustmentListener() { public void adjustmentValueChanged(java.awt.event.AdjustmentEvent evt) { timeScrollAdjustmentValueChanged(evt); } }); javax.swing.GroupLayout tracksLayout = new javax.swing.GroupLayout(tracks); tracks.setLayout(tracksLayout); tracksLayout.setHorizontalGroup( tracksLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 0, Short.MAX_VALUE) ); tracksLayout.setVerticalGroup( tracksLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 675, Short.MAX_VALUE) ); timeLine.setLayout(new java.awt.GridLayout(1, 1)); tracksScroll.addAdjustmentListener(new java.awt.event.AdjustmentListener() { public void adjustmentValueChanged(java.awt.event.AdjustmentEvent evt) { tracksScrollAdjustmentValueChanged(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(timeLine, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(timeScroll, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 594, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addComponent(tracks, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(tracksScroll, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(timeLine, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(tracks, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(tracksScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 675, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(timeScroll, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); }// </editor-fold>//GEN-END:initComponents private void timeScrollAdjustmentValueChanged(java.awt.event.AdjustmentEvent evt) {//GEN-FIRST:event_timeScrollAdjustmentValueChanged setOffset(timeScroll.getValue()); }//GEN-LAST:event_timeScrollAdjustmentValueChanged private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized resizeTracks(); }//GEN-LAST:event_formComponentResized private void tracksScrollAdjustmentValueChanged(java.awt.event.AdjustmentEvent evt) {//GEN-FIRST:event_tracksScrollAdjustmentValueChanged tracks.doLayout(); }//GEN-LAST:event_tracksScrollAdjustmentValueChanged // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel timeLine; private javax.swing.JScrollBar timeScroll; private javax.swing.JPanel tracks; private javax.swing.JScrollBar tracksScroll; // End of variables declaration//GEN-END:variables public String filename; //project xml file (.sndxml) (may be null) public String path; //path to store project files public double scale = 128.0; //# px per second public double offset = 0.0; //in seconds public double selectStart = 0.0; //in seconds public double selectStop = 0.0; //in seconds public double maxLength = 10.0; //in seconds public volatile boolean paused = false; public final Object pausedLock = new Object(); public ArrayList<TrackPanel> list = new ArrayList<TrackPanel>(); public Config config = new Config(); public static class Config { public int no_options_yet; } public void loadConfig() { try { XML xml = new XML(); FileInputStream fis = new FileInputStream(filename); xml.read(fis); xml.writeClass(config); } catch (FileNotFoundException e1) { defaultConfig(); } catch (Exception e2) { JFLog.log(e2); defaultConfig(); } } public void defaultConfig() { config = new Config(); } public void saveConfig() { try { XML xml = new XML(); FileOutputStream fos = new FileOutputStream(filename); xml.readClass("audio", config); xml.write(fos); fos.close(); } catch (Exception e) { JFLog.log(e); } } public String getTabName() { int i1 = filename.lastIndexOf("/"); int i2 = filename.lastIndexOf("."); return filename.substring(i1+1, i2); } public int genTrackID() { int tid = list.size(); for(int a=0;a<list.size();) { if (list.get(a).tid == tid) {tid++; a = 0;} else {a++;} } return tid; } boolean use_transcoder = true; public void importFile(String fn) { // if (fn.toLowerCase().endsWith(".wav")) { // importWav(fn); //does not support all wav types (ie: non-PCM) // } else { if (use_transcoder) importFileTranscoder(fn); else importFileDecoder(fn); // } } public Transcoder transcoder; public boolean transcoderSuccess; public File transcoderOutFile; public String transcoderInFile; public void importFileTranscoder(String fn) { try { transcoder = new Transcoder(); transcoderOutFile = Paths.getTempFile("temp", ".wav"); transcoderInFile = fn; JFTask task = new JFTask() { public boolean work() { this.setProgress(-1); //indeterminate this.setTitle("Progress"); this.setLabel("Transcoding file..."); transcoderSuccess = transcoder.transcode(transcoderInFile, transcoderOutFile.getAbsolutePath() , "wav"); return true; } }; ProgressDialog dialog = new ProgressDialog(null, true, task); dialog.setAutoClose(true); dialog.setVisible(true); if (!transcoderSuccess) { transcoderOutFile.delete(); throw new Exception("error"); } importWav(transcoderOutFile.getAbsolutePath()); transcoderOutFile.delete(); } catch (Exception e) { JFLog.log(e); JF.showError("Error", "Import failed"); } } public void importFileDecoder(String fn) { try { File tempFile = Paths.getTempFile("temp", ".wav"); FileOutputStream fos = new FileOutputStream(tempFile); Decoder decoder = new Decoder(); decoder.decode(fn); //write wav header TrackPanel.writeWavHeader(fos, decoder.getChannels(), decoder.getSampleRate(), 16, 2, 0x7fffffff); do { short samples[] = decoder.getSamples(); if (samples == null) break; fos.write(LE.shortArray2byteArray(samples, null)); } while (true); fos.close(); importWav(tempFile.getAbsolutePath()); tempFile.delete(); } catch (Exception e) { JFLog.log(e); JF.showError("Error", "Import failed"); } } public void importWav(String fn) { //create a new TrackID # and convert WAV into managable files int tid = genTrackID(); Wav wav = new Wav(); if (!wav.load(fn)) { JF.showError("Error", wav.errmsg); return; } String trackPath = path + "/" + tid; File file = new File(trackPath); file.mkdirs(); TrackPanel track = new TrackPanel(this, tid, wav); wav.close(); list.add(track); tracks.add(track); resizeTracks(); validate(); } public void loadTracks() { File file = new File(path); File trackFolders[] = file.listFiles(); if (trackFolders == null) return; for(int a=0;a<trackFolders.length;a++) { String num = trackFolders[a].getName(); if (num.charAt(0) < '0') continue; if (num.charAt(0) > '9') continue; if (!trackFolders[a].isDirectory()) continue; loadTrack(JF.atoi(num)); } validate(); repaint(); } public void loadTrack(int tid) { TrackPanel track = new TrackPanel(this, tid); tracks.add(track); list.add(track); } public void close() { if (Paths.isTempPath(path)) { JFLog.log("deleting temp project:" + path); try {Paths.deleteFolderEx(path); } catch (Exception e) {} } } private class TimeLine extends JComponent { public void paint(Graphics g) { // JFLog.log("TimeLine:scale=" + scale); int x = getWidth(); int y = getHeight(); g.setColor(Color.GRAY); g.fillRect(0,0,x,y); double point_n = 1.0; double point_d = scale; if (point_d > 256.0) { while (point_d > 256.0) { point_n /= 2.0; point_d /= 2.0; } } else { while (point_d < 64.0) { point_n *= 2.0; point_d *= 2.0; } } double toff = offset; double toffDec = toff % point_n; int poff = (int)(-scale * toffDec); toff -= toffDec; int h = getHeight(); g.setColor(Color.BLACK); FontMetrics fm = this.getFontMetrics(this.getFont()); int fh = fm.getHeight(); String txt; while (poff < getWidth()) { int min = (int)(toff / 60.0); int sec = (int)(toff - (min * 60.0)); int sec100 = (int)((toff - (min * 60.0) - sec + .005) * 100.0); if (toff >= 60.0) { txt = String.format("%d:%02d:%02d", min, sec, sec100); } else { txt = String.format("%d:%02d", sec, sec100); } int fw = fm.stringWidth(txt) / 2; g.drawBytes(txt.getBytes(), 0, txt.length(), poff - fw, h/2 - 5); g.drawLine(poff,h/2,poff,h); if (selectStart >= toff && selectStart < toff + point_n) { int soff = poff + (int)((selectStart - toff) * scale); g.drawLine(soff, 0, soff, h); } if (selectStop >= toff && selectStop < toff + point_n) { int soff = poff + (int)((selectStop - toff) * scale); g.drawLine(soff, 0, soff, h); } toff += point_n; poff += point_d; } } public Dimension getPreferredSize() { return new Dimension(ProjectPanel.this.getWidth(), 32); } public Dimension getMaximumSize() { return getPreferredSize(); } } public void zoom(int dir) { switch (dir) { case -1: if (scale <= 0.25) return; scale /= 2.0; break; case 1: if (scale >= 32.0 * 1024.0) return; scale *= 2.0; break; } calcMaxLength(); repaint(); } public void setOffset(int value) { offset = value / scale; repaint(); } public void calcMaxLength() { maxLength = getWidth() / scale; for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); double trackLength = track.totalLength / track.rate; //in seconds if (trackLength > maxLength) maxLength = trackLength; } maxLength += 10.0f; //show a bit beyond the end // JFLog.log("maxLength=" + maxLength); timeScroll.setMinimum(0); timeScroll.setMaximum((int)(maxLength * scale)); // JFLog.log(" max=" + maxLength * scale); // timeScroll.setBlockIncrement(?); // JFLog.log("view=" + getWidth() / scale); timeScroll.setVisibleAmount(getWidth()); } public void autoZoom() { //make sure to call calcMaxLength() first //find scale that will show maxLength //TODO } public void record() { //create a new track and add samples to it paused = false; TrackPanel track = new TrackPanel(this, genTrackID(), Settings.current.channels, Settings.current.freq, 16); list.add(track); tracks.add(track); resizeTracks(); tracksScroll.setValue(totalTracksHeight()); // setRecVol(); track.record(); validate(); } public void stop() { paused = false; for(int a=0;a<list.size();a++) { list.get(a).stop(); } } public void stopped(int tid) { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.recording) return; if (track.playing) return; } //tracks all done MainPanel.main.stopped(); } public void play() { paused = false; // setPlayVol(); for(int a=0;a<list.size();a++) { list.get(a).play(); } } public void pause() { paused = !paused; synchronized(pausedLock) { pausedLock.notifyAll(); } } public void selectStart(double pos) { selectStart = pos; selectStop = selectStart; repaint(); } public void selectStop(double pos) { selectStop = pos; repaint(); } public void selectTrack(TrackPanel track, boolean mute) { //unselect all other tracks for(int a=0;a<list.size();a++) { TrackPanel otherTrack = list.get(a); if (track.tid != otherTrack.tid) otherTrack.unselectAll(); } MainPanel.main.selectTrack(track); } /** ensure offset (in seconds) is visible */ public void showOffset(long offset) { int value = timeScroll.getValue(); int newValue = (int)(offset * scale); int start = value; int end = (int)(value + getWidth()); if (newValue >= start && newValue <= end) return; timeScroll.setValue(newValue); } public void deleteTrack() { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) { if (track.playing || track.recording) return; try { Paths.deleteFolderEx(path + "/" + track.tid); } catch (Exception e) { JF.showError("Error", "Failed to delete track from project data folder"); JFLog.log(e); return; } list.remove(a); tracks.remove(track); selectStart = selectStop = 0.0; calcMaxLength(); resizeTracks(); gotoHome(); validate(); repaint(); return; } } } public void gotoHome() { showOffset(0); } public void gotoEnd() { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) { showOffset(track.totalLength); return; } } } public void mute() { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) { track.mute(); return; } } } public void unmute() { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) { track.unmute(); return; } } } public void solo() { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) { track.unmute(); } else { track.mute(); } } } public boolean isTrackSelected() { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) return true; } return false; } public void exportFile(String fn, boolean selection) { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) { track.exportFile(fn, selection); return; } } JF.showError("Error", "Select a track to export"); } public TrackPanel getSelectedTrack() { for(int a=0;a<list.size();a++) { TrackPanel track = list.get(a); if (track.selected) { return track; } } JF.showError("Error", "Select a track"); return null; } public void deleteUndo() { deleteFiles(path + "/undo/"); } public static void deleteClipboard() { deleteFiles(MainPanel.clipboardPath + "/"); } private static void deleteFiles(String path) { File files[] = new File(path).listFiles(); if (files == null) return; for(int a=0;a<files.length;a++) { files[a].delete(); } } private void resizeTracks() { // JFLog.log("resizeTracks"); tracksScroll.setMinimum(0); tracksScroll.setMaximum(totalTracksHeight()); tracksScroll.setVisibleAmount(tracks.getHeight()); } public int getTracksWidth() { return tracks.getWidth(); } private Dimension layoutSize; private class TracksLayout implements LayoutManager { // private Vector<Component> list = new Vector<Component>(); public void addLayoutComponent(String string, Component cmp) { // list.add(cmp); } public void removeLayoutComponent(Component cmp) { // list.remove(cmp); } public Dimension preferredLayoutSize(Container c) { if (layoutSize == null) layoutContainer(c); return layoutSize; } public Dimension minimumLayoutSize(Container c) { return new Dimension(1,1); } public void layoutContainer(Container c) { int cnt = c.getComponentCount(); int x = tracks.getWidth(); int cx = 0; int cy = -tracksScroll.getValue(); for(int a=0;a<cnt;a++) { Component child = c.getComponent(a); Dimension d = child.getPreferredSize(); child.setBounds(cx, cy, x, d.height); cy += d.height; } c.setPreferredSize(new Dimension(x, cy)); layoutSize = new Dimension(x, cy); } } public void undo() { getSelectedTrack().undo(); } public void mixTracks() { if (list.size() < 2) { JF.showError("Error", "Require at least two tracks to mix"); return; } MixTracks dialog = new MixTracks(null, true, this, list); dialog.setVisible(true); if (!dialog.accepted) return; } public int totalTracksHeight() { int height = 0; for(int a=0;a<list.size();a++) { height += list.get(a).channels * 127; } return height; } public void newTrack(int chs) { int tid = genTrackID(); String trackPath = path + "/" + tid; File file = new File(trackPath); file.mkdirs(); TrackPanel track = new TrackPanel(this, tid, chs, Settings.current.freq, 16); list.add(track); tracks.add(track); resizeTracks(); validate(); } }