我正在尝试制作一个简单的Android应用程序,将实时麦克风音频流式传输到服务器进行播放。产生的播放听起来很奇怪,音频间隙很大。有谁知道我做错了什么?
编辑:解决了。事实证明我假设每个传入的缓冲区都已完全填满,这是我的错误假设。这是我的活动:
public class MainActivity extends Activity {
private static String TAG = "AudioClient";
// the server information
private static final String SERVER = "xx.xx.xx.xx";
private static final int PORT = 50005;
// the audio recording options
private static final int RECORDING_RATE = 44100;
private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;
private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;
// the button the user presses to send the audio stream to the server
private Button sendAudioButton;
// the audio recorder
private AudioRecord recorder;
// the minimum buffer size needed for audio recording
private static int BUFFER_SIZE = AudioRecord.getMinBufferSize(
RECORDING_RATE, CHANNEL, FORMAT);
// are we currently sending audio data
private boolean currentlySendingAudio = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "Creating the Audio Client with minimum buffer of "
+ BUFFER_SIZE + " bytes");
// set up the button
sendAudioButton = (Button) findViewById(R.id.start_button);
sendAudioButton.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startStreamingAudio();
break;
case MotionEvent.ACTION_UP:
stopStreamingAudio();
break;
}
return false;
}
});
}
private void startStreamingAudio() {
Log.i(TAG, "Starting the audio stream");
currentlySendingAudio = true;
startStreaming();
}
private void stopStreamingAudio() {
Log.i(TAG, "Stopping the audio stream");
currentlySendingAudio = false;
recorder.release();
}
private void startStreaming() {
Log.i(TAG, "Starting the background thread to stream the audio data");
Thread streamThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Log.d(TAG, "Creating the datagram socket");
DatagramSocket socket = new DatagramSocket();
Log.d(TAG, "Creating the buffer of size " + BUFFER_SIZE);
byte[] buffer = new byte[BUFFER_SIZE];
Log.d(TAG, "Connecting to " + SERVER + ":" + PORT);
final InetAddress serverAddress = InetAddress
.getByName(SERVER);
Log.d(TAG, "Connected to " + SERVER + ":" + PORT);
Log.d(TAG, "Creating the reuseable DatagramPacket");
DatagramPacket packet;
Log.d(TAG, "Creating the AudioRecord");
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDING_RATE, CHANNEL, FORMAT, BUFFER_SIZE * 10);
Log.d(TAG, "AudioRecord recording...");
recorder.startRecording();
while (currentlySendingAudio == true) {
// read the data into the buffer
int read = recorder.read(buffer, 0, buffer.length);
// place contents of buffer into the packet
packet = new DatagramPacket(buffer, read,
serverAddress, PORT);
// send the packet
socket.send(packet);
}
Log.d(TAG, "AudioRecord finished recording");
} catch (Exception e) {
Log.e(TAG, "Exception: " + e);
}
}
});
// start the thread
streamThread.start();
}
}
这是我的服务器端代码:
class Server {
AudioInputStream audioInputStream;
static AudioInputStream ais;
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int sampleRate = 11025;
static int bufferSize = 9728;
static Long lastTime;
static long totalBytesReceived = 0L;
private static final int audioStreamBufferSize = bufferSize * 20;
static byte[] audioStreamBuffer = new byte[audioStreamBufferSize];
private static int audioStreamBufferIndex = 0;
public static void main(String args[]) throws Exception {
Log("Starting the AudioServer...");
Log("Creating the datagram socket on port " + port + "...");
DatagramSocket serverSocket = new DatagramSocket(null);
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(port));
Log("Creating the buffer to hold the received data of size "
+ bufferSize + "...");
byte[] receiveData = new byte[bufferSize];
Log("Setting the audio rate to " + sampleRate + "hz...");
format = new AudioFormat(sampleRate, 16, 1, true, false);
Log("Ready to receive audio data");
while (status == true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
serverSocket.receive(receivePacket);
bufferAudioForPlayback(receivePacket.getData(),
receivePacket.getOffset(), receivePacket.getLength());
}
serverSocket.close();
}
private static void bufferAudioForPlayback(byte[] buffer, int offset,
int length) {
byte[] actualBytes = new byte[length];
for (int i = 0; i < length; i++) {
actualBytes[i] = buffer[i];
}
for (byte sample : actualBytes) {
int percentage = (int) (((double) audioStreamBufferIndex / (double) audioStreamBuffer.length) * 100.0);
Log("buffer is " + percentage + "% full");
audioStreamBuffer[audioStreamBufferIndex] = sample;
audioStreamBufferIndex++;
Log("Buffer " + audioStreamBufferIndex + " / "
+ audioStreamBuffer.length + " " + percentage);
if (audioStreamBufferIndex == audioStreamBuffer.length - 1) {
toSpeaker(audioStreamBuffer);
audioStreamBufferIndex = 0;
System.exit(0);
}
}
}
private static void Log(String log) {
System.out.println(log);
}
public static void toSpeaker(byte soundbytes[]) {
try {
DataLine.Info dataLineInfo = new DataLine.Info(
SourceDataLine.class, format);
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem
.getLine(dataLineInfo);
sourceDataLine.open(format);
FloatControl volumeControl = (FloatControl) sourceDataLine
.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(100.0f);
sourceDataLine.start();
sourceDataLine.open(format);
sourceDataLine.start();
sourceDataLine.write(soundbytes, 0, soundbytes.length);
sourceDataLine.drain();
sourceDataLine.close();
} catch (Exception e) {
System.out.println("Error with audio playback: " + e);
e.printStackTrace();
}
}
}
最后,这是主要活动的资源xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dip">
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"
android:scaleType="fitCenter"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/app_info"
android:layout_weight="1.0"
android:textSize="20dip"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnStart"
android:text="@string/start_recording"
android:layout_weight="1.0"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnStop"
android:text="@string/stop_recording"
android:layout_weight="1.0"/>
</LinearLayout>
</LinearLayout>
编辑:播放音频suh-suh-suh-suh-o-ou-ou-ou-nds-nds-ds就像这样。
答案 0 :(得分:8)
这是你可以尝试的东西,而不是:
// read the data into the buffer
recorder.read(buffer, 0, buffer.length);
// place contents of buffer into the packet
packet = new DatagramPacket(buffer, buffer.length, serverAddress, PORT);
不要指望您从recorder
收到完全读取缓冲区,而是使用实际读取值
// read the data into the buffer
int read = recorder.read(buffer, 0, buffer.length);
// place contents of buffer into the packet
packet = new DatagramPacket(buffer, read, serverAddress, PORT);
或类似的东西。
答案 1 :(得分:0)
感谢约书亚的帖子......对新蜜蜂的帮助很大:)
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
和android客户端的权限
ALTER PROCEDURE [dbo].[sp_Populate_Location]
@school_id int
AS
DECLARE @country_id int, @country_id_new int, @country_name nvarchar(50)
DECLARE @state_id int, @state_id_new int, @state_name nvarchar(50)
DECLARE @town_city_id int, @town_city_name nvarchar(50)
DECLARE @local_government_id int, @local_government_name nvarchar(50)
DECLARE @country TABLE (
idx smallint Primary Key IDENTITY(1,1),
country_id int,
country_name nvarchar(50)
)
DECLARE @state TABLE (
idx smallint Primary Key IDENTITY(1,1),
state_id int,
country_id int,
state_name nvarchar(50)
)
DECLARE @town_city TABLE (
idx smallint Primary Key IDENTITY(1,1),
town_city_name nvarchar(50)
)
DECLARE @local_government TABLE (
idx smallint Primary Key IDENTITY(1,1),
local_government_name nvarchar(50)
)
-- populate country table
INSERT @country
SELECT CountryId, CountryName
FROM tbl_Country
WHERE IsActive = 1
-- enumerate the table
DECLARE @icount_1 int
DECLARE @numrows_country int
SET @icount_1 = 1
SET @numrows_country = (SELECT COUNT(*) FROM @country)
IF @numrows_country > 0
WHILE (@icount_1 <= (SELECT MAX(idx) FROM @country))
BEGIN
-- get the next country primary key
SET @country_id = (SELECT country_id FROM @country WHERE idx = @icount_1)
SET @country_name = (SELECT country_name FROM @country WHERE idx = @icount_1)
INSERT tbl_SchoolCountry(CountryName, IsActive, CreatedBy, CreatedOn, SchoolId)
VALUES(@country_name, 1, 1, GETDATE(), @school_id);
SET @country_id_new = (SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY])
INSERT @state
SELECT StateId, CountryId, StateName
FROM tbl_State
WHERE CountryId = @country_id
DECLARE @icount_2 int
DECLARE @numrows_state int
SET @icount_2 = 1
SET @numrows_state = (SELECT COUNT(*) FROM @state)
IF @numrows_state > 0
WHILE (@icount_2 <= (SELECT MAX(idx) FROM @state))
BEGIN
SET @state_id = (SELECT state_id FROM @state WHERE idx = @icount_2)
SET @state_name = (SELECT state_name FROM @state WHERE idx = @icount_2)
INSERT tbl_SchoolState(CountryId, StateName, IsActive, CreatedBy, CreatedOn, SchoolId)
VALUES(@country_id_new, @state_name, 1, 1, GETDATE(), @school_id);
SET @state_id_new = (SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY])
-- Town and City
INSERT @town_city
SELECT TownCityName
FROM tbl_TownCity
WHERE StateId = @state_id AND IsActive = 1
DECLARE @icount_3 int
DECLARE @numrows_town_city int
SET @icount_3 = 1
SET @numrows_town_city = (SELECT COUNT(*) FROM @town_city)
IF @numrows_town_city > 0
WHILE (@icount_3 <= (SELECT MAX(idx) FROM @town_city))
BEGIN
SET @town_city_name = (SELECT town_city_name FROM @town_city WHERE idx = @icount_3)
INSERT tbl_SchoolTownCity(CountryId, StateId, TownCityName, IsActive, CreatedBy, CreatedOn, SchoolId)
VALUES(@country_id_new, @state_id_new, @town_city_name, 1, 1, GETDATE(), @school_id);
SET @icount_3 = @icount_3 + 1
END
-- LGA
INSERT @local_government
SELECT LocalGovernmentName FROM tbl_LocalGovernment where StateId = @state_id and IsActive = 1
DECLARE @icount_4 int
DECLARE @numrows_lga int
SET @icount_4 = 1
SET @numrows_lga = (SELECT COUNT(*) FROM @local_government)
IF @numrows_lga > 0
WHILE (@icount_4 <= (SELECT MAX(idx) FROM @local_government))
BEGIN
SET @local_government_name = (SELECT local_government_name FROM @local_government WHERE idx = @icount_4)
INSERT tbl_SchoolLocalGovernment(CountryId, StateId, LocalGovernmentName, IsActive, CreatedBy, CreatedOn, SchoolId)
VALUES(@country_id_new, @state_id_new, @local_government_name,1,1,GETDATE(),@school_id);
SET @icount_4 = @icount_4 + 1
END
SET @icount_2 = @icount_2 + 1
END
-- increment counter for next country
SET @icount_1 = @icount_1 + 1
END