/* * Created on Mar 2, 2007 * * Copyright (c) 2006-2007 P.J.Leonard * * http://www.frinika.com * * This file is part of Frinika. * * Frinika is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Frinika is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.frinika.sequencer.model; import com.frinika.audio.toot.AudioPeakMonitor; import static com.frinika.localization.CurrentLocale.getMessage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import javax.sound.sampled.AudioFormat; import javax.swing.Icon; import javax.swing.JFrame; import javax.swing.JProgressBar; import uk.org.toot.audio.core.AudioBuffer; import uk.org.toot.audio.core.AudioProcess; import uk.org.toot.audio.core.AudioBuffer.MetaInfo; import uk.org.toot.audio.mixer.MixControls; import uk.org.toot.audio.server.AudioServer; import com.frinika.project.FrinikaAudioSystem; import com.frinika.global.FrinikaConfig; import com.frinika.project.MidiDeviceDescriptor; import com.frinika.project.ProjectContainer; import com.frinika.project.gui.ProjectFrame; import com.frinika.sequencer.FrinikaSequence; import com.frinika.sequencer.FrinikaSequencer; import com.frinika.sequencer.gui.mixer.MidiDeviceIconProvider; import com.frinika.sequencer.gui.mixer.SynthWrapper; import com.frinika.audio.io.AudioWriter; public class SynthLane extends Lane implements RecordableLane { /** * */ private static final long serialVersionUID = 1L; MidiDeviceDescriptor midiDeviceDescriptor = null; private boolean isRendered = false; // transient private SynthWrapper synthWrapper; transient AudioProcess audioSynthProcess; // audio input transient AudioProcess audioProcess; transient AudioPeakMonitor peakMonitor; transient File clipFile = null; static int stripNo = 0; transient private MixControls mixerControls = null; transient private boolean isRendering = false; transient boolean isInstalled = false; public SynthLane() { } public SynthLane(ProjectContainer project, MidiDeviceDescriptor desc) { super(null, project); install(desc); channelLabel = new MetaInfo(getName()); } public void attachAudioProcessToMixer() { // System .out.println(" ATTACHING TO MIXER "); peakMonitor = new AudioPeakMonitor(); audioProcess = new AudioProcess() { public void close() { } public void open() { } public int processAudio(AudioBuffer buffer) { if (!isInstalled) return AUDIO_OK; // audioSynthProcess.processAudio(buffer); if (!isRendered) audioSynthProcess.processAudio(buffer); else { buffer.makeSilence(); if (!parts.isEmpty() && parts.get(0) != null) { ((AudioPart) (parts.get(0))).getAudioProcess() .processAudio(buffer); } } peakMonitor.processAudio(buffer); buffer.setMetaInfo(channelLabel); return AUDIO_OK; } }; try { mixerControls = project.addMixerInput(audioProcess, (stripNo++) + "X"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /* * (non-Javadoc) * * @see com.frinika.sequencer.model.Lane#getName() @ */ public String getName() { return midiDeviceDescriptor.getProjectName(); // return "SYnthLane"; // TODO synthWrapper.getName(); } public void setName(String name) { if (name != null && midiDeviceDescriptor != null) // evasive null // pointer if midiDeviceDescriptor.setProjectName(name); ProjectFrame frame = ProjectFrame.findFrame(project); if (frame != null) frame.getMidiDevicesPanel().updateDeviceTabs(); channelLabel = new MetaInfo(name); } public void removeFromModel() { ProjectFrame frame = ProjectFrame.findFrame(project); if (frame != null) { frame.getMidiDevicesPanel().remove( getMidiDescriptor().getMidiDevice()); frame.getMidiDevicesPanel().updateDeviceTabs(); } super.removeFromModel(); } public Selectable deepCopy(Selectable parent) { // TODO Auto-generated method stub return null; } public void deepMove(long tick) { // TODO Auto-generated method stub } public void restoreFromClone(EditHistoryRecordable object) { // TODO Auto-generated method stub } public boolean install(MidiDeviceDescriptor desc) { if (midiDeviceDescriptor != null && midiDeviceDescriptor != desc) { try { throw new Exception( " Synthlane already attached to a different device "); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } midiDeviceDescriptor = desc; if (midiDeviceDescriptor.getMidiDevice() instanceof SynthWrapper) { audioSynthProcess = ((SynthWrapper) midiDeviceDescriptor .getMidiDevice()).getAudioProcess(); } else { // System.err.println(" Whatever TODO"); // audioSynthProcess = new AudioProcess() { // // public void close() { // // TODO Auto-generated method stub // // } // // public void open() { // // TODO Auto-generated method stub // // } // // public int processAudio(AudioBuffer buffer) { // System. out.println(" ... "); // TODO Auto-generated method stub // return AUDIO_OK; // } // // }; try { throw new Exception(" SynthLane can not attach to " + midiDeviceDescriptor.getMidiDevice()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } isInstalled = true; if (!parts.isEmpty()) try { parts.get(0).onLoad(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return true; } private File getAudioFile() { if (clipFile != null) return clipFile; if (!parts.isEmpty()) { AudioPart part = (AudioPart) parts.get(0); clipFile = part.getAudioFile(); return clipFile; } ProjectContainer proj = getProject(); File audioDir = proj.getAudioDirectory(); String audioFileName = getName() + ".wav"; clipFile = new File(audioDir, audioFileName); int cnt = 1; while (clipFile.exists()) { audioFileName = getName() + "_" + (cnt++) + ".wav"; clipFile = new File(audioDir, audioFileName); } return clipFile; } private void setAudioFile(File clipFile, double startTimeInMicros) { if (!parts.isEmpty()) { remove(parts.get(0)); } if (clipFile != null) { AudioPart newPart; try { newPart = new AudioPart(this, clipFile, startTimeInMicros); newPart.onLoad(); } catch (FileNotFoundException e) { e.printStackTrace(); } } project.getEditHistoryContainer().notifyEditHistoryListeners(); } public double getMonitorValue() { if (peakMonitor == null) return 0; return peakMonitor.getPeak(); } public void setMute(boolean b) { if (mixerControls == null) return; mixerControls.getMuteControl().setValue(b); } public void setSolo(boolean b) { if (mixerControls == null) return; mixerControls.getSoloControl().setValue(b); } public boolean isRecording() { if (mixerControls == null) return false; return isRendered; } public boolean isSynthesizer() { return mixerControls != null; } /** * Steal the recording logic (recording == rendered) */ public void setRecording(boolean b) { if (mixerControls == null) return; if (isRendering) return; if (b == isRendered) return; if (b) { isRendering = true; SynthRenderer renderer = new SynthRenderer(); Thread t = new Thread(renderer); t.start(); } else { isRendered = false; setAudioFile(null, 0); } } public MixControls getMixerControls() { return mixerControls; } public boolean isMute() { if (mixerControls == null) return false; return mixerControls.getMuteControl().getValue(); } public boolean isSolo() { if (mixerControls == null) return false; return mixerControls.getSoloControl().getValue(); } class SynthRenderer implements Runnable { /** * */ private static final long serialVersionUID = 1L; FrinikaSequencer sequencer; FrinikaSequence sequence; JProgressBar bar; JFrame frame; double sampleRate; long currentPos; long startTick; long stopTick; public void run() { sequencer = project.getSequencer(); sequence = project.getSequence(); int ticksPerbeat=sequence.getResolution(); currentPos = sequencer.getTickPosition(); startTick = sequencer.getLoopStartPoint(); stopTick = sequencer.getLoopEndPoint(); frame = new JFrame(); bar = new JProgressBar(0, (int) (stopTick - startTick)); frame.setContentPane(bar); frame.pack(); frame.setVisible(true); project.getEditHistoryContainer().mark( getMessage("sequencer.project.render_synth")); sampleRate = FrinikaConfig.sampleRate; double samplesPerTick = samplesPerTick(); int samplesPerFrame = 128; AudioFormat format = new AudioFormat(FrinikaConfig.sampleRate, 16, 2, true, false); AudioServer server = FrinikaAudioSystem.getAudioServer(); File clipFile = getAudioFile(); AudioWriter writer; try { writer = new AudioWriter(clipFile, format); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); if (frame != null) frame.dispose(); return; } final AudioBuffer buffer = new AudioBuffer("renderbuf", 2, samplesPerFrame, (float) sampleRate); samplesPerFrame = buffer.getSampleCount(); sequencer.setTickPosition(startTick); // Stop the server calling the mixer. project.getAudioServer().stealAudioServer(this, null); server.stop(); sequencer.setRealtime(false); sequencer.start(); double samplesRendered = 0.0; double samplesTarget = 0.0; do { sequencer.nonRealtimeNextTick(); samplesTarget += samplesPerTick(); // samplesPerTick; while (samplesRendered < samplesTarget) { audioSynthProcess.processAudio(buffer); writer.processAudio(buffer); samplesRendered += samplesPerFrame; } bar.setValue((int) (sequencer.getTickPosition() - startTick)); } while (sequencer.getTickPosition() < stopTick); sequencer.stop(); sequencer.setRealtime(true); sequencer.setTickPosition(currentPos); writer.close(); double startTime=project.getTempoList().getTimeAtTick(startTick); // setAudioFile(clipFile, (long) (startTick * 1000000.0 // * samplesPerTick / sampleRate)); setAudioFile(clipFile, startTime*1000000.0); project.getEditHistoryContainer().notifyEditHistoryListeners(); server.start(); project.getAudioServer().returnAudioServer(this); isRendering = false; isRendered = true; if (frame != null) frame.dispose(); } double samplesPerTick() { double ticksPerSecond = (sequence.getResolution() * sequencer .getTempoInBPM()) / 60.0; double seconds = 1.0 / ticksPerSecond; return (seconds * sampleRate); } } public MidiDeviceDescriptor getMidiDescriptor() { return midiDeviceDescriptor; } @Override public Part createPart() { try { throw new Exception(" Attempt to create an SynthlanePart"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub return null; } @Override public Icon getIcon() { return ((MidiDeviceIconProvider) getMidiDescriptor().getMidiDevice()).getIcon(); } }