将麦克风流式传输到安卓扬声器

3

我一直在尝试弄清如何在Android上将麦克风流式传输到扬声器的问题。

我希望有一个按钮可以开启和关闭这个功能。

我找到了一些代码,但是让它工作起来是个问题。如果我从按钮中运行此代码,应用程序会崩溃。似乎很难仅仅将麦克风的声音播放到扬声器中。

提前感谢大家的帮助!

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    // Variables
    private static final String TAG = "Aufnahme";
    private AudioRecord recorder = null;
    private boolean isRecording = false;
    private int SAMPLERATE = 8000;
    private int CHANNELS = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    private int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private int bufferSize = AudioRecord.getMinBufferSize(SAMPLERATE,           CHANNELS, AUDIO_FORMAT);
private Thread recordingThread = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();

            SetupRecordButton();
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

public void SetupRecordButton(){

    Button recordButton = (Button) findViewById(R.id.button);

    recordButton.setOnClickListener(new View.OnClickListener(){

        @Override
        public void onClick(View v) {
            startRecording();
        }
    });
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

public void startRecording() {
    recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLERATE,
            CHANNELS, AUDIO_FORMAT, bufferSize);

    recorder.startRecording();
    isRecording = true;

    recordingThread = new Thread(new Runnable()

    {
        public void run() {
            writeAudioData();
        }

    });


    //Start recording thread
    recordingThread.start();

}

public void stopRecording() {
    isRecording = false;
    recorder.stop();
    recorder.release();
    recorder = null;
    recordingThread = null;
}

private void writeAudioData() {

    byte data[] = new byte[bufferSize];

    while (isRecording) {

        recorder.read(data, 0, bufferSize);
        send(data);

    }
}

private void send(byte[] data) {

    int minBufferSize = AudioTrack.getMinBufferSize(8000,
            AudioFormat.CHANNEL_CONFIGURATION_MONO,
            AudioFormat.ENCODING_PCM_16BIT);

    AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
            AudioFormat.CHANNEL_CONFIGURATION_MONO,
            AudioFormat.ENCODING_PCM_16BIT, minBufferSize,
            AudioTrack.MODE_STREAM);

    at.play();
    at.write(data, 0, bufferSize);
    at.stop();
    at.release();

    }
}

StackOverflow不适合用来询问参考资料。 - Enamul Hassan
在代码的哪个点崩溃了?你已经测试过什么了? - Shane Duffy
我遇到了完全相同的问题!如果我有任何进展,我会在这里更新...我正准备就此提出一个问题 :) - oetoni
1个回答

1
这是经过审核并成功运行的版本 :)
为创建它,我使用了默认的AndroidStudio Hello World应用程序,并使用活动视图(侧边菜单和浮动按钮),以模仿上面的示例。当它运行时,没有错误,您需要在文件中进行一些小修改:

 

MainActivity.java

package al.tdb.daf;

//Extra import(s)
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    // Variables
    private static final String TAG = "Aufnahme";
    private AudioRecord recorder = null;
    private boolean isRecording = false;
    private int SAMPLERATE = 8000;
    private int CHANNELS = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    private int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private int bufferSize = AudioRecord.getMinBufferSize(SAMPLERATE, CHANNELS, AUDIO_FORMAT);
    private Thread recordingThread = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        SetupRecordButtons();  //MOVE THIS OUTSIDE ;)

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_camera) {
            // Handle the camera action
        } else if (id == R.id.nav_gallery) {

        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }


    //CUSTOM FUNCTIONS
    //below are all the custom function to perform the operations

    public void SetupRecordButtons(){
        Button startButton = (Button) findViewById(R.id.btnStart);
        startButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                startRecording();
            }
        });

        Button stopButton = (Button) findViewById(R.id.btnStop);
        stopButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                stopRecording();
            }
        });
        stopButton.setEnabled(false);
    }

    public void startRecording() {
        recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLERATE,
                CHANNELS, AUDIO_FORMAT, bufferSize);

        recorder.startRecording();
        isRecording = true;

        recordingThread = new Thread(new Runnable() {
            public void run() {
                writeAudioData();
            }

        });


        //Start recording thread
        recordingThread.start();

        Button startButton = (Button) findViewById(R.id.btnStart);
        startButton.setEnabled(false);
        Button stopButton = (Button) findViewById(R.id.btnStop);
        stopButton.setEnabled(true);
        TextView indicatorLabel = (TextView) findViewById(R.id.indicatorLabel);
        indicatorLabel.setText("recording");
    }

    public void stopRecording() {
        isRecording = false;
        recorder.stop();
        recorder.release();
        recorder = null;
        recordingThread = null;

        Button startButton = (Button) findViewById(R.id.btnStart);
        startButton.setEnabled(true);
        Button stopButton = (Button) findViewById(R.id.btnStop);
        stopButton.setEnabled(false);
        TextView indicatorLabel = (TextView) findViewById(R.id.indicatorLabel);
        indicatorLabel.setText("halted");
    }

    private void writeAudioData() {
        byte data[] = new byte[bufferSize];

        while (isRecording) {
            recorder.read(data, 0, bufferSize);
            send(data);

        }
    }

    private void send(byte[] data) {

        int minBufferSize = AudioTrack.getMinBufferSize(
                8000,
                AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT
        );

        AudioTrack at = new AudioTrack(
                AudioManager.STREAM_MUSIC,
                8000,
                AudioFormat.CHANNEL_CONFIGURATION_MONO,
                AudioFormat.ENCODING_PCM_16BIT,
                minBufferSize,
                AudioTrack.MODE_STREAM
        );

        at.play();
        at.write(data, 0, bufferSize);
        at.stop();
        at.release();

    }
}

 

并且

content_main.xml

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/app_bar_main">

    <Button
        android:id="@+id/btnStart"
        android:layout_width="159dp"
        android:layout_height="0dp"
        android:layout_marginStart="101dp"
        android:layout_marginTop="66dp"
        android:layout_marginBottom="186dp"
        android:text="Start"
        app:layout_constraintBottom_toTopOf="@+id/btnStop"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="154dp"
        android:layout_height="0dp"
        android:layout_marginStart="101dp"
        android:layout_marginBottom="94dp"
        android:text="Stop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnStart" />

    <TextView
        android:id="@+id/indicatorLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

使用类似以下内容的最终结果:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接