「混音pcmjava」混音remix

博主:adminadmin 2022-11-26 15:23:07 51

本篇文章给大家谈谈混音pcmjava,以及混音remix对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

java音频处理问题

为什么audioFormat.getChannels(), 一次后还要再一个audioFormat.getChannels()*2

每声道每帧字节数2字节,再加上双声道所以用声道数(2)*字节数(2) = 每帧的总字节数

看来你的audioFormat不是从AudioInputStream中产生的啊?刚才回答你的问题我还没注意。原来audioFormat也可以new出来的啊,嘿嘿。

求解,JAVA语言多人语音聊天怎么解决混音问

两路音视频流,符合以下条件才能混合:

格式相同,要解压成 PCM 格式。

采样率相同,要转换成相同的采样率。主流采样率包括:16k Hz、32k Hz、44.1k Hz 和 48k Hz。

帧长相同,帧长由编码格式决定,PCM 没有帧长的概念,开发者自行决定帧长。为了和主流音频编码格式的帧长保持一致,推荐采用 20ms 为帧长。

位深(Bit-Depth)或采样格式 (Sample Format) 相同,承载每个采样点数据的 bit 数目要相同。

声道数相同,必须同样是单声道或者双声道 (立体声)。这样,把格式、采样率、帧长、位深和声道数对齐了以后,两个音频流就可以混合了。

JAVA代码问题可以到即构开发者中心看看

android开发中怎样实现混音

android开发中怎样实现混音,实例代码如下:

[mw_shl_code=java,true]public class MixRunnable implements Runnable {

private MixRecorder context;

/**

* AudioRecord创建参数类

*

* @author christ

*/

private static class RecorderParameter {

// 音频获取源

private static int audioSource = MediaRecorder.AudioSource.MIC;

// 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025

private static final int sampleRateInHz = 44100;

// 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道

private static final int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;

// 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。

private static final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

// 缓冲区字节大小

private static int bufferSizeInBytes;

}

// 设置运行状态

private boolean isRunning = true;

// AudioRecord对象

private static AudioRecord recorder;

// 设置MediaPlayer对象

private static MediaPlayer mediaPlayer;

// 伴奏文件

private FileInputStream accompany;

// 原唱文件

private FileInputStream original;

// 得分

private int score;

private boolean isFirst = true;

/**

* 混音评分线程的构造方法

*

* @param accompany

* :伴奏文件路径

* @param original

* :原唱文件路径

* @throws FileNotFoundException

*/

public MixRunnable(MixRecorder context, String accompany, String original) throws FileNotFoundException {

this.context = context;

this.accompany = new FileInputStream(accompany);

this.original = new FileInputStream(original);

creatAudioRecord();

mediaPlayer = new MediaPlayer();

}

@Override

public void run() {

try {

// MediaPlayer准备

mediaPlayer.reset();

mediaPlayer.setDataSource("/sdcard/111.wav");

// mediaPlayer.setDataSource(accompany.getFD());

mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

@Override

public void onCompletion(MediaPlayer mp) {

isRunning = false;

}

});

mediaPlayer.prepare();

// 跳过头

accompany.read(new byte[44]);

original.read(new byte[44]);

FileOutputStream fos = new FileOutputStream(new File("/sdcard/love.raw"));

// 开始读

byte[] sourceReader = new byte[RecorderParameter.bufferSizeInBytes * 2];

short[] sourceShortArray;

short[] audioReader = new short[sourceReader.length / 4];

mediaPlayer.start();

recorder.startRecording();

while (isRunning) {

int sourceReadSize = accompany.read(sourceReader, 0, sourceReader.length);

if (sourceReadSize 0) {

isRunning = false;

continue;

}

sourceShortArray = byteToShortArray(sourceReader, sourceReadSize / 2);

recorder.read(audioReader, 0, audioReader.length);

short[] oneSecond = mixVoice(sourceShortArray, audioReader, sourceReadSize / 2);

byte[] outStream = new byte[oneSecond.length * 2];

for (int i = 0; i oneSecond.length; i++) {

byte[] b = shortToByteArray(oneSecondi);

outStream[2 * i] = b[0];

outStream[2 * i + 1] = b[1];

}

MediaPlayer和AudioTrack播放Audio的区别与联系

播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供应用开发者使用。虽然都可以播放声音,但两者还是有很大的区别的。

其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。

而AudioTrack只能播放已经解码的PCM流,如果是文件的话只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件。

当然两者之间还是有紧密的联系的,MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放。

所以是MediaPlayer包含了AudioTRack。

通过查看API可以知道,MediaPlayer提供了5个setDataSource方法,分为三类,一类是传递播放文件的字符串路径作为参数,例如直接取sd卡里mp3文件的路径,一类是传递播放文件的FileDescriptor文件描述符作为播放的id,例例如从db中查询的音频文件的id,就可以直接赋给MediaPlayer进行播放。还有一类是Uri类型的资源文件,用于播放content uri文件。

AudioTrack如何与AudioFlinger交换音频数据

如何使用AudioTrackAudioTrack的主要代码位于 frameworks\base\media\libmedia\audiotrack.cpp中。现在先通过一个例子来了解一下如何使用 AudioTrack,ToneGenerator是android中产生电话拨号音和其他音调波形的一个实现,我们就以它为例子:ToneGenerator的初始化函数:bool ToneGenerator::initAudioTrack() { // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size mpAudioTrack = new AudioTrack(); mpAudioTrack-set(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0, 0, mThreadCanCallJava); if (mpAudioTrack-initCheck() != NO_ERROR) { LOGE("AudioTrack-initCheck failed"); goto initAudioTrack_exit; } mpAudioTrack-setVolume(mVolume, mVolume); mState = TONE_INIT; ...... } bool ToneGenerator::initAudioTrack() { // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size mpAudioTrack = new AudioTrack(); mpAudioTrack-set(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0, 0, mThreadCanCallJava); if (mpAudioTrack-initCheck() != NO_ERROR) { LOGE("AudioTrack-initCheck failed"); goto initAudioTrack_exit; } mpAudioTrack-setVolume(mVolume, mVolume); mState = TONE_INIT; ...... }

可见,创建步骤很简单,先new一个AudioTrack的实例,然后调用set成员函数完成参数的设置并注册到AudioFlinger中,然后 可以调用其他诸如设置音量等函数进一步设置音频参数。其中,一个重要的参数是audioCallback,audioCallback是一个回调函数,负 责响应AudioTrack的通知,例如填充数据、循环播放、播放位置触发等等。回调函数的写法通常像这样:void ToneGenerator::audioCallback(int event, void* user, void *info) { if (event != AudioTrack::EVENT_MORE_DATA) return; AudioTrack::Buffer *buffer = static_cast(info); ToneGenerator *lpToneGen = static_cast(user); short *lpOut = buffer-i16; unsigned int lNumSmp = buffer-size/sizeof(short); const ToneDescriptor *lpToneDesc = lpToneGen-mpToneDesc; if (buffer-size == 0) return; // Clear output buffer: WaveGenerator accumulates into lpOut buffer memset(lpOut, 0, buffer-size); ...... // 以下是产生音调数据的代码,略.... } void ToneGenerator::audioCallback(int event, void* user, void *info) { if (event != AudioTrack::EVENT_MORE_DATA) return; AudioTrack::Buffer *buffer = static_cast(info); ToneGenerator *lpToneGen = static_cast(user); short *lpOut = buffer-i16; unsigned int lNumSmp = buffer-size/sizeof(short); const ToneDescriptor *lpToneDesc = lpToneGen-mpToneDesc; if (buffer-size == 0) return; // Clear output buffer: WaveGenerator accumulates into lpOut buffer memset(lpOut, 0, buffer-size); ...... // 以下是产生音调数据的代码,略.... }

该函数首先判断事件的类型是否是EVENT_MORE_DATA,如果是,则后续的代码会填充相应的音频数据后返回,当然你可以处理其他事件,以下 是可用的事件类型:enum event_type { EVENT_MORE_DATA = 0, // Request to write more data to PCM buffer. EVENT_UNDERRUN = 1, // PCM buffer underrun occured. EVENT_LOOP_END = 2, // Sample loop end was reached; playback restarted from loop start if loop count was not 0. EVENT_MARKER = 3, // Playback head is at the specified marker position (See setMarkerPosition()). EVENT_NEW_POS = 4, // Playback head is at a new position (See setPositionUpdatePeriod()). EVENT_BUFFER_END = 5 // Playback head is at the end of the buffer. }; enum event_type { EVENT_MORE_DATA = 0, // Request to write more data to PCM buffer. EVENT_UNDERRUN = 1, // PCM buffer underrun occured. EVENT_LOOP_END = 2, // Sample loop end was reached; playback restarted from loop start if loop count was not 0. EVENT_MARKER = 3, // Playback head is at the specified marker position (See setMarkerPosition()). EVENT_NEW_POS = 4, // Playback head is at a new position (See setPositionUpdatePeriod()). EVENT_BUFFER_END = 5 // Playback head is at the end of the buffer. };

开始播放:mpAudioTrack-start(); mpAudioTrack-start();

停止播放:mpAudioTrack-stop(); mpAudioTrack-stop();

只要简单地调用成员函数start()和stop()即可。AudioTrack和AudioFlinger的通信机制通常,AudioTrack和AudioFlinger并不在同一个进程中,它们通过android中的binder机制建立联系。AudioFlinger是android中的一个service,在android启动时就已经被加载。下面这张图展示了他们两个的关系: 图一AudioTrack和AudioFlinger的关系我们可以这样理解这张图的含义:audio_track_cblk_t实现了一个环形FIFO;AudioTrack是FIFO的数据生产者;AudioFlinger是FIFO的数据消费者。建立联系的过程下面的序列图展示了AudioTrack和AudioFlinger建立联系的过程: 图二AudioTrack和AudioFlinger建立联系解释一下过程:Framework或者Java层通过JNI,new AudioTrack();根据StreamType等参数,通过一系列的调用getOutput();如有必要,AudioFlinger根据StreamType打开不同硬件设备;AudioFlinger为该输出设备创建混音线程: MixerThread(),并把该线程的id作为getOutput()的返回值返回给AudioTrack;AudioTrack通过binder机制调用AudioFlinger的createTrack();AudioFlinger注册该AudioTrack到MixerThread中;AudioFlinger创建一个用于控制的TrackHandle,并以IAudioTrack这一接口作为createTrack()的返回 值;AudioTrack通过IAudioTrack接口,得到在AudioFlinger中创建的 FIFO(audio_track_cblk_t);AudioTrack创建自己的监控线程:AudioTrackThread;自此,AudioTrack建立了和AudioFlinger的全部联系工作,接下来,AudioTrack可以:通过IAudioTrack接口控制该音轨的状态,例如start,stop,pause等等;通过对FIFO的写入,实现连续的音频播放;监控线程监控事件的发生,并通过audioCallback回调函数与用户程序进行交互;FIFO的管理 audio_track_cblk_taudio_track_cblk_t这个结构是FIFO实现的关键,该结构是在createTrack的时候,由AudioFlinger申请相 应的内存,然后通过IMemory接口返回AudioTrack的,这样AudioTrack和AudioFlinger管理着同一个 audio_track_cblk_t,通过它实现了环形FIFO,AudioTrack向FIFO中写入音频数据,AudioFlinger从FIFO 中读取音频数据,经Mixer后送给AudioHardware进行播放。audio_track_cblk_t的主要数据成员: user -- AudioTrack当前的写位置的偏移

userBase -- AudioTrack写偏移的基准位置,结合user的值方可确定真实的FIFO地址指针

server -- AudioFlinger当前的读位置的偏移

serverBase -- AudioFlinger读偏移的基准位置,结合server的值方可确定真实的FIFO地址指针 frameCount -- FIFO的大小,以音频数据的帧为单位,16bit的音频每帧的大小是2字节 buffers -- 指向FIFO的起始地址 out -- 音频流的方向,对于AudioTrack,out=1,对于AudioRecord,out=0audio_track_cblk_t的主要成员函数:framesAvailable_l()和framesAvailable()用于获取FIFO中可写的空闲空间的大小,只是加锁和不加锁的区别。

Android音视频【十二】使用OpenSLES和AudioTrack进行播放PCM

本节我们学习下如何播放pcm数据,在Android中有两种方法:一种是使用java层的 AudioTrack 方法,一种是使用底层的 OpenSLES 直接在 jni 层调用系统的 OpenSLES的c方法 实现。

两种使用场景不一样:

AudioTrack 一般用于 比如本地播放一个pcm文件/流,又或者播放解码后的音频的pcm流,API较简单。

OpenSLES 一般用于一些播放器中开发中,比如音频/视频播放器,声音/音频的播放采用的OpenSLES,一是播放器一般是c/c++实现,便于直接在c层调用OpenSLES的API,二也是如果用AudioTrack进行播放,务必会带来java和jni层的反射调用的开销,API较复杂。

可以根据业务自行决定来进行选择。

AudioTrack的方式使用较简单,直接在java层。

指定采样率,采样位数,声道数进行创建。

其中44100是采样率, AudioFormat.CHANNEL_OUT_STEREO 为双声道,还有 CHANNEL_OUT_MONO 单声道。 AudioFormat.ENCODING_PCM_16BIT 为采样位数16位,还有 ENCODING_PCM_8BIT 8位。 minBufferSize 是播放器缓冲的大小,也是根据采样率和采样位数,声道数 进行获取,只有满足最小的buffer才去操作底层进程播放。

最后一个参数mode。可以指定的值有 AudioTrack.MODE_STREAM 和 AudioTrack.MODE_STATIC 。

MODE_STREAM 适用于大多数的场景,比如动态的处理audio buffer,或者播放很长的音频文件,它是将audio buffers从java层传递到native层。音频播放时音频数据从Java流式传输到native层的创建模式。

MODE_STATIC 适用场景,比如播放很短的音频,它是一次性将全部的音频资源从java传递到native层。音频数据在音频开始播放前仅从Java传输到native层的创建模式。

是的,就这么一个方法。注意此方法是同步方法,是个耗时方法,一般是开启一个线程循环调用 write 方法进行写入。

注意在调用 write 方法前需要调用 audioTrack.play() 方法开始播放。

因为是pcm裸数据,无法像mediaplayer一样提供了API。所以需要自己处理下。可以利用 getPlaybackHeadPosition 方法。

getPlaybackHeadPosition() 的意思是返回以帧为单位表示的播放头位置

getPlaybackRate() 的意思是返回以Hz为单位返回当前播放采样率。

所以当前播放时间可以通过如下方式获取

OpenSLES:(Open Sound Library for Embedded Systems).

OpenSLES是跨平台是针对嵌入式系统精心优化的硬件音频加速API。使用OpenSLES进行音频播放的好处是可以不依赖第三方。比如一些音频或者视频播放器中都是用OpenSLES进行播放解码后的pcm的,这样免去了和java层的交互。

在Android中使用OpenSLES首先需要把Android 系统提供的so链接到外面自己的so。在CMakeLists.txt脚本中添加链接库OpenSLES。库的名字可以在 类似如下目录中

需要去掉lib

然后导入头文件即可使用了OpenSLES提供的底层方法了。

创建使用的步骤大致分为:

一个 SLObjectItf 里面可能包含了多个Interface,获取Interface通过 GetInterface 方法,而 GetInterface 方法的地2个参数 SLInterfaceID 参数来指定到的需要获取Object里面的那个Interface。比如通过指定 SL_IID_ENGINE 的类型来获取 SLEngineItf 。我们可以通过 SLEngineItf 去创建各种Object,例如播放器、录音器、混音器的Object,然后在用这些Object去获取各种Interface去实现各种功能。

如上所说,SLEngineItf可以创建混音器的Object。

在创建播放器前需要创建音频的配置信息(比如采样率,声道数,每个采样的位数等)

开始播放后会不断的回调这个 pcmBufferCallBack 函数将音频数据压入队列

(*pcmBufferQueue)-RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);

如果想要暂停播放参数直接设置为SL_PLAYSTATE_PAUSED,若暂停后继续播放设置参数为SL_PLAYSTATE_PLAYING即可。若想要停止播放参数设置为SL_PLAYSTATE_STOPPED即可。

首先获取播放器的用于控制音量的接口SLVolumeItf pcmVolumePlay

然后动态设置

首先也是获取播放器的用于控制音量的接口SLMuteSoloItf pcmMutePlay

然后动态设置

看起来控制还是蛮简单的哈。先熟悉这么多,OpenSLES还是蛮强大的。

混音pcmjava的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于混音remix、混音pcmjava的信息别忘了在本站进行查找喔。

The End

发布于:2022-11-26,除非注明,否则均为首码项目网原创文章,转载请注明出处。