精华内容
下载资源
问答
  • 标题微信公众号如何实现录音功能(包含下载到服务器+AMR转化为MP3格式) 我们在微信公众号开发的过程中,总会遇到各种各样的需求和难题,然而最大的难题是微信公众号的开发文档,说多了都是泪,总结起来就是一个‘虚...

    标题微信公众号如何实现录音功能(包含下载到服务器+AMR转化为MP3格式)

    我们在微信公众号开发的过程中,总会遇到各种各样的需求和难题,然而最大的难题是微信公众号的开发文档,说多了都是泪,总结起来就是一个‘虚’字了得,不过项目还是要做的,下面是我在开发过程中的一点心得,希望对大家能有帮助,不足之处,请大家多多指正,共同学习。
    在这里插入图片描述
    一、 微信音频接口说明
    具体以最新官方文档为准原文地址: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#23

    1.1.1 权限的声明
    在使用前需要申明相对应的权限,具体使用以个人卡发需求为主。

     wx.config({
                    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                    appId: appId, // 必填,公众号的唯一标识
                    timestamp: timestamp, // 必填,生成签名的时间戳
                    nonceStr: nonceStr, // 必填,生成签名的随机串
                    signature: signature,// 必填,签名,见附录1
                    jsApiList: [            
                        'startRecord',
                        'stopRecord',
                        'onVoiceRecordEnd',             
                        'downloadVoice',
                        'uploadVoice'
                    ] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
                });
    

    1.1.2 开始录音接口

    wx.startRecord();
    

    1.1.3 停止录音接口

    wx.stopRecord({
    success: function (res) {
    var localId = res.localId;
    }
    });
    

    1.1.4 监听录音自动停止接口

    wx.onVoiceRecordEnd({
    // 录音时间超过一分钟没有停止的时候会执行 complete 回调
    complete: function (res) {
    var localId = res.localId;
    }
    });
    

    1.1.5 播放语音接口

    wx.playVoice({
    localId: '' // 需要播放的音频的本地ID,由stopRecord接口获得
    });
    

    1.1.6 暂停播放接口

    wx.pauseVoice({
    localId: '' // 需要暂停的音频的本地ID,由stopRecord接口获得
    });
    

    1.1.7 停止播放接口

    wx.stopVoice({
    localId: '' // 需要停止的音频的本地ID,由stopRecord接口获得
    });
    

    1.1.8 监听语音播放完毕接口

    wx.onVoicePlayEnd({
    success: function (res) {
    var localId = res.localId; // 返回音频的本地ID
    }
    });
    

    1.1.9 上传语音接口

    wx.uploadVoice({
    localId: '', // 需要上传的音频的本地ID,由stopRecord接口获得
    isShowProgressTips: 1, // 默认为1,显示进度提示
    success: function (res) {
    var serverId = res.serverId; // 返回音频的服务器端ID
    }
    });
    

    备注:上传语音有效期3天,可用微信多媒体接口下载语音到自己的服务器,此处获得的 serverId 即 media_id,参考文档 .目前多媒体文件下载接口的频率限制为10000次/天,如需要调高频率,请登录微信公众平台,在开发 - 接口权限的列表中,申请提高临时上限。

    1.2.0下载语音接口

    wx.downloadVoice({
    serverId: '', // 需要下载的音频的服务器端ID,由uploadVoice接口获得
    isShowProgressTips: 1, // 默认为1,显示进度提示
    success: function (res) {
    var localId = res.localId; // 返回音频的本地ID
    }
    });
    

    1.2.1智能接口
    识别音频并返回识别结果接口

    wx.translateVoice({
    localId: '', // 需要识别的音频的本地Id,由录音相关接口获得
    isShowProgressTips: 1, // 默认为1,显示进度提示
    success: function (res) {
    alert(res.translateResult); // 语音识别的结果
    }
    });
    

    二、 H5页面功能实现

    封装一下录音 的方法,在需要用的地址直接调用就可以。

      WechatEvent: {
                 //开始录音
                 Wechat_StartRecord: function () {
                     wx.startRecord({                  
                         cancel: function () {
                           //用户取消权限的处理...
                         }
                     });
                 },
                 //停止录音
                 Wechat_StopRecord: function () {
                     wx.stopRecord({
                         success: function (res) {
                             var localId = res.localId;
                              //上传录音文件到微信服务器
                                  wx.uploadVoice({
                                     localId: localId, // 需要上传的音频的本地ID,由stopRecord接口获得
                                     isShowProgressTips: 1, // 默认为1,显示进度提示
                                     success: function (res) {
                                         var serverId = res.serverId; // 返回音频的服务器端ID
                                      WechatEvent.DownLoadAudio(serverId );//上传到自己服务器下载的方法....                                    
    
                                     }
                             });
                         }
                     });
                 },
    
     //自己服务器下载微信服务器录音文件的方法(服务器的方法)
      	 DownLoadAudio: function (serverId) {
    		jQuery.ajax({
    		type: "POST",//使用get方法访问后台
    		dataType: "json",
    		url: "自己的接口地址", 
    		data: { "serverId": serverId},   //上传到微信服务器后返回的serverId
    			success: function (data) {
                     //服务器处理成功的时候页面自己处理
                       	  },
                      	   }
                     });
                 },
    

    三、服务器端下载微信音频文件部分

    服务器利用ffmpeg.exe 将从微信服务器上获取的录音文件**,转化为自己想要的格式**

    核心代码:

    3.1.1 下载微信服务器的录音文件到服务器指定的文件夹下
    核心代码:

                   string url =@”https://api.weixin.qq.com/cgi-bin/media/get?access_token={0}&media_id={1}“;
                      url = string.Format(url, files.Content, files.FileSize);   //  微信服务器下载录音问价的地址
                    string strpath = string.Empty;
                     string savepath = string.Empty;
                     HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
                     req.Method = "GET";
                     using (WebResponse wr = req.GetResponse())
                     {
                         HttpWebResponse myResponse = (HttpWebResponse)req.GetResponse();
                         strpath = myResponse.ResponseUri.ToString();//下载录音文件的地址
                         WebClient mywebclient = new WebClient();
                         savepath = "服务器上文件夹的路径"+ ”录音文件的名字“ + ".amr";//微信服务器下载的                                                                                                                          录音文件保存的地址
                         try
                          {
                      mywebclient.DownloadFile(strpath, savepath);//下载录音文件                    
                         }
                         catch (Exception ex)
                         {                     
                         }
                     }
                     ```
    **备注:因为编码方式不同,测试的时候要在真机上测试,在微信开发者工具上会存在一定问题。**
    
    **3.1.2 录音文件格式的转化**1) 核心工具ffmpeg.exe ,百度自己下载即可
    ```csharp
    	string outStr = "";
    	Process process = new Process();
    			try
    				{
                    	 string filepath = System.AppDomain.CurrentDomain.BaseDirectory;
                   	  process.StartInfo.FileName = filepath+"ffmpeg" + "\\" + "ffmpeg.exe"; // 这里也可以指定  ffmpeg的绝对路径
                     process.StartInfo.Arguments = @" -i " + "下载到服务器的录音地址" + " " + (要转化到的地址) + ".mp3";
                     process.StartInfo.UseShellExecute = false;
                     process.StartInfo.CreateNoWindow = true;
                     process.StartInfo.RedirectStandardOutput = true;
                     process.StartInfo.RedirectStandardInput = true;
                     process.StartInfo.RedirectStandardError = true;
                     process.Start();
                     process.BeginErrorReadLine(); // 开始异步读取
                     process.WaitForExit(); // 等待转码完成
                     if (process.ExitCode == 0)
                     {
                         outStr = process.StandardOutput.ReadToEnd();
                     }
                     else
                     {
                         outStr = "";
                     }
                 }
                 catch (Exception ex)
                 {
                                  outStr = "";
    
                 }
                 finally
                 {
                     process.Close();
                     process.Dispose();
                 }
       return outStr;
    

    (2) 利用ffmpeg.exe 获取音频文件的时长

              string duration = "";//时长
              try
                 {
            	  using (Process pro = new Process())
                   	  {
                         pro.StartInfo.UseShellExecute = false;
                         pro.StartInfo.ErrorDialog = false;
                         pro.StartInfo.RedirectStandardError = true;
                         pro.StartInfo.FileName = AppDomain.CurrentDomain.BaseDirectory + "ffmpeg" + "\\"                       + "ffmpeg.exe";
                         pro.StartInfo.Arguments = " -i " + ”要获取时长的音频文件的绝对地址“;
                         pro.Start();
                         System.IO.StreamReader errorreader = pro.StandardError;
                         pro.WaitForExit(1000);
                          string result = errorreader.ReadToEnd();
                         if (!string.IsNullOrEmpty(result))
                        	 {
                             //获取到的时长
                             result = result.Substring(result.IndexOf("Duration: ") + ("Duration: ").Length,                                    ("00:00:00").Length);
                             duration = result;
                         }
                     }
                 }
           catch (Exception ex)
                 {
                 }
    
      return duration;
    

    写在最后:本文只是列举了一下大概的实现,没有把全部代码放出来,大家可以根据核心代码自行封装即可

    展开全文
  • 接上篇文章vue录音功能网址      ...因为我使用上篇说的依赖1来做录音功能,所以录音十秒左右的时候,...虽然是bit但是还是很大的,所以需要将wav的数据转化为mp3的数据。 在这里呐,...

    接上篇文章vue录音功能网址       https://blog.csdn.net/like_you_aha/article/details/103165271
    因为我使用上篇说的依赖1来做录音功能,所以录音十秒左右的时候,数据size已经是90W左右了。虽然是bit但是还是很大的,所以需要将wav的数据转化为mp3的数据。

    在这里呐,又是我要介绍一个依赖了。谁让这些东西我没有做过,且我的后端是一个很优秀的男生,依赖都是他来的。
    wav转mp3 网址      https://github.com/zhuker/lamejs

    来,说一下我踩的坑。我直接拿Quick Start 的例子试的。事实证明,是可以的。因为我就是实验,直接录了不到一秒的语音,然后我觉得417bit是正常的,所以我就觉得自己转换成功了。然后就开始实验项目,结果发现不对。然后发现Quick Start 的例子只能转换一部分,也就是417bit,至于为什么是这个数字,我也不知道。
    于是乎,用Quick Start 的例子下面的Real Example的例子。
    然后

    samples = new Int16Array(44100); //one second of silence (get your data from the source you have)
    

    这一行代码是获取数据的,我是直接把数据放进去的,直接data,然后一直有错,于是改成

    var samples = new Int16Array(data.buffer);
    

    这样就可以直接转换了。

    小提示:上篇文章提到的依赖1里面有一个方法叫

    // 获取 WAV 数据(Blob)
    recorder.getWAVBlob();
    

    来看一下依赖的封装吧

    getWAVBlob() {
     // 先停止
     	this.stop();
     	return new Blob([ this.getWAV() ], { type: 'audio/wav' });
    }
    

    这里面引用了一个函数叫getWAV,获取到数据,然后新建一个blob对象,从而得到数据,而这个需要转换的依赖接收的是buffer,所以看到recorder.getWAVBlob();函数是不可以直接用的,这样拿到的是blob的话就没有办法转格式了。所以使用方法getWAV.
    看一下我封装的函数

    changeFormat(recorderData){
          // new Int16Array(this.recorder.getWAV().buffer) 取录音的buffer
          const channels = 1; //1 单声道 2 立体声
          const sampleRate = 44100; //44.1khz
          const kbps = 128; //128kbps mp3
          var mp3encoder = new lamejs.Mp3Encoder(channels, sampleRate, kbps);
          var mp3Data = [];
          var samples = new Int16Array(recorderData.buffer); // 从源中获取数据
          const sampleBlockSize = 1152; //576的倍数
          for (var i = 0; i < samples.length; i += sampleBlockSize) {
            var sampleChunk = samples.subarray(i, i + sampleBlockSize);
            var mp3buf = mp3encoder.encodeBuffer(sampleChunk);
            if (mp3buf.length > 0) {
                mp3Data.push(mp3buf);
            }
          }
          var mp3buf = mp3encoder.flush();   //finish writing mp3
          if (mp3buf.length > 0) {
              mp3Data.push(new Int8Array(mp3buf));
          }
          var blob = new Blob(mp3Data, {type: 'audio/mp3'});
          return blob;
        },
    

    调用changeFormat的时候,changeFormat(recorder.getWAV())就可以了,这样getWAV()的话拿到的是字符串就可以正常转化了。

    另:blob是可以用来存储文件的,不常用,链接附上blob说明

    展开全文
  • 将WAV格式的声音文件转换成MP3格式,没有必要找专门的格式转换工具,没那么复杂,你的电脑上就有一个现成的工具,这就是Windows附件中所带的“录音机”,使用这个工具能最简便实现WAV格式转为MP3格式,具体操作方法...
    将WAV格式的声音文件转换成MP3格式,没有必要找专门的格式转换工具,没那么复杂,你的电脑上就有一个现成的工具,这就是Windows附件中所带的“录音机”,使用这个工具能最简便实现WAV格式转为MP3格式,具体操作方法如下:
    
    依次单击“开始→程序→附件→娱乐→录音机”,打开录音机程序,然后单击文件菜单,从文件中打开需要转换的WAV文件,再单击“文件→另存为”,在“另存为”对话框中单击“更改”按钮,在“格式”框中选择“MPEG Layer-3”格式,一切就OK了。
    在转换时,如果想控制文件大小,如想让声音更好听或要使文件更加小巧,可以在窗口的“声音选定”中选择其中的“属性”,在该栏中选择更低或更高的采样频率就可以了,怎么样,是不是很方便,快试一试吧
    展开全文
  • 1.实现了长按录音及播放音频功能,并把录音资源格式转化为mp3(基于开源库LAME) 2.实现了把mp3文件上传到服务器(基于Retrofit2),支持再次下载播放,缓存功能 3.对于权限的管理,本Demo用的第三方库:...

    主要实现功能点:

    1.实现了长按录音及播放音频功能,并把录音资源格式转化为mp3(基于开源库LAME)
    2.实现了把mp3文件上传到服务器(基于Retrofit2),支持再次下载播放,缓存功能
    3.对于权限的管理,本Demo用的第三方库:https://github.com/yanzhenjie/AndPermission
    4.本Demo内部封装了长按录音控件,解决了与列表滚动View的冲突
    用法参考MainActivity。
    核心类
        录音类:RecorderManager
        播音类:MediaManager
        录音控件:LongPressRecordView

    前期调研:

    android原生SDK里提供了两种语音方案:

    1、AudioRecord

    主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的实时处理(如会说话的汤姆猫、语音)

    优点:语音的实时处理,可以用代码实现各种音频的封装

    缺点:输出是PCM语音数据,如果保存成音频文件,是不能够被播放器播放的,所以必须先写代码实现数据编码以及压缩

    示例:

    使用AudioRecord类录音,并实现WAV格式封装。录音20s,输出的音频文件大概为3.5M左右(已写测试代码)

    2、MediaRecorder

    已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有.aac(API = 16) .amr .3gp

    优点:大部分以及集成,直接调用相关接口即可,代码量小

    缺点:无法实时处理音频;输出的音频格式不是很多,例如没有输出mp3格式文件

    示例:

    使用MediaRecorder类录音,输出amr格式文件。录音20s,输出的音频文件大概为33K(已写测试代码)

    以上引用自:https://www.cnblogs.com/MMLoveMeMM/articles/3444718.html传送门里有具体的代码实现。

    考虑到跨平台的需求,aac,amr做的并不好,我们可以将录音文件转为为mp3格式的文件(基于LAME开源库),文件体积小,而且还可以平台。

    技术实现:

    大体思路是:利用AudioRecord类录制音频文件-录制的同时借助LAME库进行转化-存储mp3音频文件

    之所以用AudioRecord是为了获取录制的实时资源;

    之所以边录边转是为了如果录制时间太久,录完再转,用户等待的时间太过于长久。

    对于将android原生的音频格式转为mp3,可以参考GavinCT提供的开源库:https://github.com/GavinCT/AndroidMP3Recorder

    我是在此库的基础上添加了播放,上传,下载等功能,同时由于此库作者已不再维护,期间也踩过坑,后面会细说。

    项目结构及核心代码分析:

    1.录音手势长按实现类LongPressRecordView,通过重写setOnTouchListener将控件的手势操作传递给GestureDetector对应的对象来处理。

    注意有个小坑:如果此控件的父控件是列表类的,比如Scrollview,如果Scrollview足够长(超过全屏)可以滚动的话,会产生bug,长按录音控件,滚动到此控件外,会发现父控件也跟着滚动了。解决方案也很简单,我们需要处理下事件分发机制,重写dispatchTouchEvent方法(因为此方法发生在onTouchEvent之前),调用下

    getParent().requestDisallowInterceptTouchEvent(true);

    方法,告诉父控件我的事件自己处理,不要给我拦截了。

    LongPressRecordView具体代码:
    /**
     * Created by wangmaobo on 2018/9/14.
     */
    public class LongPressRecordView extends android.support.v7.widget.AppCompatTextView {
        private OnGestureListener mListener;
    
        public LongPressRecordView(Context context) {
            this(context, null, -1);
        }
    
        public LongPressRecordView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, -1);
        }
    
        public LongPressRecordView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        public void setListener(OnGestureListener listener) {
            mListener = listener;
        }
    
        private void init() {
            GestureDetector gesture = new GestureDetector(new RecordLongClickGesture().setLongPressListener(new OnLongPressListener() {
                @Override
                public void startRecord() {
                    if (null != mListener) {
                        mListener.longClick();
                    }
                }
            }));
            setOnTouchListener((view, motionEvent) -> {
                if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                    if (null != mListener) {
                        mListener.actionUp();
                    }
                }
                return gesture.onTouchEvent(motionEvent);
            });
            setFocusable(true);
            setClickable(true);
            setLongClickable(true);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            getParent().requestDisallowInterceptTouchEvent(true);
            return super.dispatchTouchEvent(event);
        }
    
        public interface OnGestureListener {
            void longClick();
    
            void actionUp();
        }
    }

    2.mp3转化类MP3Recorder,这个类有个bug,就是对于低版本即android6.0(api23)以下的手机由于无法真正判断录音权限是否开启,导致权限被禁止后无法正常录音,app崩溃。

    我们可以重写一下此类的开始录音,即start方法,通过

    mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING

    间接获取录音权限(关于权限的坑,可以在传送门里细看)。

    initAudioRecorder();
            mAudioRecord.startRecording();
            if (!checkPermission()) {
                mListener.noRecordRight();
                return;
            } else {
                mListener.hasRecordRight();
            }
    public boolean checkPermission() {
            if (null != mAudioRecord) {
                return mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING;
            }
            return false;
        }

    3.播音控制器MediaManager

    如果播放时需要倒计时,可以在构造函数里传入handler,提供了两种播放方式,播放本地资源和播放网络url资源:

     /**
         * 加载本地资源
         *
         * @param path
         * @param onCompletionListener
         */
        public void playSound(String path, OnCompletionListener onCompletionListener) {
            if (mPlayer == null) {
                mPlayer = new MediaPlayer();
                mPlayer.setOnErrorListener((mp, what, extra) -> {
                    mPlayer.reset();
                    return false;
                });
            } else {
                mPlayer.reset();
            }
    
            try {
                mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mPlayer.setOnCompletionListener(onCompletionListener);
                mPlayer.setDataSource(path);
                mPlayer.prepare();
                mPlayer.start();
                if (null != mHandler) {
                    mHandler.sendMessage(Message.obtain(mHandler, 2, 1));
                }
                Log.e("tag", "time=" + mPlayer.getDuration());
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 加载网络资源
         */
        public void play(String url) {
            try {
                this.uri = url;
                mPlayer = new MediaPlayer();
                mPlayer.setLooping(false);
                mPlayer.setOnCompletionListener(this);
                mPlayer.setOnErrorListener(this);
                mPlayer.setOnPreparedListener(this);
                mPlayer.setDataSource(mContext, Uri.parse(url));
                mHud.show();
                mPlayer.prepareAsync();
    
            } catch (IOException e) {
                Log.v("AudioHttpPlayer", e.getMessage());
            }
        }

    最后附上Demo地址:https://github.com/MarsToken/RecordDemo传送门

    参考资料:

    https://www.cnblogs.com/MMLoveMeMM/articles/3444718.html

    https://github.com/GavinCT/AndroidMP3Recorder

    https://github.com/yanzhenjie/AndPermission

    展开全文
  • 2.个人花时间相对多的部分:将MP3格式录音文件转化为WAV格式,参数是:16000(8000可能会出现3301错误:语音模糊)因为百度云语音识别只接受WAV、Pcm格式的录音文件。 3.调用百度云语音识别:获取密钥,转化为文本...
  • 1、可将录音文件保存WAV、MP3、WMA格式。对于MP3及WMA格式文件均直接生成,无需生成中间文件及极为耗费时间的转化过程。 2、可对录音文件进行编辑。软件提供常用的音频编辑功能,如截取、复制、合并多个文件等。 ...
  • 1、可将录音文件保存WAV、MP3、WMA格式。对于MP3及WMA格式文件均直接生成,无需生成中间文件及极为耗费时间的转化过程。 2、可播放WAV、MP3、MP2、OGG、WMA、VOX等多种音频格式。 3、任务计划功能。您可使用本软件...
  • 我在网上找到的关于lame将android的arm格式音频转化为通用的mp3, 目前在5.0以下的版本运行没有问题,在5.0的android手机运行出现 java.lang.UnsatisfiedLinkError: No implementation found for void ...
  • 1、可将录音文件保存WAV、MP3格式MP3格式文件直接生成,无需生成中间文件及极为耗费时间的转化过程。 2、可对录音文件进行编辑。软件提供常用的音频编辑功能,如截取、复制、合并多个文件等。 3、任务计划功能...
  • 1、可将录音文件保存WAV、MP3格式MP3格式文件直接生成,无需生成中间文件及极为耗费时间的转化过程。 2、可对录音文件进行编辑。软件提供常用的音频编辑功能,如截取、复制、合并多个文件等。 3、任务计划功能...
  •   我遇到的需求是,将微信小程序录音得来的.mp3转换科大讯飞语音识别的.wav/.pcm。   我在解决问题时,尝试过很多方法,比如构造.wav文件头,使用NAudio.dll等,这两种方式我先贴出来,当时并没有...
  • iOS Native录音+web交互

    千次阅读 2018-05-17 16:53:42
    主要流程: 1.创建音频路径,位置放在document文件夹下; 2.通过AVAudioseesion来设置录音时的背景声音,需要...5.将录音生成的文件转化Mp3的文件,Mp3转化相当于将之前的音频再次做一次采样,生成的Mp3文件会更...
  • 在使用百度人工智能语音输入的时候,因为一般录音格式为MP3,而百度只支持PCM之类格式的音频文件,所以需要音频格式之间的转换,此下载为音频转化所需要的jar包,如果需要代码,可以私聊我呦!
  • 怎么把语音转化成文字?生活中有时候需要把语音转化成文字,这样能...对,你看的没错,这里也包含视频文件,不管你是会议录音还是教程视频,都能转化为文字,适用范围非常广,音视频转文字,只需要一个网站就能搞定了。
  • C#winform窗口基于百度云的语音识别,亲测可用,保存MP3格式录音文件在本地,在转化为WAV格式,基于百度云进行识别。
  • 1、SoundTouch库,可以实现对原有声音处理后达到变声的效果。该库的官网地址:... 2、LAME MP3 Encoder库,可以实现把录音的格式转化为mp3格式。该库的官网地址:http://lame.sourceforge.net/

空空如也

空空如也

1 2 3
收藏数 46
精华内容 18
关键字:

录音转化为mp3格式