Android 使用外置USB麦克风MIC录音遇到问题并解决(含录音播放源码)

这篇具有很好参考价值的文章主要介绍了Android 使用外置USB麦克风MIC录音遇到问题并解决(含录音播放源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、遇到问题

  • 使用RK3399的开发板,跑Android8.1系统
  • 一开始插上外置的USB麦克风的时候,无法使用

二、分析问题

  • 查看USB麦克风拔插过程的debug打印日志

Android 使用外置USB麦克风MIC录音遇到问题并解决(含录音播放源码)

  • 插入USB麦克风后,adb 查询当前声卡信息
  • cat cards

Android 使用外置USB麦克风MIC录音遇到问题并解决(含录音播放源码)

三、解决问题

  • 通过分析发现,USB麦克风设备没有枚举出来,节点都没挂载上去
  • 这时候就怀疑是硬件问题,USB麦克风设备故障,或者USB供电不足,或者USB布线问题
  • 通过交叉实验,拔插不同的USB口更换RK3399开发板更换USB麦克风
  • 最后确认是USB麦克风设备故障,更换USB麦克风后,再cat cards就能查询出USB-AUDIO设备

Android 使用外置USB麦克风MIC录音遇到问题并解决(含录音播放源码)

  • 使用USB麦克风设备
    • 首先需要正常识别到该USB-AUDIO设备
    • 其次Android系统会自动切换MIC录音源
      • 如果没有插入USB麦克风设备,则Android系统使用主板上模拟MIC
      • 如果插入USB麦克风设备,则Android系统就会切换到USB麦克风

四、录音源码分析

  • audioSource: MediaRecorder.AudioSource.CAMCORDER, MediaRecorder.AudioSource.MIC 等
  • sampleRateInHz: 常用的有: 8000,11025,16000,22050,44100,96000;44100Hz是唯一可以保证兼容所有Android手机的采样率
  • channelConfig: AudioFormat.CHANNEL_CONFIGURATION_DEFAULT,常用的是 CHANNEL_IN_MONO(单通道)CHANNEL_IN_STEREO(双通道)
  • audioFormat: AudioFormat.ENCODING_PCM_16BIT, 测试中要捕获PCM数据
  • bufferSizeInBytes: 由AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)获得
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
AudioRecord.startRecording();

AudioRecord.stop();

AudioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes);
public class AudioCapturer {

    private static final String TAG = "AudioCapturer";
	
    private static final int DEFAULT_SOURCE = MediaRecorder.AudioSource.MIC;
    private static final int DEFAULT_SAMPLE_RATE = 44100;
    private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;
    private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;

    private AudioRecord mAudioRecord;
    private int mMinBufferSize = 0;
	
    private Thread mCaptureThread; 	
    private boolean mIsCaptureStarted = false;
    private volatile boolean mIsLoopExit = false;

    private OnAudioFrameCapturedListener mAudioFrameCapturedListener;

    public interface OnAudioFrameCapturedListener {
        public void onAudioFrameCaptured(byte[] audioData);
    }	

    public boolean isCaptureStarted() {		
        return mIsCaptureStarted;
    }

    public void setOnAudioFrameCapturedListener(OnAudioFrameCapturedListener listener) {
        mAudioFrameCapturedListener = listener;
    }

    public boolean startCapture() {
        return startCapture(DEFAULT_SOURCE, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_CONFIG,
            DEFAULT_AUDIO_FORMAT);
    }

    public boolean startCapture(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat) {

        if (mIsCaptureStarted) {
            Log.e(TAG, "Capture already started !");
            return false;
        }
    
        mMinBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz,channelConfig,audioFormat);
        if (mMinBufferSize == AudioRecord.ERROR_BAD_VALUE) {
            Log.e(TAG, "Invalid parameter !");
            return false;
        }
        Log.d(TAG , "getMinBufferSize = "+mMinBufferSize+" bytes !");
		
        mAudioRecord = new AudioRecord(audioSource,sampleRateInHz,channelConfig,audioFormat,mMinBufferSize);				
        if (mAudioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {
    	    Log.e(TAG, "AudioRecord initialize fail !");
    	    return false;
        }		

        mAudioRecord.startRecording();

        mIsLoopExit = false;
        mCaptureThread = new Thread(new AudioCaptureRunnable());
        mCaptureThread.start();

        mIsCaptureStarted = true;

        Log.d(TAG, "Start audio capture success !");

        return true;
    }

    public void stopCapture() {

        if (!mIsCaptureStarted) {
            return;
        }

        mIsLoopExit = true;		
        try {
            mCaptureThread.interrupt();
            mCaptureThread.join(1000);
        } 
        catch (InterruptedException e) {		
            e.printStackTrace();
        }

        if (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
            mAudioRecord.stop();						
        }

        mAudioRecord.release();		
	
        mIsCaptureStarted = false;
        mAudioFrameCapturedListener = null;

        Log.d(TAG, "Stop audio capture success !");
    }

    private class AudioCaptureRunnable implements Runnable {		
	
        @Override
        public void run() {

            while (!mIsLoopExit) {

                byte[] buffer = new byte[mMinBufferSize];

                int ret = mAudioRecord.read(buffer, 0, mMinBufferSize);				
                if (ret == AudioRecord.ERROR_INVALID_OPERATION) {
                    Log.e(TAG , "Error ERROR_INVALID_OPERATION");
                } 
                else if (ret == AudioRecord.ERROR_BAD_VALUE) {
                    Log.e(TAG , "Error ERROR_BAD_VALUE");
                } 
                else { 
                    if (mAudioFrameCapturedListener != null) {
                        mAudioFrameCapturedListener.onAudioFrameCaptured(buffer);
                    }   
                    Log.d(TAG , "OK, Captured "+ret+" bytes !");
                }			
                
                SystemClock.sleep(10);
            }		
        }    
    }
}

五、播放源码分析

  • streamType:
    • AudioManager.STREAM_VOCIE_CALL:电话声音
    • AudioManager.STREAM_SYSTEM:系统声音
    • AudioManager.STREAM_RING:铃声
    • AudioManager.STREAM_MUSCI:音乐声
    • AudioManager.STREAM_ALARM:警告声
    • AudioManager.STREAM_NOTIFICATION:通知声
  • sampleRateInHz: 常用的有: 8000,11025,16000,22050,44100,96000;44100Hz是唯一可以保证兼容所有Android手机的采样率
  • channelConfig: AudioFormat.CHANNEL_CONFIGURATION_DEFAULT,常用的是 CHANNEL_IN_MONO(单通道)
  • audioFormat: AudioFormat.ENCODING_PCM_16BIT, 测试中要捕获PCM数据
  • bufferSizeInBytes: 由AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)获得
  • mode:
    • AudioTrack.MODE_STATIC,一次性将所有的数据都写入播放缓冲区,简单高效
    • AudioTrack.MODE_STREAM,按照一定的时间间隔不间断地写入音频数据
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)
AudioTrack.play();
AudioTrack.stop();                        
import android.util.Log;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;

public class AudioPlayer {
    
    private static final String TAG = "AudioPlayer";

    private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
    private static final int DEFAULT_SAMPLE_RATE = 44100;
    private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;
    private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private static final int DEFAULT_PLAY_MODE = AudioTrack.MODE_STREAM;
            
    private boolean mIsPlayStarted = false;
    private int mMinBufferSize = 0;
    private AudioTrack mAudioTrack;  
    
    public boolean startPlayer() {
        return startPlayer(DEFAULT_STREAM_TYPE,DEFAULT_SAMPLE_RATE,DEFAULT_CHANNEL_CONFIG,DEFAULT_AUDIO_FORMAT);
    }
    
    public boolean startPlayer(int streamType, int sampleRateInHz, int channelConfig, int audioFormat) {
        
        if (mIsPlayStarted) {
            Log.e(TAG, "Player already started !");
            return false;
        }
        
        mMinBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz,channelConfig,audioFormat);
        if (mMinBufferSize == AudioTrack.ERROR_BAD_VALUE) {
            Log.e(TAG, "Invalid parameter !");
            return false;
        }
        Log.d(TAG , "getMinBufferSize = "+mMinBufferSize+" bytes !");
        
        mAudioTrack = new AudioTrack(streamType,sampleRateInHz,channelConfig,audioFormat,mMinBufferSize,DEFAULT_PLAY_MODE);
        if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {
            Log.e(TAG, "AudioTrack initialize fail !");
            return false;
        }            
        
        mIsPlayStarted = true;
        
        Log.d(TAG, "Start audio player success !");
        
        return true;
    }
    
    public int getMinBufferSize() {
        return mMinBufferSize;
    }
    
    public void stopPlayer() {
        
        if (!mIsPlayStarted) {
            return;
        }
        
        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            mAudioTrack.stop();                        
        }
        
        mAudioTrack.release();
        mIsPlayStarted = false;
           
        Log.d(TAG, "Stop audio player success !");
    }
    
    public boolean play(byte[] audioData, int offsetInBytes, int sizeInBytes) {
        
        if (!mIsPlayStarted) {
            Log.e(TAG, "Player not started !");
            return false;
        }
        
        if (sizeInBytes < mMinBufferSize) {
            Log.e(TAG, "audio data is not enough !");
            return false;
        }
        
        if (mAudioTrack.write(audioData,offsetInBytes,sizeInBytes) != sizeInBytes) {                
            Log.e(TAG, "Could not write all the samples to the audio device !");
        }                                   
                                                   
        mAudioTrack.play();
        
        Log.d(TAG , "OK, Played "+sizeInBytes+" bytes !");
        
        return true;
    }
}

六、参考

  • Jhuster/Android
  • android 采集PCM音频数据并播放(支持USB摄像头MIC)

觉得好,就一键三连呗(点赞+收藏+关注)文章来源地址https://www.toymoban.com/news/detail-490521.html

到了这里,关于Android 使用外置USB麦克风MIC录音遇到问题并解决(含录音播放源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android麦克风数据采集格式和常见的编码格式

    在 Android 平台上,PCM(脉冲编码调制)数据是一种常用的音频数据表示格式。PCM 是一种无损音频编码,可以捕捉和存储原始音频信号的波形。在 Android 系统中,开发人员可以使用 PCM 数据处理音频信号,以实现音频播放、录音、实时语音通信等功能。 在处理 PCM 数据时,And

    2023年04月08日
    浏览(53)
  • 用Android手机充当台式电脑的摄像头、麦克风和音箱

    需要两个软件(都需要装电脑端和手机端): DroidCam:起摄像头和麦克风的作用 AudioRelay:起输出音频的作用 步骤: 打开手机USB调试模式,连电脑(电脑没反应就重启手机) 电脑、手机启动AudioRelay,在app界面下拉打开USB网络共享,回到主界面随便切换一下,看到DESKTOP字样说

    2024年02月11日
    浏览(91)
  • Android WebView加载h5打开麦克风与摄像头的权限问题

    Android webview h5 麦克风权限,摄像头(相机)权限实现与填坑。 app 必须先具备如下权限(本文只讨论录音与相机) AndroidManifest中添加: 注意:前面两项需要app动态申请 懒人快速做法(可直接copy代码) 默许授权(不再确认) 当h5向app申请的时候,重写 WebChromeClient 的onPermis

    2024年02月11日
    浏览(63)
  • Unity 使用 Microphone 采集麦克风声音

    1、获取麦克风,Microphone.devices 2、开始录音,Microphone.Start 3、结束录音,Microphone.End 1、MicrophoneWrapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

    2024年02月04日
    浏览(45)
  • Android 13 定制化开发--开启相机或麦克风时,去掉状态栏上的绿色图标

     Android 12 或更高版本的设备上,当应用使用麦克风或相机时,图标会出现在状态栏中。如果应用处于沉浸模式,图标会出现在屏幕的右上角。用户可以打开“快捷设置”,并选择图标以查看哪些应用当前正在使用麦克风或摄像头。图 1 显示了包含图标的示例屏幕截图。 需求

    2024年04月24日
    浏览(81)
  • 使用手机作为电脑的麦克风和摄像头外设

    Iriun 电脑端安装:Iriun Android: Iriun 4K Webcam for PC and Mac - Apps on Google Play Apple: Iriun Webcam for PC and Mac on the App Store 基础功能免费,普通使用足够了。 付费功能: 这里有介绍: 手机相机作为电脑摄像头使用(Android、iPhone) - Bandicam(班迪录屏) 手机和电脑在同一个局域网中就可以

    2024年02月01日
    浏览(67)
  • 音频——数字麦克风和模拟麦克风(DMIC/AMIC)

    麦克风 (microphone):是将声音信号转换为电信号的能量转换器件,也就是用来采集你说话的声音 扬声器 (speaker):是一种把电信号转变为声信号的换能器件,就是把对方说话产生的电信号转换成声音播放出来。 简单来说,麦克风的功能是采集声音,扬声器的功能是播放声音。

    2024年02月10日
    浏览(77)
  • 使用STM32的I2S协议读取麦克风INMP441

    本文将详细介绍使用STM32的硬件I2S协议,从麦克风模块INMP441读取音频信号的步骤和避坑指南。 点这里,从Github下载工程文件 百度网盘: 链接:https://pan.baidu.com/s/1cglAGirn6bTQhIEpWDb7Ig?pwd=k9gh 提取码:k9gh INMP441模块: 主控芯片:我用的是STM32F103ZET6,可以在STM的选型手册上看到哪

    2024年02月02日
    浏览(60)
  • 传感器使用——ESP-WROOM-32 and INMP441麦克风音频采集

             使用不同传感器前需要了解其连接及通信方式。对于 ESP-WROOM-32 and INMP441麦克风 是通过 I2S 方式进行通信。具体连接引脚参照下图: ESP-WROOM-32引脚示意图          使用的麦克风为MEMS麦克风,具体规格可参照芯片规格数说明。 INMP441麦克风         I2S音频连接

    2024年02月09日
    浏览(44)
  • MacOS 为指定应用添加指定权限(浏览器无法使用摄像头、麦克风终极解决方案)

    起因:需要浏览器在线做一些测评,但我的 Chrome 没有摄像头/麦克风权限,并且在设置中是没有手动添加按钮的。 我尝试了重装软件,更新系统(上面的 13.5 就是这么来的,我本来都半年懒得更新系统了),都没有任何用。 系统版本:MacOS 13.5.1(需要开启 sip,可参考 macOS

    2024年02月07日
    浏览(80)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包