一个广泛的项目:将音频从麦克风流式传输到 Android 设备

2023-12-06

我想从蓝牙麦克风获取音频输入并将其大声输出 - 就像扩音器或简单的 PA 系统一样 -并实时播出.

为此,我使用 AudioRecord 和 AudioTrack 类 - 因为 MediaRecorder 和 MediaPlayer 写入和读取外部文件,我可以想象这会导致音频延迟超过必要的程度(这是真的吗?)。

我已经放弃了使用任何蓝牙类的想法,因为 Android API 不支持 Android 设备作为源/接收器场景中的接收器(至少现在还不是),然后需要对 Android 库进行本机编程(这是也是如此,对吧?)

现在。该应用程序正在运行, but 延迟时间太长该应用程序应该实时播放音频。

我的第一个问题是:AudioRecord/AudioTrack 是否适合此目的(通过蓝牙麦克风将音频流式传输到设备扬声器并实时大声播放)?

如果是这样:如何减少音频延迟这样它就可以从麦克风中传输音频真正的实时?此处列出了完整代码,请自行参考:

在 Manifest 中,需要获得录制音频的权限。

<uses-permission android:name="android.permission.RECORD_AUDIO" />

我有一个专用的类,它扩展了一个用于处理音频的线程:

public class AudioStreamer extends Thread {

/**
 * @Params:
 */
private int audioSource = MediaRecorder.AudioSource.MIC;
private int sampleRate = 11025;
private int streamType = AudioManager.STREAM_MUSIC;
private int mode = AudioTrack.MODE_STREAM;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

private int channelConfigIn = AudioFormat.CHANNEL_IN_MONO;
private int channelConfigOut = AudioFormat.CHANNEL_OUT_MONO;

private int recordSize;
private int trackSize;

private AudioTrack track;
private AudioRecord recorder;

/**
 * Initializes the un-initialized params: buffer, bufferSize, track and recorder
 * starts recording/playing with AudioRecord and AudioTrack respectively
 */
public AudioStreamer() {
    System.out.println("New code!");

    recordSize = AudioRecord.getMinBufferSize(sampleRate,
            channelConfigIn, audioFormat);
    System.out.println("recordSize: "+recordSize);
    trackSize = AudioTrack.getMinBufferSize(sampleRate,
            channelConfigOut, audioFormat);
    System.out.println("trackSize: "+trackSize);

    recorder = new AudioRecord(audioSource, sampleRate,
            channelConfigIn, audioFormat, recordSize);

    if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {
        track = new AudioTrack(streamType, sampleRate,
                channelConfigOut, audioFormat, trackSize, mode);

        if (track.getState() == AudioTrack.STATE_INITIALIZED) {
            System.out.println("Record and Track initialized");

        } else {
            System.out.println("Track != init");
        }
    } else {
        System.out.println("Recorder != init");
    }
}

/**
 * Runs thread--which reads and writes from/to the Android hardware
 */
public void run() {
    recorder.startRecording();
    track.play();

    if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING
            && track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
        System.out.println("Recorder and track playing");
    } else {
        System.out.println("Track and recorder != PLAYING");
    }

    short[] buffer = new short[recordSize];
    int audioLenght = 0;

    while (recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING
            && track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
        audioLenght = recorder.read(buffer, 0, recordSize);
        track.write(buffer, 0, audioLenght);
    }
}

/**
 * sets up the AudioManager for bluetooth audio streaming
 */
public void setAudioManager(AudioManager manager) {
    manager.setMode(AudioManager.MODE_IN_COMMUNICATION);

    // set true and test
    manager.setBluetoothScoOn(true);
    manager.setSpeakerphoneOn(true);
    System.out.println("bluetoothScoOn: " + manager.isBluetoothScoOn()
            + ", bluetoothA2DP: " + manager.isBluetoothA2dpOn()
            +", speakerPhone: "+manager.isSpeakerphoneOn());

    /**
     * Start BluetoothSCO
     */
    if (manager.isBluetoothA2dpOn()) {
        manager.startBluetoothSco();
        System.out.println("BtSco started");
    }
}

/**
 * Pauses the audio stream
 */
public void pause() {
    if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
        recorder.stop();
    }

    if (track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
        track.pause();
        track.flush();
    }

    if (track.getPlayState() == AudioTrack.PLAYSTATE_PAUSED
            && recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
        System.out.println("Stopped");
    }
}

}

我的主类提供了可单击的按钮,可以在音频流上调用启动/停止方法:

public class MainActivity extends AppCompatActivity {
private AudioStreamer audioStreamer;
private AudioManager manager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    System.out.println("Program running ... ");
    audioStreamer = new AudioStreamer();

    manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
    audioStreamer.setAudioManager(manager);
}

public void startAudioStreamer(View view) {
    audioStreamer.start();
}

public void pauseAudioStreamer(View view) {
    audioStreamer.pause();
}

}

它们通过带有如下按钮的图形布局显示:

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.android.audiorecordrevisited.MainActivity">

<Button
    android:id="@+id/startAudioStreamer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="startAudioStreamer"
    android:text="Start AudioStreamer" />

<Button
    android:id="@+id/stopAudioStreamer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/startAudioStreamer"
    android:onClick="pauseAudioStreamer"
    android:text="Stop Audio recording" />

 </RelativeLayout>

如何减少音频流的延迟?我是否使用了正确的类,或者是否有另一种/更好的方法来解决这个问题?

另外:音频输入似乎不是通过实际的蓝牙麦克风,而是来自 Android 硬件麦克风 - 这不是这个想法。如何直接读取蓝牙麦克风的音频输入而不是读取内部音频?


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一个广泛的项目:将音频从麦克风流式传输到 Android 设备 的相关文章

  • 通过存储访问框架 (SAF) 启用显示/隐藏 SD 卡的额外功能

    我正在使用存储访问框架 SAF Intent intent new Intent Intent ACTION OPEN DOCUMENT intent addCategory Intent CATEGORY OPENABLE intent
  • 浮动操作按钮动画

    当 fab 出现时 它使用动画中的比例进行动画处理 当它隐藏时 它使用向外扩展动画 因此 它是一个缩小和缩小的动画 这个动画要怎么制作呢 从 Zielony 的回答来看 我完全按照我想要的方式制作了它 下面是正确应用效果的代码 scale
  • Recyclerview 中的 view.GONE 仍保留空间

    我在网上搜索了一下 发现 view invisible 和 view gone 之间有区别 vie gone 必须消失而不保留空间 但它不会发生在我身上 我该如何解决它 请帮忙看看这两张图片的链接 Android模拟器上的结果图 https
  • 如何在同一个列表视图中同时实现ontouch和onfling?

    我有一个listview并实现了onclick和onfling 问题是当我进行fling 从左向右滑动 时 listview的onclick事件也被执行 如何克服这个问题 如何区分列表视图中的触摸 点击 和滑动 滑动 listClickLi
  • 如何让精灵对 cocos2d android 中的触摸做出反应?

    我有 1 支枪 当点击屏幕上的任何一点时 子弹都会发射 但根据我的要求 有 3 支枪 精灵 当触摸任何精灵时 子弹必须发射 当谷歌搜索时 我知道这可以是通过使用targetedTouchDelegate或循环所有的精灵并为每个触摸的精灵设置
  • 未找到 Gradle DSL 方法:“exclude()”

    我正在 Android Studio 中工作 当我在 build gradle 文件中添加该行时 dependencies compile files libs poi ooxml schemas 3 12 20150511 a jar e
  • 已将 APK 上传到 Play 商店,获得 0 个受支持的设备,但没有错误

    我正在尝试将我的应用程序的 Alpha 版本发布到 Play 商店 但我得到了 0 个受支持的设备 这令人沮丧 因为我没有看到该错误 该项目是一个新的样式 gradle API声明和版本在build gradle中定义 您可以在此处获取该应
  • FirebaseAuth - 让用户使用任何提供商更改密码 - Android

    我有一个社交媒体应用程序 我正在使用FirebaseUI让用户使用电子邮件 Google 或 Facebook 登录 注册应用程序 如果使用 电子邮件 作为密码 我怎样才能让用户稍后更改他 她的密码 提供商 如果使用 Facebook 或
  • 从布局中添加和删除视图

    如何从布局中添加和删除视图 我是这样做的 ViewManager entry getParent removeView entry
  • 如何让文字发光?

    我们可以将发光效果应用于任何文本 如下所示 Updated Please also tell me what things i need to create something like this 我需要为此使用特殊字体吗 如何使用以下命令
  • eclipse 找不到我的 sdk 文件夹

    我已经在 Windows 7 上的 eclipse helios 上安装了 android sdk 和 adt 插件 但是 当我进入首选项时 我在定位 sdk 位置时遇到了问题 我的 SDK 位置是 C Program Files Andr
  • Fabric Beta 和 APK 拆分

    我根据 ABI 而不是密度来拆分我的应用程序 如下所示 splits abi enable true reset include x86 armeabi armeabi v7a mips arm64 v8a universalApk tru
  • 如何将 Material 按钮与其他 UI 元素对齐

    我无法将默认材质按钮与 UI 的其他元素对齐 事实上 我已经查看了 Android 源代码 按钮的背景包含插图 以便能够绘制阴影并处理单击时按钮的高度
  • 当我滚动 gridview 时图像发生变化

    GridView 从 URL 加载图像 当我滚动 gridview 时 更多图像机会 如何修复它 我尝试过 imageAdapter notifyDataSetChanged gridView invalidateViews 我还没有找到这
  • 无法将图像从 url 存储到 SD 卡

    我想将图像存储在 mnt sdcard 中 package com Downld file frm net import java io BufferedInputStream import java io File import java
  • 尝试在 android 中更新 sqlite 数据库时出错

    我的数据库正在运行 但我插入的所有记录都是为了检查目的 现在我想删除所有表并创建新表 所以我尝试通过更改版本来更新数据库 我没有对创建表查询进行任何更改 但外键约束失败 代码 787 这是我的DBHelper class private s
  • 获取定制旋转器项目

    我实现了自定义微调器 public class MyAdapter extends ArrayAdapter
  • 从 SD 卡读取 pdf 文件

    我想阅读存储在 SD 卡中的 pdf 文件 我尝试使用此代码片段 File file new File Environment getExternalStorageDirectory vvveksperten ypc pdf Package
  • onActivityResult() Intent 数据始终为 null

    有人可以告诉我为什么Intent data总是为空 Override protected void onActivityResult int requestCode int resultCode Intent data super onAc
  • INSTALL_FAILED_NO_MATCHING_ABIS:无法提取本机库,res = -113设备

    当我在 android 8 0 设备中执行 android 项目时 我收到错误 INSTALL FAILED NO MATCHING ABIS 无法提取本机库 res 113 错误图像 https i stack imgur com 3kb

随机推荐