Package com.meapsoft

Source Code of com.meapsoft.Synthesizer

/*
*  Copyright 2006-2007 Columbia University.
*
*  This file is part of MEAPsoft.
*
*  MEAPsoft is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License version 2 as
*  published by the Free Software Foundation.
*
*  MEAPsoft 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 MEAPsoft; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
*  02110-1301 USA
*
*  See the file "COPYING" for the text of the license.
*/

package com.meapsoft;

import gnu.getopt.Getopt;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Vector;

import java.util.Date;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

/**
* Program that processes a MEAPsoft EDL file and synthesizes audio data from
* it. This supports audio playback or saving the audio data to a wav file.
*
* @author Ron Weiss (ronw@ee.columbia.edu)
*/
public class Synthesizer extends MEAPUtil
{
  EDLFile edlFile;

  // if outFile is null, sound will be output to the first available
  // audio mixer
  String outFile = null;

  AudioWriter audioWriter;

  // output buffer
  double[] outSamples;

  int outSamplesLength;

  // a vector of line listeners (for synching audio and things)
  // Vector<LineListener> m_kLineListeners;
  Vector m_kLineListeners;

  // We want to synthesize cd quality audio. Note that samples from
  // left and right channels will be interleaved.
  public static AudioFormat outputFormat = new AudioFormat(44100,
      bitsPerSamp, 2, signed, bigEndian);

  public Synthesizer(String infile, String outfile)
  {
    this(new EDLFile(infile), outfile);
  }

  public Synthesizer(EDLFile edl, String outfile)
  {
    // create the vector of line listeners
    // m_kLineListeners = new Vector<LineListener>();
    m_kLineListeners = new Vector();

    edlFile = edl;
    outFile = outfile;
  }

  public void addLineListener(LineListener kListener)
  {
    // add it if it does not already contain it
    if (!m_kLineListeners.contains(kListener))
    {
      m_kLineListeners.add(kListener);
    }
  }

  // \todo{test output to line out}
  public void printUsageAndExit()
  {
    System.out
        .println("Usage: Synthesizer [-options] file.edl \n\n"
            + "  where options include:\n"
            + "    -o output_file  output sound file (defaults to line out)\n"
            + "");
    System.exit(0);
  }

  /**
   * Synthesizer constructor. Parses command line arguments
   */
  public Synthesizer(String[] args)
  {
    if (args.length == 0)
      printUsageAndExit();

    // Parse arguments
    Getopt opt = new Getopt("Synthesizer", args, "o:");
    opt.setOpterr(false);

    int c = -1;
    while ((c = opt.getopt()) != -1)
    {
      switch (c)
      {
      case 'o':
        outFile = opt.getOptarg();
        break;
      case '?':
        printUsageAndExit();
        break;
      default:
        System.out.print("getopt() returned " + c + "\n");
      }
    }

    // parse arguments
    int ind = opt.getOptind();
    if (ind > args.length)
      printUsageAndExit();

    edlFile = new EDLFile(args[ind]);

    System.out.println("Synthesizing " + outFile + " from " + args[ind]
        + ".");
  }

  public void setup() throws IOException, ParserException
  {
    if (!edlFile.haveReadFile)
      edlFile.readFile();

    if (edlFile.chunks.size() == 0)
      throw new ParserException(edlFile.filename, "No chunks found");

    // keep track of our progress in synthesizing this EDLFile:
    progress.setMinimum(0);
    progress.setMaximum(edlFile.chunks.size());
    progress.setValue(0);

    // System.out.println("edlFile.chunks.size(): " +
    // edlFile.chunks.size());
  }

  public void processEDL() throws IOException, UnsupportedAudioFileException
  {
    // we need to go through the chunks in ascending order of dstTime:
    Collections.sort(edlFile.chunks);

    // create output samples
    // EDLChunk ch = (EDLChunk)edlFile.chunks.get(0);
    EDLChunk ch = (EDLChunk) edlFile.chunks.get(edlFile.chunks.size() - 1);
    // outSamplesLength = (int)(outputFormat.getSampleRate() * (ch.dstTime +
    // ch.length));
    outSamplesLength = (int) (outputFormat.getSampleRate()
        * outputFormat.getChannels() * (ch.dstTime + ch.length));

    // System.out.println("edlFile.chunks.size(): " +
    // edlFile.chunks.size());
    // System.out.println("ch.dstTime: " + ch.dstTime + " ch.length: " +
    // ch.length +
    // " ch total length: " + (ch.dstTime + ch.length) + " outSamplesLength:
    // " + outSamplesLength);

    outSamples = new double[outSamplesLength];
    // initialize outSamples properly
    Arrays.fill(outSamples, 0);

    Iterator i = edlFile.chunks.iterator();
    int overlapAccumulator = 0;
    while (i.hasNext())
    {
      EDLChunk currChunk = (EDLChunk) i.next();

      double[] chunkSamples = currChunk.getSamples(outputFormat);

      int offset = (int) (currChunk.dstTime
          * outputFormat.getSampleRate() * outputFormat.getChannels())
          - overlapAccumulator;

      for (int c = 0; c < currChunk.commands.size(); c++)
      {
        String cmd = (String) currChunk.commands.get(c);
        if (cmd.equalsIgnoreCase("reverse"))
        {
          // reverse the current chunk in time
          for (int x = 0; x < chunkSamples.length / 2; x++)
          {
            double tmp = chunkSamples[chunkSamples.length - x - 1];
            chunkSamples[chunkSamples.length - x - 1] = chunkSamples[x];
            chunkSamples[x] = tmp;
          }
        }
        // crossfade(time) (time in seconds) - add fade on
        // both ends of chunk (like the "fade" command) but
        // also overlaps it with the previous segment.
        // default overlap is 1ms;
        else if (cmd.toLowerCase().startsWith("crossfade"))
        {
          int overlap = (int) (.005 * outputFormat.getSampleRate() * outputFormat
              .getChannels());

          String[] overlapTime = cmd.split("[(),\\s]");

          if (overlapTime.length >= 2)
            overlap = (int) (outputFormat.getSampleRate()
                * outputFormat.getChannels() * Double
                .parseDouble(overlapTime[1]));

          if (offset - overlap > 0)
          {
            offset -= overlap;
            overlapAccumulator += overlap;
          }
        }
        // fade|crossfade(fadeInTime,fadeOutTime) (times in seconds)
        // if no arguments present defaults to 1ms fade in/out time
        // if 1 argument fadeInTime = fadeOutTime
        // \todo{crossfade is only allowed to have one
        // argument but this is not enforced}
        else if (cmd.toLowerCase().startsWith("crossfade")
            || cmd.toLowerCase().startsWith("fade"))
        {
          double fadeInTime = .005;
          double fadeOutTime = .005;

          String[] fadeTimes = cmd.split("[(),\\s]");

          if (fadeTimes.length == 2)
          {
            fadeInTime = Double.parseDouble(fadeTimes[1]);
            fadeOutTime = fadeInTime;
          }
          else if (fadeTimes.length > 2)
          {
            fadeInTime = Double.parseDouble(fadeTimes[1]);
            fadeOutTime = Double.parseDouble(fadeTimes[2]);
          }

          // Smooth out any rough edges to the data with a
          // simple triangular window on each end.
          int fadeInWinSize = (int) (fadeInTime
              * outputFormat.getSampleRate() * outputFormat
              .getChannels());
          for (int x = 0; x < fadeInWinSize; x++)
          {
            if (x < chunkSamples.length)
              chunkSamples[x] *= (double) x
                  / (double) fadeInWinSize;
          }

          int fadeOutWinSize = (int) (fadeOutTime
              * outputFormat.getSampleRate() * outputFormat
              .getChannels());
          for (int x = 0; x < fadeOutWinSize; x++)
          {
            if (chunkSamples.length - 1 - x > 0)
              chunkSamples[chunkSamples.length - 1 - x] *= (double) x
                  / (double) fadeOutWinSize;
          }
        }
        // gain(A) - scale the waveform by A.
        else if (cmd.toLowerCase().startsWith("gain"))
        {
          // scale the amplitudes
          String[] gainString = cmd.split("[(),\\s]");
          double gain = Double.parseDouble(gainString[1]);

          for (int x = 0; x < chunkSamples.length; x++)
            chunkSamples[x] *= gain;
        }
        else
          System.out.println("Ignored unknown command: " + cmd);
      }
      // copy the current chunk's samples into the output buffer
      for (int x = 0; x < chunkSamples.length; x++)
      {
        if (offset + x < outSamples.length)
          outSamples[offset + x] += chunkSamples[x];
        else
          System.out.println("ERROR: " + (offset + x) + " > "
              + outSamples.length);
      }

      progress.setValue(progress.getValue() + 1);
    }

    outSamplesLength -= overlapAccumulator;
  }

  /**
   * Set everything up, process input, and write output.
   */
  public void run()
  {
    try
    {
      doSynthesizer();
    }
    catch (Exception e)
    {
      exceptionHandler.handleException(e);
    }
  }

  /**
   * Stop a running Synthesizer.
   */
  public void stop()
  {
    try
    {
      // audioWriter.removeLineListeners(m_kLineListeners);
      audioWriter.close();
    }
    catch (IOException e)
    {
      exceptionHandler.handleException(e);
    }

    // null out our pointer
    audioWriter = null;
  }

  public void doSynthesizer() throws IOException, ParserException,
      UnsupportedAudioFileException, LineUnavailableException
  {
    setup();
    processEDL();

    // write out the audio data
    audioWriter = new AudioWriter(outFile, outputFormat);

    // add the line listeners to the audio writer.
    // don't do this if we're null or it will barf on command line operation
    if (m_kLineListeners != null)
      audioWriter.addLineListeners(m_kLineListeners);

    // write out the sample now
    audioWriter.write(outSamples, outSamplesLength);

    // //a desperate attempt to allow all samples to play back...
    // //all these threads are making my brain go crazy!!!!
    // //System.out.println("sleeping...");
    // try
    // {
    // Thread.sleep(4000);
    // }
    // catch (InterruptedException e)
    // {
    // e.printStackTrace();
    // }
    // //System.out.println("done sleeping, closing audioWriter.");

    // if we still have an audiowriter here, kill it
    if (audioWriter != null)
    {
      audioWriter.close();
    }
  }
 

  public static void main(String[] args)
  {
    Synthesizer o2or = new Synthesizer(args);
    long startTime = System.currentTimeMillis();
    o2or.verbose = true;
    o2or.run();
    System.out.println("Done.  Took "
        + ((System.currentTimeMillis() - startTime) / 1000.0) + "s");
    System.exit(0);
  }
}
TOP

Related Classes of com.meapsoft.Synthesizer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.