mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#30 - WIP Streaming audio support. Adds Shoutcast V1 handler.
This commit is contained in:
319
src/record/mp3/MP3Recorder.java
Normal file
319
src/record/mp3/MP3Recorder.java
Normal file
@@ -0,0 +1,319 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package record.mp3;
|
||||
|
||||
import audio.AudioPacket;
|
||||
import audio.IAudioPacketListener;
|
||||
import audio.convert.MP3AudioConverter;
|
||||
import module.Module;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import record.wave.AudioPacketMonoWaveReader;
|
||||
import sample.Listener;
|
||||
import util.TimeStamp;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* MP3 recorder for converting 8 kHz PCM audio packets to MP3 and writing to .mp3 file.
|
||||
*/
|
||||
public class MP3Recorder extends Module implements Listener<AudioPacket>, IAudioPacketListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MP3Recorder.class);
|
||||
|
||||
private MP3AudioConverter mMP3Converter;
|
||||
private FileOutputStream mFileOutputStream;
|
||||
private String mFilePrefix;
|
||||
private Path mPath;
|
||||
|
||||
private BufferProcessor mBufferProcessor;
|
||||
private ScheduledFuture<?> mProcessorHandle;
|
||||
private LinkedBlockingQueue<AudioPacket> mAudioPacketQueue = new LinkedBlockingQueue<>(500);
|
||||
private long mLastBufferReceived;
|
||||
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
|
||||
/**
|
||||
* MP3 audio recorder module for converting audio packets to 16 kHz constant bit rate MP3 format and
|
||||
* recording to a file.
|
||||
*/
|
||||
public MP3Recorder(String filePrefix)
|
||||
{
|
||||
mFilePrefix = filePrefix;
|
||||
|
||||
int mp3BitRate = 16; //16 kHz
|
||||
boolean variableBitRate = false; //CBR
|
||||
mMP3Converter = new MP3AudioConverter(mp3BitRate, variableBitRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp of when the latest buffer was received by this recorder
|
||||
*/
|
||||
public long getLastBufferReceived()
|
||||
{
|
||||
return mLastBufferReceived;
|
||||
}
|
||||
|
||||
public Path getPath()
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts this recorder using as a scheduled thread broadcast running under the executor argument
|
||||
*
|
||||
* @param executor to use in scheduling MP3 conversion and file writes.
|
||||
*/
|
||||
public void start(ScheduledExecutorService executor)
|
||||
{
|
||||
if (mRunning.compareAndSet(false, true))
|
||||
{
|
||||
if (mBufferProcessor == null)
|
||||
{
|
||||
mBufferProcessor = new BufferProcessor();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(mFilePrefix);
|
||||
sb.append("_");
|
||||
sb.append(TimeStamp.getLongTimeStamp("_"));
|
||||
sb.append(".mp3");
|
||||
|
||||
mPath = Paths.get(sb.toString());
|
||||
mFileOutputStream = new FileOutputStream(mPath.toFile());
|
||||
|
||||
/* Schedule the handler to run every second */
|
||||
mProcessorHandle = executor.scheduleAtFixedRate(mBufferProcessor, 0, 200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (IOException io)
|
||||
{
|
||||
mLog.error("Error starting real buffer recorder", io);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the recorder.
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
mRunning.set(false);
|
||||
|
||||
if (mProcessorHandle != null)
|
||||
{
|
||||
mProcessorHandle.cancel(true);
|
||||
}
|
||||
|
||||
mProcessorHandle = null;
|
||||
|
||||
processAudioPacketQueue();
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the recording file.
|
||||
*/
|
||||
private void close()
|
||||
{
|
||||
if (mFileOutputStream != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mFileOutputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
mLog.error("Error closing output stream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary data insert method designed to accept a stream of audio packets and a final ending audio packet.
|
||||
*
|
||||
* Audio packets received before the recorder is started or after the recorder is stopped will be ignored.
|
||||
*/
|
||||
@Override
|
||||
public void receive(AudioPacket audioPacket)
|
||||
{
|
||||
if (mRunning.get())
|
||||
{
|
||||
boolean success = mAudioPacketQueue.offer(audioPacket);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
mLog.error("recorder buffer overflow - purging [" + mPath.toFile().getAbsolutePath() + "]");
|
||||
|
||||
mAudioPacketQueue.clear();
|
||||
}
|
||||
|
||||
mLastBufferReceived = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IAudioPacketListener interface method.
|
||||
*/
|
||||
@Override
|
||||
public Listener<AudioPacket> getAudioPacketListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes this audio recorder and prepares it for reclamation
|
||||
*/
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not implemented. Recorder modules are not appropriate for reset and reuse.
|
||||
*/
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the audio packet queue.
|
||||
*/
|
||||
private void processAudioPacketQueue()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<AudioPacket> mPacketsToProcess = new ArrayList<>();
|
||||
mAudioPacketQueue.drainTo(mPacketsToProcess);
|
||||
|
||||
if (!mPacketsToProcess.isEmpty())
|
||||
{
|
||||
boolean stopRequested = false;
|
||||
int stopPacketIndex = 0;
|
||||
|
||||
//An END audio packet is a request to stop the recorder
|
||||
for (AudioPacket packet : mPacketsToProcess)
|
||||
{
|
||||
if (packet.getType() == AudioPacket.Type.END)
|
||||
{
|
||||
stopRequested = true;
|
||||
stopPacketIndex = mPacketsToProcess.indexOf(packet);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (stopRequested)
|
||||
{
|
||||
mRunning.set(false);
|
||||
mAudioPacketQueue.clear();
|
||||
|
||||
if (stopPacketIndex > 0)
|
||||
{
|
||||
List<AudioPacket> remainingPacketsToProcess = new ArrayList<>();
|
||||
|
||||
for (int x = 0; x < stopPacketIndex; x++)
|
||||
{
|
||||
remainingPacketsToProcess.add(mPacketsToProcess.get(x));
|
||||
}
|
||||
|
||||
write(remainingPacketsToProcess);
|
||||
|
||||
remainingPacketsToProcess.clear();
|
||||
}
|
||||
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
write(mPacketsToProcess);
|
||||
}
|
||||
|
||||
mPacketsToProcess.clear();
|
||||
}
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
mAudioPacketQueue.clear();
|
||||
|
||||
mLog.error("IOException while processing audio packet queue for MP3 conversion and storage", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the PCM audio packets to MP3 format and writes the converted audio to the output stream.
|
||||
*/
|
||||
private void write(List<AudioPacket> audioPackets) throws IOException
|
||||
{
|
||||
byte[] mp3Audio = mMP3Converter.convert(audioPackets);
|
||||
|
||||
mFileOutputStream.write(mp3Audio);
|
||||
}
|
||||
|
||||
public class BufferProcessor implements Runnable
|
||||
{
|
||||
private AtomicBoolean mProcessing = new AtomicBoolean();
|
||||
|
||||
public void run()
|
||||
{
|
||||
if (mProcessing.compareAndSet(false, true))
|
||||
{
|
||||
processAudioPacketQueue();
|
||||
|
||||
mProcessing.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Path path = Paths.get("/home/denny/Music/PCM.wav");
|
||||
mLog.debug("Opening: " + path.toString());
|
||||
|
||||
final MP3Recorder recorder = new MP3Recorder("/home/denny/Music/denny_test");
|
||||
recorder.start(Executors.newSingleThreadScheduledExecutor());
|
||||
|
||||
try (AudioPacketMonoWaveReader reader = new AudioPacketMonoWaveReader(path, true))
|
||||
{
|
||||
reader.setListener(recorder);
|
||||
reader.read();
|
||||
recorder.stop();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
mLog.error("Error", e);
|
||||
}
|
||||
|
||||
mLog.debug("Finished");
|
||||
}
|
||||
}
|
||||
70
src/record/wave/AudioPacketMonoWaveReader.java
Normal file
70
src/record/wave/AudioPacketMonoWaveReader.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package record.wave;
|
||||
|
||||
import audio.AudioPacket;
|
||||
import audio.IAudioPacketListener;
|
||||
import audio.metadata.AudioMetadata;
|
||||
import sample.Listener;
|
||||
import sample.real.RealBuffer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class AudioPacketMonoWaveReader extends MonoWaveReader implements Listener<RealBuffer>
|
||||
{
|
||||
private AudioMetadata mMetadata = new AudioMetadata(0, false);
|
||||
|
||||
private IAudioPacketListener mListener;
|
||||
|
||||
/**
|
||||
* Reads wav files and converts the contents to AudioPackets.
|
||||
*
|
||||
* @param path of file to read containing 8kHz, 16-bit signed litte endian PCM audio
|
||||
* @param realTime causes the read() method to read and dispatch audio packets in real time. Note: when using
|
||||
* real time playback, the thread calling the read() method will sleep as needed to effect real-time playback,
|
||||
* therefore you should invoke read() on a separate thread if this will impact the calling thread.
|
||||
*
|
||||
* @throws IOException on any file IO errors
|
||||
*/
|
||||
public AudioPacketMonoWaveReader(Path path, boolean realTime) throws IOException
|
||||
{
|
||||
super(path, realTime);
|
||||
setListener(this);
|
||||
}
|
||||
|
||||
public void setMetadata(AudioMetadata metadata)
|
||||
{
|
||||
mMetadata = metadata;
|
||||
}
|
||||
|
||||
public void setListener(IAudioPacketListener listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(RealBuffer realBuffer)
|
||||
{
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.getAudioPacketListener().receive(new AudioPacket(realBuffer.getSamples(), mMetadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
257
src/record/wave/MonoWaveReader.java
Normal file
257
src/record/wave/MonoWaveReader.java
Normal file
@@ -0,0 +1,257 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package record.wave;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.ConversionUtils;
|
||||
import sample.Listener;
|
||||
import sample.real.RealBuffer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MonoWaveReader implements AutoCloseable
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( MonoWaveReader.class);
|
||||
|
||||
private static final Pattern FILENAME_PATTERN = Pattern.compile( "(.*_)(\\d+)(\\.wav)" );
|
||||
|
||||
private Path mPath;
|
||||
private boolean mRealTime;
|
||||
private InputStream mInputStream;
|
||||
private int mDataByteSize = 0;
|
||||
private Listener<RealBuffer> mListener;
|
||||
private byte[] mDataBuffer = new byte[8000]; //500 milliseconds of audio
|
||||
|
||||
/**
|
||||
* Wave file reader for PCM 8kHz, 16-bit little-endian format.
|
||||
*
|
||||
* @param path of the wave file
|
||||
* @param realTime to force replay to real-time. Note: you should run this reader on a separate thread if you
|
||||
* choose real time since this reader will sleep the calling thread as needed to effect real-time playback.
|
||||
*/
|
||||
public MonoWaveReader(Path path, boolean realTime) throws IOException
|
||||
{
|
||||
assert( path != null );
|
||||
mPath = path;
|
||||
mRealTime = realTime;
|
||||
|
||||
open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the file
|
||||
*/
|
||||
private void open() throws IOException
|
||||
{
|
||||
if(!Files.exists(mPath))
|
||||
{
|
||||
throw new IOException("File not found");
|
||||
}
|
||||
|
||||
mInputStream = Files.newInputStream(mPath, StandardOpenOption.READ);
|
||||
|
||||
//Check for RIFF header
|
||||
byte[] buffer = new byte[4];
|
||||
mInputStream.read(buffer);
|
||||
|
||||
if(!Arrays.equals(buffer, WaveUtils.RIFF_CHUNK))
|
||||
{
|
||||
throw new IOException("File is not .wav format - missing RIFF chunk");
|
||||
}
|
||||
|
||||
//Get file size
|
||||
mInputStream.read(buffer);
|
||||
int fileSize = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get();
|
||||
|
||||
//Check for WAVE format
|
||||
mInputStream.read(buffer);
|
||||
|
||||
if(!Arrays.equals(buffer, WaveUtils.WAV_FORMAT))
|
||||
{
|
||||
throw new IOException("File is not .wav format - missing WAVE format");
|
||||
}
|
||||
|
||||
//Check for format chunk
|
||||
mInputStream.read(buffer);
|
||||
|
||||
if(!Arrays.equals(buffer, WaveUtils.CHUNK_FORMAT))
|
||||
{
|
||||
throw new IOException("File is not .wav format - missing format chunk");
|
||||
}
|
||||
|
||||
//Get chunk size
|
||||
mInputStream.read(buffer);
|
||||
int chunkSize = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get();
|
||||
|
||||
//Get format
|
||||
mInputStream.read(buffer);
|
||||
|
||||
ShortBuffer shortBuffer = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
short format = shortBuffer.get();
|
||||
if(format != WaveUtils.PCM_FORMAT)
|
||||
{
|
||||
throw new IOException("File format not supported - expecting PCM format");
|
||||
}
|
||||
|
||||
//Get number of channels
|
||||
short channels = shortBuffer.get();
|
||||
if(channels != 1)
|
||||
{
|
||||
throw new IOException("Unsupported channel count - mono audio only");
|
||||
}
|
||||
|
||||
//Get samples per second
|
||||
mInputStream.read(buffer);
|
||||
int sampleRate = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get();
|
||||
|
||||
//Get bytes per second
|
||||
mInputStream.read(buffer);
|
||||
int bytesPerSecond = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get();
|
||||
|
||||
mInputStream.read(buffer);
|
||||
|
||||
//Get frame size
|
||||
shortBuffer = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
short frameSize = shortBuffer.get();
|
||||
if(frameSize != 2)
|
||||
{
|
||||
throw new IOException("PCM frame size not supported - expecting 2 bytes per frame");
|
||||
}
|
||||
|
||||
//Get bits per sample
|
||||
short bitsPerSample = shortBuffer.get();
|
||||
if(bitsPerSample != 16)
|
||||
{
|
||||
throw new IOException("PCM sample size not supported - expecting 16 bits per sample");
|
||||
}
|
||||
|
||||
mInputStream.read(buffer);
|
||||
|
||||
if(!Arrays.equals(buffer, WaveUtils.CHUNK_DATA))
|
||||
{
|
||||
throw new IOException("Unexpected chunk - expecting data chunk");
|
||||
}
|
||||
|
||||
//Get data chunk size
|
||||
mInputStream.read(buffer);
|
||||
mDataByteSize = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get();
|
||||
}
|
||||
|
||||
public void read() throws IOException
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
long samplesRead = 0;
|
||||
|
||||
int totalBytesRead = 0;
|
||||
|
||||
while(totalBytesRead < mDataByteSize)
|
||||
{
|
||||
int bytesRead = mInputStream.read(mDataBuffer);
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
float[] samples = ConversionUtils.convertFromSigned16BitSamples(mDataBuffer);
|
||||
|
||||
if(bytesRead != mDataBuffer.length)
|
||||
{
|
||||
samples = Arrays.copyOf(samples, bytesRead / 2);
|
||||
}
|
||||
|
||||
if(mRealTime)
|
||||
{
|
||||
samplesRead += samples.length;
|
||||
|
||||
long actualElapsed = System.currentTimeMillis() - start;
|
||||
|
||||
long audioElapsed = (long)(((double)samplesRead / 8000.0) * 1000.0);
|
||||
|
||||
if(audioElapsed > actualElapsed)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(audioElapsed - actualElapsed);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(new RealBuffer(samples));
|
||||
}
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
public void setListener(Listener<RealBuffer> listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the file
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
if(mInputStream != null)
|
||||
{
|
||||
mInputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Path path = Paths.get("/home/denny/Music/PCM.wav");
|
||||
mLog.debug("Opening: " + path.toString());
|
||||
|
||||
try
|
||||
{
|
||||
MonoWaveReader reader = new MonoWaveReader(path, true);
|
||||
reader.setListener(new Listener<RealBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(RealBuffer realBuffer)
|
||||
{
|
||||
mLog.debug("Received buffer");
|
||||
}
|
||||
});
|
||||
reader.read();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Error", e);
|
||||
}
|
||||
|
||||
mLog.debug("Finished");
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,19 @@
|
||||
******************************************************************************/
|
||||
package record.wave;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
|
||||
|
||||
public class WaveUtils
|
||||
{
|
||||
public static final byte[] RIFF_CHUNK = {(byte)0x52, (byte)0x49, (byte)0x46, (byte)0x46}; //RIFF
|
||||
public static final byte[] WAV_FORMAT = {(byte)0x57, (byte)0x41, (byte)0x56, (byte)0x45}; //WAVE
|
||||
public static final byte[] CHUNK_FORMAT = {(byte)0x66, (byte)0x6D, (byte)0x74, (byte)0x20}; //fmt space
|
||||
public static final byte[] CHUNK_DATA = {(byte)0x64, (byte)0x61, (byte)0x74, (byte)0x61}; //data
|
||||
public static final int PCM_FORMAT = 1;
|
||||
|
||||
/**
|
||||
* Creates a wave file header with the RIFF chunk descriptor and the WAVE
|
||||
* format chunk 1 and chunk 2 header
|
||||
|
||||
Reference in New Issue
Block a user