Android开发--仿微信语音对讲录音

Android 仿微信语音聊天,正式加入字节跳动 /** 恢复标志位 */ private void resetState() { isRecording = false; isReady = false; changeState(STATE_NORMAL); mTime = 0; } private boolean wantCancel(int x, int y) { if (x < 0 || x > getWidth()) { return true; } // 零点在左下角? if (y < -DISTANCE_CANCEL_Y 阅读详情

自微信出现以来取得了很好的成绩,语音对讲的实现更加方便了人与人之间的交流。今天来实践一下微信的语音对讲的录音实现,这个也比较容易实现。在此,我将该按钮封装成为一个控件,并通过策略模式的方式实现录音和界面的解耦合,以方便我们在实际情况中对录音方法的不同需求(例如想要实现wav格式的编码时我们也就不能再使用MediaRecorder,而只能使用AudioRecord进行处理)。
效果图:
title=

实现思路

1.在微信中我们可以看到实现语音对讲的是通过点按按钮来完成的,因此在这里我选择重新自己的控件使其继承自Button并重写onTouchEvent方法,来实现对录音的判断。
2.在onTouchEvent方法中,
当我们按下按钮时,首先显示录音的对话框,然后调用录音准备方法并开始录音,接着开启一个计时线程,每隔0.1秒的时间获取一次录音音量的大小,并通过Handler根据音量大小更新Dialog中的显示图片;
当我们移动手指时,若手指向上移动距离大于50,在Dialog中显示松开手指取消录音的提示,并将isCanceled变量(表示我们最后是否取消了录音)置为true,上移动距离小于20时,我们恢复Dialog的图片,并将isCanceled置为false;
当抬起手指时,我们首先关闭录音对话框,接着调用录音停止方法并关闭计时线程,然后我们判断是否取消录音,若是的话则删除录音文件,否则判断计时时间是否太短,最后调用回调接口中的recordEnd方法。
3.在这里为了适应不同的录音需求,我使用了策略模式来进行处理,将每一个不同的录音方法视为一种不同的策略,根据自己的需要去改写。

注意问题

1.在onTouchEvent的返回值中应该返回true,这样才能屏蔽之后其他的触摸事件,否则当手指滑动离开Button之后将不能在响应我们的触摸方法。
2.不要忘记为自己的App添加权限:

?
1
2
3
<code class = "hljs" xml= "" >    <uses-permission android:name= "android.permission.RECORD_AUDIO" >
     <uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE" >
     <uses-permission android:name= "android.permission.READ_EXTERNAL_STORAGE" ></uses-permission></uses-permission></uses-permission></code>

代码参考

RecordButton 类,我们的自定义控件,重新复写了onTouchEvent方法

?
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<code class = "hljs" java= "" > package com.example.recordtest;
 
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
public class RecordButton extends Button {
 
     private static final int MIN_RECORD_TIME = 1 ; // 最短录音时间,单位秒
     private static final int RECORD_OFF = 0 ; // 不在录音
     private static final int RECORD_ON = 1 ; // 正在录音
 
     private Dialog mRecordDialog;
     private RecordStrategy mAudioRecorder;
     private Thread mRecordThread;
     private RecordListener listener;
 
     private int recordState = 0 ; // 录音状态
     private float recodeTime = 0 .0f; // 录音时长,如果录音时间太短则录音失败
     private double voiceValue = 0.0 ; // 录音的音量值
     private boolean isCanceled = false ; // 是否取消录音
     private float downY;
 
     private TextView dialogTextView;
     private ImageView dialogImg;
     private Context mContext;
 
     public RecordButton(Context context) {
         super (context);
         // TODO Auto-generated constructor stub
         init(context);
     }
 
     public RecordButton(Context context, AttributeSet attrs, int defStyle) {
         super (context, attrs, defStyle);
         // TODO Auto-generated constructor stub
         init(context);
     }
 
     public RecordButton(Context context, AttributeSet attrs) {
         super (context, attrs);
         // TODO Auto-generated constructor stub
         init(context);
     }
 
     private void init(Context context) {
         mContext = context;
         this .setText(按住 说话);
     }
 
     public void setAudioRecord(RecordStrategy record) {
         this .mAudioRecorder = record;
     }
 
     public void setRecordListener(RecordListener listener) {
         this .listener = listener;
     }
 
     // 录音时显示Dialog
     private void showVoiceDialog( int flag) {
         if (mRecordDialog == null ) {
             mRecordDialog = new Dialog(mContext, R.style.Dialogstyle);
             mRecordDialog.setContentView(R.layout.dialog_record);
             dialogImg = (ImageView) mRecordDialog
                     .findViewById(R.id.record_dialog_img);
             dialogTextView = (TextView) mRecordDialog
                     .findViewById(R.id.record_dialog_txt);
         }
         switch (flag) {
         case 1 :
             dialogImg.setImageResource(R.drawable.record_cancel);
             dialogTextView.setText(松开手指可取消录音);
             this .setText(松开手指 取消录音);
             break ;
 
         default :
             dialogImg.setImageResource(R.drawable.record_animate_01);
             dialogTextView.setText(向上滑动可取消录音);
             this .setText(松开手指 完成录音);
             break ;
         }
         dialogTextView.setTextSize( 14 );
         mRecordDialog.show();
     }
 
     // 录音时间太短时Toast显示
     private void showWarnToast(String toastText) {
         Toast toast = new Toast(mContext);
         View warnView = LayoutInflater.from(mContext).inflate(
                 R.layout.toast_warn, null );
         toast.setView(warnView);
         toast.setGravity(Gravity.CENTER, 0 , 0 ); // 起点位置为中间
         toast.show();
     }
 
     // 开启录音计时线程
     private void callRecordTimeThread() {
         mRecordThread = new Thread(recordThread);
         mRecordThread.start();
     }
 
     // 录音Dialog图片随录音音量大小切换
     private void setDialogImage() {
         if (voiceValue < 600.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_01);
         } else if (voiceValue > 600.0 && voiceValue < 1000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_02);
         } else if (voiceValue > 1000.0 && voiceValue < 1200.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_03);
         } else if (voiceValue > 1200.0 && voiceValue < 1400.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_04);
         } else if (voiceValue > 1400.0 && voiceValue < 1600.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_05);
         } else if (voiceValue > 1600.0 && voiceValue < 1800.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_06);
         } else if (voiceValue > 1800.0 && voiceValue < 2000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_07);
         } else if (voiceValue > 2000.0 && voiceValue < 3000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_08);
         } else if (voiceValue > 3000.0 && voiceValue < 4000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_09);
         } else if (voiceValue > 4000.0 && voiceValue < 6000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_10);
         } else if (voiceValue > 6000.0 && voiceValue < 8000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_11);
         } else if (voiceValue > 8000.0 && voiceValue < 10000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_12);
         } else if (voiceValue > 10000.0 && voiceValue < 12000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_13);
         } else if (voiceValue > 12000.0 ) {
             dialogImg.setImageResource(R.drawable.record_animate_14);
         }
     }
 
     // 录音线程
     private Runnable recordThread = new Runnable() {
 
         @Override
         public void run() {
             recodeTime = 0 .0f;
             while (recordState == RECORD_ON) {
                 {
                     try {
                         Thread.sleep( 100 );
                         recodeTime += 0.1 ;
                         // 获取音量,更新dialog
                         if (!isCanceled) {
                             voiceValue = mAudioRecorder.getAmplitude();
                             recordHandler.sendEmptyMessage( 1 );
                         }
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             }
         }
     };
 
     @SuppressLint (HandlerLeak)
     private Handler recordHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             setDialogImage();
         }
     };
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // TODO Auto-generated method stub
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN: // 按下按钮
             if (recordState != RECORD_ON) {
                 showVoiceDialog( 0 );
                 downY = event.getY();
                 if (mAudioRecorder != null ) {
                     mAudioRecorder.ready();
                     recordState = RECORD_ON;
                     mAudioRecorder.start();
                     callRecordTimeThread();
                 }
             }
             break ;
         case MotionEvent.ACTION_MOVE: // 滑动手指
             float moveY = event.getY();
             if (downY - moveY > 50 ) {
                 isCanceled = true ;
                 showVoiceDialog( 1 );
             }
             if (downY - moveY < 20 ) {
                 isCanceled = false ;
                 showVoiceDialog( 0 );
             }
             break ;
         case MotionEvent.ACTION_UP: // 松开手指
             if (recordState == RECORD_ON) {
                 recordState = RECORD_OFF;
                 if (mRecordDialog.isShowing()) {
                     mRecordDialog.dismiss();
                 }
                 mAudioRecorder.stop();
                 mRecordThread.interrupt();
                 voiceValue = 0.0 ;
                 if (isCanceled) {
                     mAudioRecorder.deleteOldFile();
                 } else {
                     if (recodeTime < MIN_RECORD_TIME) {
                         showWarnToast(时间太短  录音失败);
                         mAudioRecorder.deleteOldFile();
                     } else {
                         if (listener != null ) {
                             listener.recordEnd(mAudioRecorder.getFilePath());
                         }
                     }
                 }
                 isCanceled = false ;
                 this .setText(按住 说话);
             }
             break ;
         }
         return true ;
     }
 
     public interface RecordListener {
         public void recordEnd(String filePath);
     }
}
</code>

Dialog布局:

?
1
2
3
4
5
6
7
8
<code class = "hljs" xml= "" ><!--?xml version= 1.0 encoding=utf- 8 ?-->
<linearlayout android:background= "@drawable/record_bg" android:gravity= "center" android:layout_gravity= "center" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:orientation= "vertical" android:padding= "20dp" xmlns:android= "http://schemas.android.com/apk/res/android" >
 
     <imageview android:id= "@+id/record_dialog_img" android:layout_height= "wrap_content" android:layout_width= "wrap_content" >
 
     <textview android:id= "@+id/record_dialog_txt" android:layout_height= "wrap_content" android:layout_margintop= "5dp" android:layout_width= "wrap_content" android:textcolor= "@android:color/white" >
 
</textview></imageview></linearlayout></code>

录音时间太短的Toast布局:

?
1
2
3
4
5
6
7
8
<code class = "hljs" xml= "" ><!--?xml version= 1.0 encoding=utf- 8 ?-->
<linearlayout android:background= "@drawable/record_bg" android:gravity= "center" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:orientation= "vertical" android:padding= "20dp" xmlns:android= "http://schemas.android.com/apk/res/android" >
 
     <imageview android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:src= "@drawable/voice_to_short" >
 
     <textview android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:text= "时间太短" android:textcolor= "@android:color/white" android:textsize= "15sp" >
 
</textview></imageview></linearlayout></code>

自定义的Dialogstyle,对话框样式

?
1
2
3
4
5
6
7
8
<code applescript= "" class = "hljs" ><style name= "Dialogstyle" type= "text/css" ><item name=android:windowBackground> @android :color/transparent</item>
         <item name=android:windowFrame> @null </item>
         <item name=android:windowNoTitle> true </item>
         <item name=android:windowIsFloating> true </item>
         <item name=android:windowIsTranslucent> true </item>
         <item name=android:windowAnimationStyle> @android :style/Animation.Dialog</item>
         <!-- 显示对话框时当前的屏幕是否变暗 -->
         <item name=android:backgroundDimEnabled> false </item></style></code>

RecordStrategy 录音策略接口

?
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
<code class = "hljs" java= "" > package com.example.recordtest;
 
/**
  * RecordStrategy 录音策略接口
  * @author acer
  */
public interface RecordStrategy {
 
     /**
      * 在这里进行录音准备工作,重置录音文件名等
      */
     public void ready();
     /**
      * 开始录音
      */
     public void start();
     /**
      * 录音结束
      */
     public void stop();
 
     /**
      * 录音失败时删除原来的旧文件
      */
     public void deleteOldFile();
 
     /**
      * 获取录音音量的大小
      * @return
      */
     public double getAmplitude();
 
     /**
      * 返回录音文件完整路径
      * @return
      */
     public String getFilePath();
 
}
</code>

个人写的一个录音实践策略

?
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<code class = "hljs" java= "" > package com.example.recordtest;
 
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import android.media.MediaRecorder;
import android.os.Environment;
 
public class AudioRecorder implements RecordStrategy {
 
     private MediaRecorder recorder;
     private String fileName;
     private String fileFolder = Environment.getExternalStorageDirectory()
             .getPath() + /TestRecord;
 
     private boolean isRecording = false ;
 
     @Override
     public void ready() {
         // TODO Auto-generated method stub
         File file = new File(fileFolder);
         if (!file.exists()) {
             file.mkdir();
         }
         fileName = getCurrentDate();
         recorder = new MediaRecorder();
         recorder.setOutputFile(fileFolder + / + fileName + .amr);
         recorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置MediaRecorder的音频源为麦克风
         recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR); // 设置MediaRecorder录制的音频格式
         recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置MediaRecorder录制音频的编码为amr
     }
 
     // 以当前时间作为文件名
     private String getCurrentDate() {
         SimpleDateFormat formatter = new SimpleDateFormat(yyyy_MM_dd_HHmmss);
         Date curDate = new Date(System.currentTimeMillis()); // 获取当前时间
         String str = formatter.format(curDate);
         return str;
     }
 
     @Override
     public void start() {
         // TODO Auto-generated method stub
         if (!isRecording) {
             try {
                 recorder.prepare();
                 recorder.start();
             } catch (IllegalStateException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
 
             isRecording = true ;
         }
 
     }
 
     @Override
     public void stop() {
         // TODO Auto-generated method stub
         if (isRecording) {
             recorder.stop();
             recorder.release();
             isRecording = false ;
         }
 
     }
 
     @Override
     public void deleteOldFile() {
         // TODO Auto-generated method stub
         File file = new File(fileFolder + / + fileName + .amr);
         file.deleteOnExit();
     }
 
     @Override
     public double getAmplitude() {
         // TODO Auto-generated method stub
         if (!isRecording) {
             return 0 ;
         }
         return recorder.getMaxAmplitude();
     }
 
     @Override
     public String getFilePath() {
         // TODO Auto-generated method stub
         return fileFolder + / + fileName + .amr;
     }
 
}
</code>

MainActivity

?
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
<code class = "hljs" java= "" > package com.example.recordtest;
 
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
 
public class MainActivity extends Activity {
 
     RecordButton button;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         button = (RecordButton) findViewById(R.id.btn_record);
         button.setAudioRecord( new AudioRecorder());
     }
 
 
     @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 ;
     }
 
}
</code>


Android——实现微信语音音量变化说话 先上图:这里将绑定下面的Button和Dialog,当然使用的是自定义View。完了会附代码。用代码说话:自定义Buttonpublic class AudioButton extends Button { private static final int DISTANCE_Y_CANCEL = 50; private static final int STATE_NORMAL = 阅读详情

相关推荐

微信隐藏录音功能全解析:从原理到自动化工作流实践

音频录制与文件管理是数字信息处理的基础环节,其核心原理在于将声音信号通过模数转换存储为特定格式的音频文件。在移动应用场景中,微信内置的录音功能通过沙盒存储机制,实现了高质量音频的本地化保存,解决了语音消息无法持久化管理的痛点。这项技术的工程价值在于将信息捕获环节无缝嵌入高频使用场景,显著降低工具切换成本。对于开发者而言,结合自动化脚本与云端同步,可以构建从音频录制到文字转录的完整工作流,有效支持技术复盘、会议记录等知识管理需求。本文以微信录音功能为例,详细拆解了其文件存储路径、操作流程,并提供了基于Task

weixin_29038345的博客 354

Android仿微信录音功能,自定义控件的设计技巧

最近由于需要做一个录音功能(/嘘 悄悄透露一下,千万别告诉红薯,就是新版本的OSC客户端噢),起初打算采用仿微信的录音方式,最后又改成了QQ的录音方式,之前的微信录音控件也就白写了[大哭]。之前有很多朋友在问我自定义控件应该怎么学习,遂正好拿出来讲讲喽,没来得及截效果图,大家就...

chuomengzhi7371的博客 449

AAOS系列之(七) --- AudioRecord录音逻辑分析(一)

本文分析了Android Automotive OS(AAOS)中的AudioRecord初始化过程。文章首先强调了录音模块在AAOS开发中的重要性,介绍了采样率、通道数和音频格式等关键参数设置的必要性。通过代码示例展示了APP端录音的基础实现,包括内建APP和第三方APP的不同调用方式。重点解析了AudioRecord构造方法中channelConfig参数的处理流程,特别是从legacy配置到channel mask的转换过程

sfhong2008的专栏 1056

android按住录音按钮_Android仿微信录音功能

提要:需求是开发类似微信发语音的功能,没有语音转文字。网上看了一些代码,不能拿来直接用,部分代码逻辑有问题,所以想把自己的代码贴出来,仅供参考。功能:a、设置最大录音时长和录音倒计时(为了方便测试,最大时长设置为15秒,开始倒计时设置为7秒)b、在录音之前检查录音和存储权限源码:1、录音对话框管理类DialogManager:/*** 功能:录音对话框管理类*/public class Dialo...

weixin_28868927的博客 728

Android 仿微信语音录音小控件

Android 仿微信地语音录音小控件 效果看着和微信的效果基本上一样,对录音做了限制,录音时间不能少于一秒,最大不超过一分钟,并且设置了录音倒计时提示等

Android仿微信录制语音功能

本文实例为大家分享了Android仿微信录制语音的具体代码,供大家参考,具体内容如下 前言 我把录音分成了两部分 1.UI界面,弹窗读秒 2.一个类(包含开始、停止、创建文件名功能) 第一部分 由于6.0权限问题,点击按钮申请权限通过则弹窗,如何申请权限 弹窗布局popw_record.xml <?xml version=1.0 encoding=utf-8?> <LinearLayout xmlns:android=http://schemas.android.com/apk/res/android android:layout_width=match_parent

Android利用MedioRecorder使用仿微信语音录音以及播放(总结)

今天模仿着微信的发送语音功能做了一下,现将步骤以及其中遇到的问题记录一下,以便以后查看。 实现功能:1、录制语音并显示到列表中;2、点击列表中的语音自动进行播放;3、录制语音时以及播放语音时的动画效果显示。 最终界面效果如下: 现在说一下大致的步骤: 一、布局样式 1、拉界面。很简单,上面一个ListView或者RecyclerView用来显示语音列表,下面用线性布局做一下。 ...

lpCrazyBoy的博客 3669

国密(3)- 预主密钥/主密钥计算和Finished消息的加解密

国密的ECC-SM3-SM4-CBC模式下的预主密钥和主密钥的生成过程介绍,以及Finished消息的加解密介绍

weixin_43408952的博客 6514

音频压缩工具——Speex的使用(Android&iOS语音录音技术预演)

要在Android/iOS端实现语音对讲,原型为微信与米聊,开始预演所用技术,找到以下资料。 在Android开发中,需要录音并发送到对方设备上。这时问题来了,手机常会是GPRS、3G等方式上网,所以节省流量是非常关键的,使用Speex来压缩音频文件,可以将音频压文件小数倍。 1.去Speex官网下载最新Speex源码。 2.创建一个新的应用(我创建的应用名为Audi

zcmain的专栏 5121

Sciter教程_00_创建HelloWorld工程

本教程记录Sciter工程的创建和编译运行的过程。

qq_36707072的博客 2278

基于FPGA的工业相机项目大赏1——cmos线阵相机

●CMOS CL0402 作为图像传感芯片,曝光后产生一定格式的图像数据;●FPGA ZYNQ7020 作为主控芯片负责cmos驱动控制、图像像素的采集、cmos寄存器的配置、cameralink协议的实现、上位机串口通信的实现;●Cameralink端子实现图像数据的高速串行发送,将数据传送至采集卡PC端。系统工作流程如下: FPGA 上电加载配置程序,计算机通过异步串口向 FPGA 发送控制信息,FPGA对接收的控制信息进行分析判断。

weixin_41895751的博客 6403

IOS、安卓IM语音聊天开发初探部分心得——本地音频处理篇(上)

http://cvito.net/index.php/archives/854 最近的项目的内容开始涉及到一定的IM语音对讲的内容,而笔者从未接触过此类开发,也只是在摸索中一点点探索学习,几日下来略有了一点收获,以博客的形式跟诸位看官分享 先说明一下什么是IM语音聊天,IM全称Instant Messenger,即时通讯,简单的来说就是MSN ,QQ一类的聊天软件。而IM语音聊

u011248345的专栏 1801

深入解析MTK DRM初始化流程:从KMS模块到组件框架

本文深入解析了联发科(MTK)平台上DRM显示框架的初始化流程。从内核模块加载、Component框架管理依赖,到核心的mtk_drm_bind函数构建KMS对象(CRTC、Plane、Encoder、Connector),最终完成DRM设备注册并创建用户空间接口。文章结合调试经验,为开发者梳理了从驱动启动到屏幕点亮的完整路径与关键排查点。

onion的博客 769

p2p视频通话和p2p语音对讲的实现

手机app上查看设备时,首先拉取了设备上的视频流,音频流虽然开了,但是默认是静音的,没有传输音频数据。1.p2p视频传输这块,我们使用深圳市好游科技有限公司开发的p2p sdk,接口非常简单,穿透能力强,双对称型 NAT也能打洞成功。1、首先要解决p2p视频传输的问题:视频对讲流量大,通过服务器中转的话,既浪费服务器昂贵的宽带资源,视频延时长也严重影响用户体验。网络上能够找到的库非常少,webrtc算是比较全面的解决方案,但是它却非常复杂,编译后的库也很大,不适合在嵌入式智能家居设备上使用。

hey5178的博客 545

在 SAP HANA XS Advanced 中用 InA 打通 Web 化分析数据访问:架构原理、部署步骤与实战指南

本文介绍了如何在SAP HANA环境中通过InA协议为XS Advanced应用和分析工具提供实时数据访问。主要内容包括:InA协议的作用是将HANA计算视图等模型通过HTTP(S)暴露为实时数据源;SAP HANA Analytics Adapter(xsahaa)是关键组件,负责协议转换和连接管理;部署包含本地和云端两种形式,均需建立XSA空间与租户数据库的映射;实施前需准备HDI容器、适配器构建和网络验证;安全方面通过XSA用户角色和HDI授权双重控制;文章还提供了与SAP Analytics Clo

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 261

2023年GESP12月认证C++五级真题解析

例如,如果a=4,那么4是最⼩的幸运数,而1不是,但我们可以连续对1做3次+1操作,使其变为4,所以我们可以说,1幸运化后的结果是 4。【题目大意】给一个完全平方数的标准,推导出哪些是超级幸运数,然后判断N个数字中,哪些是超级幸运数,是的输出“lucky”,不是的输出离该数字最近的比它大的数字。前一个数字,下标是j的数字是偶数,后面的数字下标是j+1的是奇数,按照要求,偶数在奇数的后面,要交换。A函数时间复杂度是O(n/2),B函数算法是O(sqrt(n)),大部分情况后者是优的,值更小。

lan_in的博客 1008

Android基于UDP语音对讲系列(四)

微信公众号:Android疯子在前三篇得基础上,这次研究了组播功能。非常感谢https://blog.youkuaiyun.com/jspping/article/details/64438515得...

stormxiaofeng的博客 908

【STM32 Blue Pill编程】-STM32CubeIDE开发环境搭建与点亮LED

本文为使用 STM32Cube IDE 的 STM32 Blue Pill 入门教程。 在本文中,我们将介绍如何控制 STM32 Blue Pill 的 GPIO 引脚。以及开发环境搭建。 本系列文章将重点介绍采用 ARM Cortex-M 的 BluepellSTM32F103C8 微控制器以及如何使用 STM32CubeIDE 对其进行编程。 STM32CubeIDE是STM32官网提供的IDE

视觉与物联智能 1074

IM语音

IOS、安卓IM语音聊天开发初探部分心得——本地音频处理篇(上) 发表于 2013 年 5 月 27 日 由 水德星君 最近的项目的内容开始涉及到一定的IM语音对讲的内容,而笔者从未接触过此类开发,也只是在摸索中一点点探索学习,几日下来略有了一点收获,以博客的形式跟诸位看官分享 先说明一下什么是IM语音聊天,IM全称Instant Messenger,即时通讯,简单的来说

u012490953的专栏 1844

合肥工业大学操作系统实验9

依据操作系统课程所介绍的页面置换算法与动态内存分配的原理,本实验的目的是掌握OPT、FIFO、LRU、LFU、Clock等页面置换算法;掌握可用空间表及分配方法;掌握边界标识法以及伙伴系统的内存分配方法和回收方法。从而深入理解操作系统的内存管理。从而加深对教材上页面置换算法与动态内存分配的理解。

Martin-share的博客 1096

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

微信小程序/App对接Coze AI与TTS原生语音流式输出实战(附完整源码

3. **App 端 Native.js 权限**:在使用 `plus.speech` 或 Native.js 调用本地语音时,务必在 `manifest.json` 的 App 模块配置中勾选上 `Speech` 模块,否则会导致 `is not a function` 等各种未定义报错。而在实际项目中,仅仅是简单的“一问一答”往往不能满足用户体验的要求,我们需要**流式输出(打字机效果)**、**多轮对话状态保持**、以及**TTS原生语音播报**和**微信风格的语音通话页面**。

lee_4524682的博客 345

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

上一篇: 1.写一个Mvc框架_超轻量级MVC框架的设计和实现
下一篇: Android中的动态加载机制
codepython
博客等级 码龄20年 125粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值