Package net.sf.fmj.media.multiplexer.audio

Source Code of net.sf.fmj.media.multiplexer.audio.CsvAudioMux

package net.sf.fmj.media.multiplexer.audio;

import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.Buffer;
import javax.media.Format;
import javax.media.ResourceUnavailableException;
import javax.media.format.AudioFormat;
import javax.media.protocol.ContentDescriptor;

import net.sf.fmj.media.multiplexer.AbstractInputStreamMux;
import net.sf.fmj.utility.FormatArgUtils;
import net.sf.fmj.utility.LoggerSingleton;

import com.lti.utils.UnsignedUtils;

/**
* Experimental mux for dumping out audio to a delimited text file.  Useful for debugging endian/sign/conversion problems.
* @author Ken Larson
*
*/
public class CsvAudioMux extends AbstractInputStreamMux
{
  private static final Logger logger = LoggerSingleton.logger;

  public CsvAudioMux()
  {
    super(new ContentDescriptor("audio.csv"));
  }

  @Override
  public Format[] getSupportedInputFormats()
  {
    // TODO: we accept anything, really, as long as it is in a byte array
    return new Format[] {
        new AudioFormat(AudioFormat.LINEAR, -1.0, -1, -1, -1, -1, -1, -1.0, Format.byteArray),
      };
  }
 
  @Override
  public Format setInputFormat(Format format, int trackID)
  {
    logger.finer("setInputFormat " + format + " " + trackID);
   
    boolean match = false;
    for (Format supported : getSupportedInputFormats())
    {
      if (format.matches(supported))
      {  match = true;
        break;
      }
    }
    if (!match)
    {
      logger.warning("Input format does not match any supported input format: " + format);
      return null;
    }
    if (inputFormats != null// TODO: should we save this somewhere and apply once inputFormats is not null?
      inputFormats[trackID] = format;
   
    return format;
  }

 
  private void outputHeader(OutputStream os) throws IOException
  {
    // TODO: this violates the CSV format
    os.write(FormatArgUtils.toString(inputFormats[0]).getBytes());
    os.write("\n".getBytes());
   
  }
 
  private void outputTrailer(OutputStream os) throws IOException
  {
  }
 
  private boolean headerWritten = false;
  private boolean trailerWritten = false;

  public void open() throws ResourceUnavailableException
  {
    super.open();
   
    if (!headerWritten)
    {  try
      {
        outputHeader(getOutputStream());
      } catch (IOException e)
      {
        logger.log(Level.WARNING, "" + e, e);
        throw new ResourceUnavailableException("" + e);
      }
      headerWritten = true;
    }
  }
 
  @Override
  public void close()
  {
    if (!trailerWritten)
    {  try
      {
        outputTrailer(getOutputStream());
      } catch (IOException e)
      {
        logger.log(Level.WARNING, "" + e, e);
        throw new RuntimeException(e);
      }
      trailerWritten = true;
    }
   
   
    super.close();
  }

  @Override
  protected void doProcess(Buffer buffer, int trackID, OutputStream os) throws IOException
  {
    if (!headerWritten)
    {  outputHeader(os);
      headerWritten = true;
    }
    if (buffer.isEOM())
    { 
      if (!trailerWritten)
      {  outputTrailer(os);
        trailerWritten = true;
      }
      os.close();
      return;    // TODO: what if there is data in buffer?
    }
   
    if (buffer.isDiscard())
      return;

    audioBufferToStream((AudioFormat) inputFormats[0], buffer, os);
   
  }
 
  public static void audioBufferToStream(AudioFormat f, Buffer buffer, OutputStream os) throws IOException
  {
    final byte[] data = (byte[]) buffer.getData();
   
    final int sampleSizeInBytes = f.getSampleSizeInBits() / 8;
    if (sampleSizeInBytes * 8 != f.getSampleSizeInBits())
      throw new RuntimeException("Sample size in bytes must be divisible by 8");
   
    final int frameSizeInBytes = sampleSizeInBytes * f.getChannels()// TODO: check f.getFrameSizeInBits();
   
    final int framesInBuffer = buffer.getLength() / frameSizeInBytes;
    if (buffer.getLength() != framesInBuffer * frameSizeInBytes)
      throw new RuntimeException("Length of buffer not an integral number of samples");
   
    // if getSampleSizeInBits is 8, this value is 255:
    final long inputUnsignedMax = (1L << f.getSampleSizeInBits()) - 1;
    // if getSampleSizeInBits is 8, this value is 127:
    final long inputSignedMax = (1L << (f.getSampleSizeInBits() - 1)) - 1;
   
    for (int frame = 0; frame < framesInBuffer; ++frame)
    {
      for (int channel = 0; channel < f.getChannels(); ++channel)
      {
        final int offset = buffer.getOffset() + frame * frameSizeInBytes + channel * sampleSizeInBytes;
        final int inputSampleLiteral = getSample(data, offset, sampleSizeInBytes, f.getEndian());
        final long inputSampleLongWithoutSign = UnsignedUtils.uIntToLong(inputSampleLiteral);
            final long inputSampleLongWithSign;
            if (f.getSigned() == AudioFormat.UNSIGNED)
              inputSampleLongWithSign = inputSampleLongWithoutSign;
            else if (f.getSigned() == AudioFormat.SIGNED)
            {
              if (inputSampleLongWithoutSign > inputSignedMax)
                inputSampleLongWithSign = inputSampleLongWithoutSign - inputUnsignedMax - 1;
              else
                inputSampleLongWithSign = inputSampleLongWithoutSign;
            }
            else
              throw new RuntimeException("input format signed not specified");
           
        if (channel > 0)
          os.write(",".getBytes());
        os.write(("" + inputSampleLongWithSign).getBytes());
      }
      os.write("\n".getBytes());
    }
  }
 
 
  // TODO: copied from RateConverter
  /** bit-wise literal.  Is in general unsigned, but may be signed if all 32 bits are used. */
  private static int getSample(byte[] inputBufferData, int byteOffsetOfSample, int inputSampleSizeInBytes, int inputEndian)
  {
         int sample = 0;
      for (int j = 0; j < inputSampleSizeInBytes; ++j)
      {
        // offset within sample handles endian-ness:
        final int offsetWithinSample = inputEndian == AudioFormat.BIG_ENDIAN ? j : (inputSampleSizeInBytes - 1 - j);
        final byte b = inputBufferData[byteOffsetOfSample + offsetWithinSample];
        sample <<= 8;
        sample |= b & 0xff;
        }
      // handle signed-ness.
      return sample;
 
}
TOP

Related Classes of net.sf.fmj.media.multiplexer.audio.CsvAudioMux

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.