mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 09:07:27 -05:00
Updates pull request from Roman Tsypuk to convert the build system from
ant to gradle. Removes linux and windows application launch scripts and produces an executable jar with all resources included.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* 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 io.github.dsheirer.audio.convert;
|
||||
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IAudioConverter
|
||||
{
|
||||
/**
|
||||
* Converts the PCM audio packets to converted audio format. May produce partial audio frame data.
|
||||
*/
|
||||
public byte[] convert(List<AudioPacket> audioPackets);
|
||||
|
||||
/**
|
||||
* Finalizes audio conversion by fully converting any partial frames left in the buffer and returning the
|
||||
* remaining bytes to produce a full frame.
|
||||
*/
|
||||
public byte[] flush();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* 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 io.github.dsheirer.audio.convert;
|
||||
|
||||
public interface ISilenceGenerator
|
||||
{
|
||||
byte[] generate(long duration);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*******************************************************************************
|
||||
* 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 io.github.dsheirer.audio.convert;
|
||||
|
||||
import io.github.dsheirer.audio.AudioFormats;
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.audio.AudioUtils;
|
||||
import net.sourceforge.lame.lowlevel.LameEncoder;
|
||||
import net.sourceforge.lame.mp3.Lame;
|
||||
import net.sourceforge.lame.mp3.MPEGMode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MP3AudioConverter implements IAudioConverter
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( MP3AudioConverter.class );
|
||||
public static final int AUDIO_QUALITY = Lame.QUALITY_LOW;
|
||||
private LameEncoder mEncoder;
|
||||
private ByteArrayOutputStream mMP3Stream = new ByteArrayOutputStream();
|
||||
private byte[] mMP3Buffer;
|
||||
|
||||
/**
|
||||
* Converts PCM 8kHz 16-bit Little Endian audio packets to Mono, Low Quality MP3 compressed audio.
|
||||
*
|
||||
* @param bitRate for converted MP3 audio
|
||||
* @param variableBitRate (VBR) true or false for constant bit rate (CBR)
|
||||
*/
|
||||
public MP3AudioConverter(int bitRate, boolean variableBitRate)
|
||||
{
|
||||
mEncoder = new LameEncoder(AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO,
|
||||
bitRate, MPEGMode.MONO, AUDIO_QUALITY, variableBitRate);
|
||||
|
||||
mMP3Buffer = new byte[mEncoder.getPCMBufferSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] convert(List<AudioPacket> audioPackets)
|
||||
{
|
||||
mMP3Stream.reset();
|
||||
|
||||
byte[] pcmBytes = AudioUtils.convertTo16BitSamples(audioPackets);
|
||||
|
||||
int pcmBufferSize = Math.min(mMP3Buffer.length, pcmBytes.length);
|
||||
|
||||
int mp3BufferSize = 0;
|
||||
|
||||
int pcmBytesPosition = 0;
|
||||
|
||||
try
|
||||
{
|
||||
while (0 < (mp3BufferSize = mEncoder.encodeBuffer(pcmBytes, pcmBytesPosition, pcmBufferSize, mMP3Buffer)))
|
||||
{
|
||||
pcmBytesPosition += pcmBufferSize;
|
||||
pcmBufferSize = Math.min(mMP3Buffer.length, pcmBytes.length - pcmBytesPosition);
|
||||
mMP3Stream.write(mMP3Buffer, 0, mp3BufferSize);
|
||||
}
|
||||
|
||||
return mMP3Stream.toByteArray();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("There was an error converting audio to MP3: " + e.getMessage());
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] flush()
|
||||
{
|
||||
byte[] lastPartialFrame = new byte[mEncoder.getMP3BufferSize()];
|
||||
|
||||
int length = mEncoder.encodeFinish(lastPartialFrame);
|
||||
|
||||
return Arrays.copyOf(lastPartialFrame, length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 io.github.dsheirer.audio.convert;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MP3FrameInspector
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MP3FrameInspector.class);
|
||||
|
||||
public static void inspect(byte[] frames)
|
||||
{
|
||||
if(frames == null || frames.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(frames.length % 144 != 0)
|
||||
{
|
||||
log("Frame byte array not multiple of 144 byte frame size - length:" + frames.length, frames);
|
||||
return;
|
||||
}
|
||||
|
||||
//Check for erroneous frame sync
|
||||
for(int x = 0; x < frames.length - 1; x++)
|
||||
{
|
||||
if(frames[x] == (byte)0xFF && (frames[x + 1] & 0xE0) == 0xE0 && (x % 144) != 0)
|
||||
{
|
||||
log("Bad frame sync detected at byte " + x + " in frame " + (x / 144), frames);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Check MP3 headers
|
||||
|
||||
// log("Frames ...", frames);
|
||||
}
|
||||
|
||||
private static void log(String message, byte[] frames)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(message).append("\n");
|
||||
|
||||
int frameCounter = 0;
|
||||
sb.append(frameCounter++).append(" ");
|
||||
|
||||
for(int x = 0; x < frames.length; x++)
|
||||
{
|
||||
if(x > 0 && x % 144 == 0)
|
||||
{
|
||||
sb.append("\n");
|
||||
sb.append(frameCounter++).append(" ");
|
||||
}
|
||||
|
||||
sb.append(String.format("%02X ", frames[x]));
|
||||
}
|
||||
|
||||
mLog.info(sb.toString());
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
mLog.info("Starting ...");
|
||||
MP3SilenceGenerator gen = new MP3SilenceGenerator();
|
||||
|
||||
byte[] audio = gen.generate(173);
|
||||
|
||||
inspect(audio);
|
||||
|
||||
mLog.info("Finished");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*******************************************************************************
|
||||
* 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 io.github.dsheirer.audio.convert;
|
||||
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.record.mp3.MP3Recorder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MP3SilenceGenerator implements ISilenceGenerator
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MP3SilenceGenerator.class);
|
||||
|
||||
private MP3AudioConverter mGenerator = new MP3AudioConverter(MP3Recorder.MP3_BIT_RATE, MP3Recorder.CONSTANT_BIT_RATE);
|
||||
private byte[] mPreviousPartialFrameData;
|
||||
|
||||
/**
|
||||
* Generates MP3 audio silence frames
|
||||
*/
|
||||
public MP3SilenceGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public byte[] generate(long duration)
|
||||
{
|
||||
int length = (int)(duration * 8); //8000 Hz sample rate
|
||||
float[] silence = new float[length];
|
||||
AudioPacket silencePacket = new AudioPacket(silence, null);
|
||||
List<AudioPacket> silencePackets = new ArrayList<>();
|
||||
silencePackets.add(silencePacket);
|
||||
|
||||
byte[] frameData = mGenerator.convert(silencePackets);
|
||||
|
||||
frameData = merge(mPreviousPartialFrameData, frameData);
|
||||
|
||||
if(frameData != null && frameData.length > 0)
|
||||
{
|
||||
if(frameData.length < 144)
|
||||
{
|
||||
mPreviousPartialFrameData = frameData;
|
||||
return null;
|
||||
}
|
||||
else if((frameData.length % 144) == 0)
|
||||
{
|
||||
mPreviousPartialFrameData = null;
|
||||
return frameData;
|
||||
}
|
||||
else
|
||||
{
|
||||
int integralFrameLength = (int)(frameData.length / 144) * 144;
|
||||
mPreviousPartialFrameData = Arrays.copyOfRange(frameData, integralFrameLength, frameData.length);
|
||||
return Arrays.copyOf(frameData, integralFrameLength);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[] merge(byte[] a, byte[] b)
|
||||
{
|
||||
if(a == null && b == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if(a == null)
|
||||
{
|
||||
return b;
|
||||
}
|
||||
else if(b == null)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
byte[] c = new byte[a.length + b.length];
|
||||
System.arraycopy(a,0,c,0,a.length);
|
||||
System.arraycopy(b,0,c,a.length,b.length);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
mLog.debug("Starting ...");
|
||||
|
||||
for(long x = 243; x < 500; x ++)
|
||||
{
|
||||
MP3SilenceGenerator generator = new MP3SilenceGenerator();
|
||||
|
||||
byte[] silence = generator.generate(x);
|
||||
|
||||
if(silence != null && silence.length > 0)
|
||||
{
|
||||
mLog.debug("Silence:" + x + " Length:" + silence.length);
|
||||
MP3FrameInspector.inspect(silence);
|
||||
}
|
||||
}
|
||||
|
||||
mLog.debug("Finished");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user