精华内容
下载资源
问答
  • Android录音机

    2015-12-14 10:25:07
    完整的Android录音机 包括 暂停录音播放 服务 通知 重命名,很好的开源项目 仅供参考
  • android录音机

    2014-07-16 18:01:28
    Android录音机源码,看一下吧
  • android 录音机

    2012-07-17 22:44:24
    android 录音机,挺方便的一个学习例子
  • Android录音机-源码

    2021-02-07 22:24:49
    Android录音机
  • android录音机demo

    2016-09-20 16:59:08
    android录音机
  • android 录音机 源码

    2014-05-07 07:47:34
    android 录音机源码,实现了简单的录音机功能,适合初学者学习参考
  • Android录音机,带录音效果,可以学习安卓录音,动画
  • Android 录音机Demo

    热门讨论 2011-12-06 13:19:22
    Android 录音机Demo,迅速让你理解Android录音功能的使用
  • android 录音机demo,可录音,播放,文件保存到sd卡根目录,测试可用
  • android录音机例子

    2012-04-15 23:18:45
    一个简单的android录音机例子, 有不明白的地方可以邮件
  • android录音机带录音波形及播放波形,安卓4.1.3以上版本可以使用,同时使用了H5的引导界面,用了各种多线程,初学者可以参考。其实也是我在网上整合的几个内容。
  • android 录音机源码(带波形显示)

    热门讨论 2015-08-12 23:11:50
    android 录音机源码, 可以显示波形
  • 高仿三星android录音机界面, 除了有1,2个设置菜单外,其他都已经OK. 不知道有没有人需要的, 特此共享一下, 望各位捧场,谢谢!
  • 超酷android录音机

    2014-05-27 15:09:28
    小米 录音机 代码 ,超经典,尤其是初学android人员的好例子。
  • Android 录音机功能之读取音档绘制波形,可自订裁剪区间,录音暂停继续 一.效果图: 二.实现: 1.简介: 使用多个github项目,实现了以下: *读取手机上有的音档 *滑动自订 裁剪音档区间 *可录音暂停后...

    Android 录音机功能之读取音档绘制波形,可自订裁剪区间,录音暂停继续    下载地址:源码

    一.效果图:

     

    二.实现:

    1.简介:

    使用多个github项目,实现了以下:

    *读取手机上有的音档
    *滑动自订 裁剪音档区间
    *可录音暂停后继续录音
    *录音同时绘制波型图

    2.添加依赖:

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 25
        buildToolsVersion '27.0.3'
        defaultConfig {
            applicationId "com.mkjihu.audioedit"
            minSdkVersion 19
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
    
    
        compile 'com.android.support:appcompat-v7:25.+'
        compile 'com.android.support:support-v4:25.+'
    
        compile 'com.android.support:recyclerview-v7:25.+'
        //--RxJava2
        compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
        compile 'io.reactivex.rxjava2:rxjava:2.1.0'
        compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
    
        //--MP3轉檔
        compile 'com.github.adrielcafe:AndroidAudioConverter:0.0.8'
    
        compile 'org.florescu.android.rangeseekbar:rangeseekbar-library:0.3.0'
        compile 'com.github.Jay-Goo:RangeSeekBar:v1.1.0'
    
    
    }
    

    设置权限:

    
        <uses-sdk
            android:minSdkVersion="19"
            android:targetSdkVersion="25" />
    
        <!-- 使用音场效果必要的权限 -->
        <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
        <uses-permission android:name="android.permission.READ_CONTACTS" />
        <uses-permission android:name="android.permission.WRITE_CONTACTS" />
        <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    
        <!-- 使用SD卡 -->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
        <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
        <uses-permission android:name="android.permission.INTERNET" />
        <!-- 允许程序重新启动其他程序
    	<uses-permission android:name="android.permission.RESTART_PACKAGES" />

    3.主函数:

    
    import java.io.File;
    import java.io.FilenameFilter;
    import java.io.UnsupportedEncodingException;
    
    import com.mkjihu.audioedit.Presenter.MainPresenter;
    import com.mkjihu.audioedit.obj.DialogBox;
    
    import android.Manifest;
    import android.app.ProgressDialog;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.database.Cursor;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.provider.MediaStore;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.DividerItemDecoration;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.RecyclerView.ViewHolder;
    import android.util.Base64;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.ImageView;
    
    public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
    
    	public SwipeRefreshLayout mSwipeRefreshLayout;
    	public RecyclerView recycler_view;
    	public ProgressDialog progressDialog;
    	public MainPresenter presenter;
    	
    	final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
    	
    	public ImageView imageView1;
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		fid();
    		presenter = new MainPresenter(this);
    		progressDialog = new ProgressDialog(this);
    		progressDialog.setCancelable(true);
    		progressDialog.setInverseBackgroundForced(false);
    		progressDialog.setCanceledOnTouchOutside(false);
    		progressDialog.setMessage("掃描中...");
    		presenter.GetAudio();
    		
    
    		
    		try {
    	        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    				//申请WRITE_EXTERNAL_STORAGE权限
    				ActivityCompat.requestPermissions(this, new String[]{
    						Manifest.permission.READ_EXTERNAL_STORAGE
    						,Manifest.permission.WRITE_EXTERNAL_STORAGE
    						, Manifest.permission.READ_PHONE_STATE
    						, Manifest.permission.RECORD_AUDIO
    						, Manifest.permission.MODIFY_AUDIO_SETTINGS
    						, Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_ASK_PERMISSIONS);
    	        }
    		} catch (Exception e) {
    			// TODO: handle exception
    		}
    		
    		
    	}
    	
    	
    	
      	
    	//權限同意返回
    	@Override
    	public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    		switch (requestCode) {
    			case REQUEST_CODE_ASK_PERMISSIONS:
    				if(verifyPermissions(grantResults)){
    					Log.i("!!!", "同意");
    				}
    				else {
    					Log.i("!!!", "不同意");
    					DialogBox.getAlertDialog2(this, "提示", "請至設定之應用程式,開啟權限!");
    				}
    				super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    				break;
    			default:
    				super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    		}
    	}
    	
    	
    	public boolean verifyPermissions(int[] grantResults) {
            // At least one result must be checked.
            if(grantResults.length < 1){
                return false;
            }
    
            // Verify that each required permission has been granted, otherwise return false.
            for (int result : grantResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
            return true;
        }
    
    	public void tosdg(String aas) {
    		Intent intent = new Intent(this, SoundFilePage.class);
    		intent.putExtra("va", aas);
    		startActivity(intent);
    	}
    
    	
    	private void fid() 
    	{
    		imageView1 = (ImageView)findViewById(R.id.imageView1);
    		recycler_view = (RecyclerView)findViewById(R.id.recycler_view);
    		recycler_view.setLayoutManager(new LinearLayoutManager(this));
    		//下面这行代码就是添加分隔线的方法
    		recycler_view.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
    		mSwipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.mSwipeRefreshLayout);
    		//设置下拉出现小圆圈是否是缩放出现,出现的位置,最大的下拉位置
    		mSwipeRefreshLayout.setProgressViewOffset(true, 0, 200);
    		//设置下拉圆圈的大小,两个值 LARGE, DEFAULT
    		//mSwipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE);
    		// 设置下拉圆圈上的颜色,蓝色、绿色、橙色、红色
    		mSwipeRefreshLayout.setColorSchemeResources(
    		    android.R.color.holo_blue_bright,
    		    android.R.color.holo_green_light,
    		    android.R.color.holo_orange_light,
    		    android.R.color.holo_red_light);
    		mSwipeRefreshLayout.setOnRefreshListener(this);	
    		
    		imageView1.setOnClickListener(new OnClickListener() {
    			@Override
    			public void onClick(View v) {
    				Intent intent = new Intent(MainActivity.this, RecordingPage.class);
    				startActivity(intent);
    			}
    		});
    		
    	}
    	public void adasp(RecyclerView.Adapter<ViewHolder> adapter) {
    		recycler_view.setAdapter(adapter);
    	}
    	
    	public void offRefresh() {
    		mSwipeRefreshLayout.setRefreshing(false);
    		//iwillPaint.dissdig();
    	}
    	public void openRefresh() {
    		mSwipeRefreshLayout.setRefreshing(true);
    	}
    	@Override
    	public void onRefresh() {
    		//下拉更新執行
    		
    		new Handler().postDelayed(new Runnable()  {
    			@Override
    			public void run()  {
    				presenter.GetAudio();
    			}
    		}, 1000);
    	
    	}
    	@Override
    	protected void onDestroy() {
    		presenter.disposable.dispose();
    		super.onDestroy();
    	}
    	
    	
    }
    

    4.布局:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.mkjihu.audioedit.MainActivity" >
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:background="@color/colorPrimary" >
    
            <ImageView
                android:id="@+id/imageView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right|center"
                android:layout_marginRight="8dp"
                android:src="@drawable/uin" />
    
            <TextView
                android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="可讀音頻列表"
                android:textColor="#FFFFFF"
                android:textSize="16sp" />
    
        </FrameLayout>
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/mSwipeRefreshLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
    
    		<android.support.v7.widget.RecyclerView
    		    
        		android:id="@+id/recycler_view"
        		android:layout_width="match_parent"
        		android:layout_height="match_parent"
        		android:clipToPadding="false"
        		android:scrollbarStyle="outsideOverlay"
        		android:scrollbars="vertical" >
    
    		</android.support.v7.widget.RecyclerView>
    	</android.support.v4.widget.SwipeRefreshLayout>
        
    </LinearLayout>
    

    5.录音相关功能RecordingPage.java:

     

    import java.nio.ByteBuffer;
    
    import com.androidquery.AQuery;
    import com.cokus.wavelibrary.draw.WaveCanvas;
    import com.cokus.wavelibrary.view.WaveSurfaceView;
    import com.mkjihu.audioedit.Presenter.RecordingPresenter;
    import com.mkjihu.audioedit.view.Chronometer;
    
    import android.app.ProgressDialog;
    import android.app.AlertDialog.Builder;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.graphics.PixelFormat;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.SystemClock;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.EditText;
    import android.widget.Toast;
    
    public class RecordingPage extends AppCompatActivity {
    
    	public AQuery aq;
    	
    	public WaveSurfaceView waveSfv;
    	public WaveCanvas waveCanvas;
    	private ProgressDialog logdialogs;
    	public RecordingPresenter presenter;
    	public int RegType = 0;
    	private String fileName = "MergePCM";//完成合併後的文件名;
    	
    	private EditText editText;
    	public Chronometer chronometer1;
    	public long escapeTime = 0;
    	
    	//https://github.com/adrielcafe/AndroidAudioConverter
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_recording_page);
    		aq = new AQuery(this);
    		fid();
    		presenter = new RecordingPresenter(this,waveSfv);
    		aq.id(R.id.bt1).clicked(ls);
    		aq.id(R.id.bt2).clicked(ls);
    		aq.id(R.id.bt3).clicked(ls);
    		aq.id(R.id.bt1).text("開始");
    		RegType = 0;
    	}
    	
    	public OnClickListener ls = new OnClickListener() {
    		
    		@Override
    		public void onClick(View v) {
    			switch (v.getId()) {
    			case R.id.bt1:
    				waveSfv.setVisibility(View.VISIBLE);
    				if (RegType==0) {//按下开始
    					
    					aq.id(R.id.bt1).text("暫停");
    					presenter.StartReg();
    					RegType = 1;
    					chronometer1.setBase(SystemClock.elapsedRealtime() + escapeTime);
    					chronometer1.start();
    				}
    				else {//按下暫停
    					
    					aq.id(R.id.bt1).text("開始");//-呼叫暫停
    					presenter.Stop(0);//暫停
    					RegType = 0;
    					
    					escapeTime = chronometer1.getBase() - SystemClock.elapsedRealtime();
    					chronometer1.stop();
    				}
    				
    				break;
    			case R.id.bt2:
    				aq.id(R.id.bt1).text("開始");
    				presenter.Stop(0);//暫停
    				presenter.clearFiles();
    				presenter.clidview();
    				RegType = 0;
    				escapeTime = 0;
    				
    				
    				chronometer1.stop();
    				chronometer1.setBase(SystemClock.elapsedRealtime());
    				
    				
    				break;
    			case R.id.bt3:
    				
    				if (!editText.getText().toString().equals("")) {
    					fileName = editText.getText().toString();
    				}
    				presenter.steTitle(fileName);
    				if (RegType==1) {//播放狀態
    					//presenter.Stop(1);
    					Toast.makeText(RecordingPage.this, "請先暫停錄音", Toast.LENGTH_SHORT).show();
    				}
    				else{//已暫停狀態
    					presenter.Stop(2);
    				}
    				
    				break;
    			}
    		}
    	};
    	
    	public void toSidie(final String Path)
    	{
    		
    		Builder builder = new Builder(this);
            builder.setTitle("訊號轉換完成");
            builder.setCancelable(false);
            builder.setMessage("檔案路徑:"+Path);
            //設定Negative按鈕資料
            builder.setNegativeButton("確定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) 
                { 
                	Intent intent = new Intent(RecordingPage.this, SoundFilePage.class);
            		intent.putExtra("va", Path);
            		startActivity(intent);
            		finish();
                }
            });
            builder.create().show();
    	}
    	
    
    	@Override
    	protected void onDestroy() {
    		try {
    			presenter.destroy();
    		} catch (Exception e) { }
    		super.onDestroy();
    	}
    
    
    
    	private void fid() {
    		waveSfv = (WaveSurfaceView)findViewById(R.id.wavesfv);
    		if(waveSfv != null) {
                waveSfv.setLine_off(42);
                //解决surfaceView黑色闪动效果
                waveSfv.setZOrderOnTop(true);
                waveSfv.getHolder().setFormat(PixelFormat.TRANSLUCENT);
            }
    		logdialogs = new ProgressDialog(this);
    		logdialogs.setCancelable(false);
    		logdialogs.setInverseBackgroundForced(false);
    		logdialogs.setCanceledOnTouchOutside(false);
    		logdialogs.setMessage("訊號轉換中...");
    		chronometer1 = (Chronometer)findViewById(R.id.chronometer1);
    		editText = (EditText)findViewById(R.id.editText1);
    	}
    	   
    	//--顯示加載
    	public void showdia() {
    		if(logdialogs!=null && !logdialogs.isShowing()) {
    			logdialogs.show();
    		}
    	}			
    	//此处关闭加載
    	public void disdia() {
    		if(logdialogs!=null && logdialogs.isShowing()) {
    			logdialogs.dismiss();
    		}
    	}	
    
    	//==================================================================================================================================================
    	
    	
    	
        //轉換為短字節
        private byte[] short2byte(short[] sData) {
            int shortArrsize = sData.length;
            byte[] bytes = new byte[shortArrsize * 2];
            for (int i = 0; i < shortArrsize; i++) {
                bytes[i * 2] = (byte) (sData[i] & 0x00FF);
                bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
                sData[i] = 0;
            }
            return bytes;
    
        }
        
        //byte[] 轉換 short[]
        public static short[] shortMe(byte[] bytes) {
            short[] out = new short[bytes.length / 2]; // will drop last byte if odd number
            ByteBuffer bb = ByteBuffer.wrap(bytes);
            for (int i = 0; i < out.length; i++) {
                out[i] = bb.getShort();
            }
            return out;
        }
    }
    

     6.录音布局:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.mkjihu.audioedit.RecordingPage" >
        
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:background="@color/colorPrimary" >
    
            <TextView
                android:id="@+id/gwg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="錄音"
                android:textColor="#FFFFFF"
                android:textSize="16sp" />
    
        </FrameLayout>   
        
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="150dp" >
    
        	<com.cokus.wavelibrary.view.WaveSurfaceView
        	    android:id="@+id/wavesfv"
        	    android:layout_width="fill_parent"
        	    android:layout_height="150dp" />
        </FrameLayout>
    
        <com.mkjihu.audioedit.view.Chronometer
            android:id="@+id/chronometer1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="8dp"
            android:text="Chronometer"
            android:textSize="20sp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
    
            <Button
                android:id="@+id/bt1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="開始" />
    
            <Button
                android:id="@+id/bt2"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="重錄" />
    
            <Button
                android:id="@+id/bt3"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="完成" />
    
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp" >
    
            <TextView
                android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="檔名:"
                android:textSize="20sp" />
    
            <EditText
                android:id="@+id/editText1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10"
                android:hint="未输入将使用预设檔名" />
    
        </LinearLayout>
    
    </LinearLayout>
    

    7.自定义实现音波类:

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    
    /**
     * 该类只是一个初始化surfaceview的封装
     */
    public class WaveSurfaceView extends SurfaceView implements SurfaceHolder.Callback{
    	private SurfaceHolder holder;
    	private int line_off;//上下边距距离
    	
    
        public int getLine_off() {
    		return line_off;
    	}
    
    
    	public void setLine_off(int line_off) {
    		this.line_off = line_off;
    	}
    
    
    	public WaveSurfaceView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		// TODO Auto-generated constructor stub
    		this.holder = getHolder();
    		holder.addCallback(this);
    		
    	}
    
    
        /**
         * @author cokus
         * init surfaceview
         */
        public  void initSurfaceView( final SurfaceView sfv){
        	new Thread(){
        		public void run() {
        			 Canvas canvas = sfv.getHolder().lockCanvas(  
        	                 new Rect(0, 0, sfv.getWidth(), sfv.getHeight()));// 关键:获取画布  
        	         if(canvas==null){
        	        	 return;
        	         }
        	         //canvas.drawColor(Color.rgb(241, 241, 241));// 清除背景  
        	         canvas.drawARGB(255, 239, 239, 239);
        	        
    				int height = sfv.getHeight()-line_off;
        	         Paint paintLine =new Paint();
        	         Paint centerLine =new Paint();
        	         Paint circlePaint = new Paint();
        	         circlePaint.setColor(Color.rgb(246, 131, 126));
        	         circlePaint.setAntiAlias(true);
        	         
        	         canvas.drawCircle(0, line_off/4, line_off/4, circlePaint);// 上面小圆
        	         canvas.drawCircle(0, sfv.getHeight()-line_off/4, line_off/4, circlePaint);// 下面小圆
        	         canvas.drawLine(0, 0, 0, sfv.getHeight(), circlePaint);//垂直的线
        	         paintLine.setColor(Color.rgb(169, 169, 169));
        	         centerLine.setColor(Color.rgb(85, 140, 208));//-音波繪製顏色39, 199, 175
        	         canvas.drawLine(0, line_off/2, sfv.getWidth(), line_off/2, paintLine);//最上面的那根线
        	         canvas.drawLine(0, sfv.getHeight()-line_off/2-1, sfv.getWidth(), sfv.getHeight()-line_off/2-1, paintLine);//最下面的那根线  
    //    	         canvas.drawLine(0, height*0.25f+20, sfv.getWidth(),height*0.25f+20, paintLine);//第二根线
    //    	         canvas.drawLine(0, height*0.75f+20, sfv.getWidth(),height*0.75f+20, paintLine);//第3根线
        	         canvas.drawLine(0, height*0.5f+line_off/2, sfv.getWidth() ,height*0.5f+line_off/2, centerLine);//中心线
        	         sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
        		};
        	}.start();
        	
        }
    
    
    	@Override
    	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void surfaceCreated(SurfaceHolder holder) {
    		// TODO Auto-generated method stub
    		initSurfaceView(this);
    		
    	}
    
    	@Override
    	public void surfaceDestroyed(SurfaceHolder holder) {
    		
    	}
    
    	
    
    }
    

     8.自定义时间显示控件:

    
    import android.content.Context;
    import android.os.Handler;
    import android.os.Message;
    import android.os.SystemClock;
    import android.util.AttributeSet;
    import android.widget.TextView;
    import java.text.DecimalFormat;
    
    public class Chronometer extends TextView {
        @SuppressWarnings("unused")
    	private static final String TAG = "Chronometer";
    
        public interface OnChronometerTickListener {
    
            void onChronometerTick(Chronometer chronometer);
        }
    
        private long mBase;
        private boolean mVisible;
        private boolean mStarted;
        private boolean mRunning;
        private OnChronometerTickListener mOnChronometerTickListener;
    
        private static final int TICK_WHAT = 2;
    
        private long timeElapsed;
        
        public Chronometer(Context context) {
            this (context, null, 0);
        }
    
        public Chronometer(Context context, AttributeSet attrs) {
            this (context, attrs, 0);
        }
    
        public Chronometer(Context context, AttributeSet attrs, int defStyle) {
            super (context, attrs, defStyle);
    
            init();
        }
    
        private void init() {
            mBase = SystemClock.elapsedRealtime();
            updateText(mBase);
        }
    
        public void setBase(long base) {
            mBase = base;
            dispatchChronometerTick();
            updateText(SystemClock.elapsedRealtime());
        }
    
        public long getBase() {
            return mBase;
        }
    
        public void setOnChronometerTickListener(
                OnChronometerTickListener listener) {
            mOnChronometerTickListener = listener;
        }
    
        public OnChronometerTickListener getOnChronometerTickListener() {
            return mOnChronometerTickListener;
        }
    
        public void start() {
            mStarted = true;
            updateRunning();
        }
    
        public void stop() {
            mStarted = false;
            updateRunning();
        }
    
    
        public void setStarted(boolean started) {
            mStarted = started;
            updateRunning();
        }
    
        @Override
        protected void onDetachedFromWindow() {
            super .onDetachedFromWindow();
            mVisible = false;
            updateRunning();
        }
    
        @Override
        protected void onWindowVisibilityChanged(int visibility) {
            super .onWindowVisibilityChanged(visibility);
            mVisible = visibility == VISIBLE;
            updateRunning();
        }
    
        private synchronized void updateText(long now) {
            timeElapsed = now - mBase;
            
            DecimalFormat df = new DecimalFormat("00");
            
            int hours = (int)(timeElapsed / (3600 * 1000));
            int remaining = (int)(timeElapsed % (3600 * 1000));
            
            int minutes = (int)(remaining / (60 * 1000));
            remaining = (int)(remaining % (60 * 1000));
            
            int seconds = (int)(remaining / 1000);
            remaining = (int)(remaining % (1000));
            
            int milliseconds = (int)(((int)timeElapsed % 1000) / 10);
            
            String text = "";
            
            if (hours > 0) {
            	text += df.format(hours) + ":";
            }
            
           	text += df.format(minutes) + ":";
           	text += df.format(seconds) + ":";
           	//text += Integer.toString(milliseconds);
           	text +=	String.format("%02d", milliseconds);
           	
            setText(text);
        }
    
        private void updateRunning() {
            boolean running = mVisible && mStarted;
            if (running != mRunning) {
                if (running) {
                    updateText(SystemClock.elapsedRealtime());
                    dispatchChronometerTick();
                    mHandler.sendMessageDelayed(Message.obtain(mHandler,TICK_WHAT), 10);
                } else {
                    mHandler.removeMessages(TICK_WHAT);
                }
                mRunning = running;
            }
        }
    
        private Handler mHandler = new Handler() {
            public void handleMessage(Message m) {
                if (mRunning) {
                    updateText(SystemClock.elapsedRealtime());
                    dispatchChronometerTick();
                    sendMessageDelayed(Message.obtain(this , TICK_WHAT),10);
                }
            }
        };
    
        void dispatchChronometerTick() {
            if (mOnChronometerTickListener != null) {
                mOnChronometerTickListener.onChronometerTick(this);
            }
        }
    
    	public long getTimeElapsed() {
    		return timeElapsed;
    	}
        
    }

    具体代码请看:https://github.com/mkjihu/AudioWaveViewEdit

    参考:

    https://github.com/mkjihu/AudioWaveViewEdit

    https://github.com/cokuscz/audioWaveCanvas

    https://github.com/Jay-Goo/RangeSeekBar

    https://github.com/google/ringdroid

    展开全文
  • Android MediaRecorder录音录像 暂停 继续录音 播放 ARM格式(音频一) ...Android 录音机小米商业项目开源代码 AudioRecord录音暂停 播放 Wav格式(音频二) https://blog.csdn.net/WHB20081815/arti...

    Android MediaRecorder录音录像 暂停 继续录音 播放 ARM格式(音频一)

    https://blog.csdn.net/WHB20081815/article/details/88778605

    Android 录音机小米商业项目开源代码 AudioRecord录音暂停 播放 Wav格式(音频二)

    https://blog.csdn.net/WHB20081815/article/details/88778623

    Android 录音机小米商业项目开源代码 pcm转AAC硬编码 录音暂停 播放 (音频三)

    https://blog.csdn.net/WHB20081815/article/details/88778634

    Android 录音机商业项目开源代码 pcm转AAC软编码 录音暂停 播放 (音频四)

    https://blog.csdn.net/WHB20081815/article/details/88778641

    Android 高仿唱吧 咔拉ok 商业项目开源代码 K歌合成 伴奏录音合成MP3(音频五)

    https://blog.csdn.net/WHB20081815/article/details/88778652

    音频基础知识

     

    声道数(通道数)

     

    即声音的通道的数目。很好理解,有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的也处理成两个喇叭输出同一个声道的声音),立体声的PCM可以使两个喇叭都发声(一般左右声道有分工) ,更能感受到空间效果。

     

    采样位数

     

    即 采样值或取样值(就是将采样样本幅度量化)。它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。

     

    在计算机中采样位数一般有8位和16位之分,但有一点请大家注意,8位不是说把纵坐标分成8份,而是分成2的8次方即256份; 同理16位是把纵坐标分成2的16次方65536份。

     

    采样频率

     

    即取样频率,指 每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。由于人耳的分辨率很有限,太高的频率并不能分辨出来。在16位声卡中有22KHz、44KHz等几级,其中,22KHz相当于普通FM广播的音质,44KHz已相当于CD音质了,目前的常用采样频率都不超过48KHz。

     

    既然知道了以上三个概念,就可以由下边的公式得出PCM文件所占容量:

     

    存储量= (采样频率 * 采样位数 * 声道 * 时间)/8 (单位:字节数)。

     

    PCM 介绍

     

    目前我们在计算机上进行音频播放都需要依赖于音频文件,音频文件的生成过程是将声音信息采样、量化和编码产生的数字信号的过程,人耳所能听到的声音,最低的频率是从20Hz起一直到最高频率20KHZ,因此音频文件格式的最大带宽是20KHZ。根据奈奎斯特的理论,只有采样频率高于声音信号最高频率的两倍时,才能把数字信号表示的声音还原成为原来的声音,所以音频文件的采样率一般在40~50KHZ,比如最常见的CD音质采样率44.1KHZ。

     

    对声音进行采样、量化过程被称为脉冲编码调制(Pulse Code Modulation),简称PCM。PCM数据是最原始的音频数据完全无损,所以PCM数据虽然音质优秀但体积庞大,为了解决这个问题先后诞生了一系列的音频格式,这些音频格式运用不同的方法对音频数据进行压缩,其中有无损压缩(ALAC、APE、FLAC)和有损压缩(MP3、AAC、OGG、WMA)两种。

     

    WAV

     

    Waveform Audio File Format(WAVE,又或者是因为扩展名而被大众所知的WAV),是微软与IBM公司所开发在个人电脑存储音频流的编码格式,在Windows平台的应用软件受到广泛的支持,地位上类似于麦金塔电脑里的AIFF。 此格式属于资源交换档案格式(RIFF)的应用之一,通常会将采用脉冲编码调制的音频资存储在区块中。也是其音乐发烧友中常用的指定规格之一。由于此音频格式未经过压缩,所以在音质方面不会出现失真的情况,但档案的体积因而在众多音频格式中较为大。

     

    所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。WAV也可以使用多种音频编码来压缩其音频流,不过我们常见的都是音频流被PCM编码处理的WAV,但这不表示WAV只能使用PCM编码,MP3编码同样也可以运用在WAV中,和AVI一样,只要安装好了相应的Decode,就可以欣赏这些WAV了。

     

    在Windows平台下,基于PCM编码的WAV是被支持得最好的音频格式,所有音频软件都能完美支持,由于本身可以达到较高的音质的要求,因此,WAV也是音乐编辑创作的首选格式,适合保存音乐素材。因此,基于PCM编码的WAV被作为了一种中介的格式,常常使用在其他编码的相互转换之中,例如MP3转换成WMA。

     

    wav文件格式

    在文件的前44字节放置标头(header),使播放器或编辑器能够简单掌握文件的基本信息,其内容以区块(chunk)为最小单位,每一区块长度为4字节。

     

    录音的原理:

    开始录音的时候,一个 AudioRecord 需要初始化一个相关联的声音buffer,这个 buffer 主要是用来保存新的声音数据。这个 buffer 的大小,我们可以在对象构造期间去指定。它表明一个 AudioRecord 对象还没有被读取(同步)声音数据前能录多长的音(即一次可以录制的声音容量)。声音数据从音频硬件中被读出,数据大小不超过整个录音数据的大小(可以分多次读出),即每次读取初始化 buffer 容量的数据。

    采集工作很简单,我们只需要构造一个AudioRecord对象,然后传入各种不同配置的参数即可。一般情况下录音实现的简单流程如下:

    1. 音频源:我们可以使用麦克风作为采集音频的数据源。

    2. 采样率:一秒钟对声音数据的采样次数,采样率越高,音质越好。

    3. 音频通道:单声道,双声道等,

    4. 音频格式:一般选用PCM格式,即原始的音频样本。

    5. 缓冲区大小:音频数据写入缓冲区的总数,可以通过AudioRecord.getMinBufferSize获取最小的缓冲区。(将音频采集到缓冲区中然后再从缓冲区中读取)。

     

    image

    摘自维基百科

     

    起始位址 区块名称 区块大小 端序 区块内容 备注

    0 区块编号 4 大 “RIFF”

    4 总区块大小 4 小 = N+36 N:音频数据的总字节数;36:从下一个地址开始到头文件尾的总字节数

    8 档案格式 4 大 “WAVE”

    12 子区块1标识 4 大 “fmt ” (最后有一个空格)

    16 子区块1大小 4 小 16

    20 音频格式 2 小 1(PCM)

    22 声道数量 2 小 1(单声道)2(立体声)

    24 取样频率(采用频率) 4 小 取样点/秒(Hz)

    28 位元(组)率 4 小 = 取样频率 * 位元深度 / 8 Byte率 = 采样频率 *音频通道数 *每次采样得到的样本位数 / 8

    32 区块对齐 2 小 4

    36 子区块2标识 4 大 “data”

    40 子区块2大小 4 小 N(=位元(组)*秒数 *声道数量) 音频数据的大小

    44 音频数据 =N 小 <音频数据从此开始>

    端序,即字节顺序

    --------------------- 

    Android 中MediaRecorder和AudioRecord 与 MediaPlayer和 AudioTrack 的介绍

    官方提供两种API用于音频开发,分别为 MediaRecorder和 AudioRecord 用与音频的采集, MediaPlayer和 AudioTrack 用于音频 的播放

     

     

    image

    小知识点:

    1. 在用MediaRecorder进行录制音视频时,最终还是会创建AudioRecord用来与AudioFlinger进行交互。

    2. MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放。所以是MediaPlayer包含了AudioTRack。

    使用AudioRecord录制pcm音频

     

    PCM转WAV
    只要加上wav头文件即可。

    使用AudioTrack播放pcm音频
    AudioTrack 类为java程序实现了控制和播放简单的音频。它允许将 PCM音频流传输到音频接收器进行播放。这是通过将音频数据推给 AudioTrack对象实现的,可以使用 write(byte[], int, int) , write(short[], int, int) 或 write(float[], int, int, int) 方法。

    AudioTrack可以在两种模式下运行:static 或 streaming。
    在Streaming模式下,应用程序使用其中一种write()方法将连续的数据流写入AudioTrack 。当数据从Java层传输到native层并排队等待播放时,它们会阻塞并返回。在播放音频数据块时,流模式非常有用,例如:

        由于声音播放的持续时间太长而不能装入内存,
        由于音频数据的特性(高采样率,每个样本的位数……)而不能装入内存
        在先前排队的音频正在播放时接收或生成。
    

    在处理能够装入内存的短音时,应选择静态模式,并且需要尽可能以最小的延迟播放。因此,对于经常播放的UI和游戏声音而言,静态模式将是优选的,并且可能具有最小的开销。

    一旦创建,AudioTrack对象将初始化其关联的音频缓冲区。在构建过程中指定的这个缓冲区的大小决定了AudioTrack在耗尽数据之前可以播放多长时间。

    对于使用静态模式的AudioTrack,此大小是可以从中播放的最大声音大小。

    对于流模式,数据将以小于或等于总缓冲区大小的块形式写入音频接收器。AudioTrack不是 final,因此允许使用子类,但不建议使用这种类型的子类。

     

    录音代码的片段:

    audioRecord.startRecording();
    isRecording = true;
    
    new Thread(new Runnable() {
        @Override public void run() {
    
            FileOutputStream os = null;
            try {
                os = new FileOutputStream(file);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
    
            if (null != os) {
                while (isRecording) {
                    int read = audioRecord.read(data, 0, minBufferSize);
                    // 如果读取音频数据没有出现错误,就将数据写入到文件
                    if (AudioRecord.ERROR_INVALID_OPERATION != read) {
                        try {
                            os.write(data);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                try {
                    Log.i(TAG, "run: close file output stream !");
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();
    

    来源: https://www.jianshu.com/p/a14787fc4b6b



    demo地址:
    https://github.com/Xiaoben336/AudioDemo.git

     

    展开全文
  • Android MediaRecorder录音录像 暂停 继续录音 播放 ARM格式(音频一) ...Android 录音机小米商业项目开源代码 AudioRecord录音暂停 播放 Wav格式(音频二) https://blog.csdn.net/WHB20081815/arti...

    Android MediaRecorder录音录像 暂停 继续录音 播放 ARM格式(音频一)

    https://blog.csdn.net/WHB20081815/article/details/88778605

    Android 录音机小米商业项目开源代码 AudioRecord录音暂停 播放 Wav格式(音频二)

    https://blog.csdn.net/WHB20081815/article/details/88778623

    Android 录音机小米商业项目开源代码 pcm转AAC硬编码 录音暂停 播放 (音频三)

    https://blog.csdn.net/WHB20081815/article/details/88778634

    Android 录音机商业项目开源代码 pcm转AAC软编码 录音暂停 播放 (音频四)

    https://blog.csdn.net/WHB20081815/article/details/88778641

    Android 高仿唱吧 咔拉ok 商业项目开源代码 K歌合成 伴奏录音合成MP3(音频五)

    https://blog.csdn.net/WHB20081815/article/details/88778652

     

    2种AAC的编码库

    1.https://github.com/timsu/android-aac-enc)

    2.https://github.com/shaoyuan1943/AndroidRecordAAC

     

     

    开源Faac实现PCM编码AAC(libfaac)

     

    1.录音的格式 3种暂停的录音格式 软编码和硬件编码

    2.MediaRecord的录音,断点录音的一个重要类,边录制边转码  .arm 格式的生成

    3.在 Android 平台使用 AudioRecord 和  完成音频 PCM 数据的采集,并实现读写音频 wav 文件

    4.学习 MediaCodec API,完成音频 AAC 硬编、硬解

    5.AAC 软解码====硬解码和软解码的区别

    6.怎么把aar变成pcm

    7.怎么把2个aac,音乐的伴奏合并成一个(做一个ktv的软件)

    8.录音的动画,频谱图

     

     

    不多的mediacodec转aac

    https://blog.csdn.net/a512337862/article/details/72629755(播放进行解码)

    录音资源下载(代码有效果的)

    https://blog.csdn.net/lanseyuanwei2/article/details/79885963

     

     

     

    我在github上找到了一个非常有用的编解码开源项目android-aac-enc(地址:https://github.com/timsu/android-aac-enc),

    该开源项目能完美地实现将原始的pcm格式的二进制数据编码成m4a格式的数据文件,相比于FFmpeg库,这个库有以下几点优点:

     

        1. aac-enc库的体积比FFmpeg库的体积更小;

     

        2. 相比FFMpeg, aac-enc实现格式转换更加简单和快速;

     

        3. aac-enc比FFmpeg需要编译更少的底层的代码。

     

    AudioRecord:

     

      特性:该类录制的音频为原始的PCM二进制音频数据,没有文件头和文件尾,生成的PCM文件不能直接使用 Mediaplayer播放,只能使用AudioTrack播放。使用AudioRecord可以实现边录边播的音频实时处理。

    --------------------- 

     

    该开源项目使用起来也非常地简单,通过分析其示例代码我们可以通过以下四个步骤来实现音频的编码工作,代码如下:

     

    /**

      * 1.初始化编码配置

      * 

      * 32000 : 音频的比特率

      * 2 : 音频的声道

      * sampleRateInHz : 音频采样率

      * 16 :音频数据格式,PCM 16位每个样本

      * FileUtils.getAAcFilePath(mAudioRecordFileName) : aac音频文件的存储路径

      */

     encoder.init(32000, 2, sampleRateInHz, 16, FileUtils.

       getAAcFilePath(mAudioRecordFileName));

     /**

      * 2.对二进制代码进行编码

      * 

      * b :需要编码的二进制音频流

      */

        encoder.encode(b);

        /**

         * 3. 从pcm二进制数据转aac音频文件编码完成

         * 

         */

        encoder.uninit();

     

    软件编码:

     private void encodeAudio(){

      try {

       //读取录制的pcm音频文件

          DataInputStream mDataInputStream = new DataInputStream(new FileInputStream(

            FileUtils.getPcmFilePath(mAudioRecordFileName)));

        byte[] b = new byte[(int) new File(FileUtils.

          getPcmFilePath(mAudioRecordFileName)).length()];

        mDataInputStream.read(b);

        //初始化编码配置

        encoder.init(32000, 2, sampleRateInHz, 16, FileUtils.

          getAAcFilePath(mAudioRecordFileName));

        //对二进制代码进行编码

              encoder.encode(b);

              //编码完成

              encoder.uninit();

              //关闭流

              mDataInputStream.close();

              try {

               //将aac文件转码成m4a文件

                  new AACToM4A().convert(mContext, FileUtils.getAAcFilePath(mAudioRecordFileName), 

                    FileUtils.getM4aFilePath(mAudioRecordFileName));

              } catch (IOException e) {

                  Log.e("ERROR", "error converting", e);

              }

              deleteAllFiles(RECORDED_COMPLETED_DELETE);

       } catch (FileNotFoundException e) {

        // TODO Auto-generated catch block

        e.printStackTrace();

       } catch (IOException e1) {

        // TODO Auto-generated catch block

        e1.printStackTrace();

       }

     }

     

    demo地址:

    https://github.com/timsu/android-aac-enc

     

    展开全文
  • 一个录音机程序,简单的实现了广播,service等功能
  • Android MediaRecorder录音录像 暂停 继续录音 播放 ARM格式(音频一) ...Android 录音机小米商业项目开源代码 AudioRecord录音暂停 播放 Wav格式(音频二) https://blog.csdn.net/WHB20081815/arti...

    Android MediaRecorder录音录像 暂停 继续录音 播放 ARM格式(音频一)

    https://blog.csdn.net/WHB20081815/article/details/88778605

    Android 录音机小米商业项目开源代码 AudioRecord录音暂停 播放 Wav格式(音频二)

    https://blog.csdn.net/WHB20081815/article/details/88778623

    Android 录音机小米商业项目开源代码 pcm转AAC硬编码 录音暂停 播放 (音频三)

    https://blog.csdn.net/WHB20081815/article/details/88778634

    Android 录音机商业项目开源代码 pcm转AAC软编码 录音暂停 播放 (音频四)

    https://blog.csdn.net/WHB20081815/article/details/88778641

    Android 高仿唱吧 咔拉ok 商业项目开源代码 K歌合成 伴奏录音合成MP3(音频五)

    https://blog.csdn.net/WHB20081815/article/details/88778652

     

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

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

     

    Android4.1以后google就提供了MediaCodec这个类来为用户提供音视频的编码解码功能

    大致的意思是:MediaCodec处理输入数据然后产生输出数据,它异步处理数据,使用了一组输入和输入缓存。你请求到了一个空的输入缓存,将数据填满后,发送给编解码器进行处理。编解码器处理完后,将处理的结果输出到一个空的输出缓存中,我们能请求到这个输出缓存,并将缓存的数据使用。

     

    AAC的音频文件格式有ADIF和ADTS:

     

    ADIF:音频数据交换格式。这种格式的特点是,它只有一个统一的文件头,其余的都是音频数据。

     

    ADTS:音频数据传输流。它是一个有同步字的比特流。每一帧都有头信息。

     

    简单来说:ADIF不能随意解码,之后确定得到所有的数据以后才能解码,因为它只有一个头文件。

     

    ADTS可以任意解码,因为每一帧都有一个头文件。

    --------------------- 

    编码流程:

       定义输入的缓冲流inputBuffers      mediaCodec.getInputBuffers();

      把输入流通过循环在编码器中mediaCodec取出mediaCodec.dequeueOutputBuffer(bufferInfo,0);

      在处理aac的头,添加ADTS

     

    开始音频编码:

     

    从AudioRecord出来的数据通过MediaCodec处理,输出硬编码需要的格式

     

       try {

         ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();

         ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();

         int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);

         if (inputBufferIndex &gt;= 0) {

            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];

            inputBuffer.clear();

            inputBuffer.put(input);

            mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length,0, 0);

          }

          MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

          int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);

          while (outputBufferIndex >= 0) {

            outputBuffer = outputBuffers[outputBufferIndex];

            outputBuffer.position(bufferInfo.offset);

            outData = new byte[bufferInfo.size];

            outputBuffer.get(outData);

            System.arraycopy(outData, 0, output, pos, outData.length);

            pos += outData.length;

            mediaCodec.releaseOutputBuffer(outputBufferIndex, false);

            outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);  

        }

     

    其中需要注意的有dequeueInputBuffer(-1),参数表示需要得到的毫秒数,-1表示一直等,0表示不需要等,传0的话程序不会等待,但是有可能会丢帧。

    dequeueOutputBuffer方法表示dequeue output buffer ,返回成功decode的output buffer的index,返回值有时候有时候大于等于0有时候小于0,

    小于0表示MediaCodec没有成功decode,大于等于0表示成功decode。

    编码器正常的流程是喂一次数据吐一次数据,但少数时候,编码器并不会严格按照这个流程执行,有可能你往编码器喂了几次数据,编码器都没有准备好输出数据,

    那么编码器将不会吐出数据,有的时候编码器把之前没有处理好的数据,在这一次吐出数据的时候,又会吐出多次数据,所以这里用了while,在结束的时候去查询是否编码器的还有数据没有吐出来。

     

    这里基本可以成功,加上aac头就可以播放流或者保存为aac文件了。

     

    --------------------- 

     

    /**

         * 给编码出的aac裸流添加adts头字段

         *

         * @param packet 要空出前7个字节,否则会搞乱数据

         * @param packetLen

         */

        private void addADTStoPacket(byte[] packet, int packetLen) {

            int profile = 2; //AAC LC

            int freqIdx = 4; //44.1KHz

            int chanCfg = 2; //CPE

            packet[0] = (byte) 0xFF;

            packet[1] = (byte) 0xF9;

            packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));

            packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));

            packet[4] = (byte) ((packetLen & 0x7FF) >> 3);

            packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);

            packet[6] = (byte) 0xFC;

        }

     

    {

            if (aEncoder == null) {

                throw new RuntimeException(" =lgd= =请初始化音频编码器=====");

            }

     

            if (audioEncoderLoop) {

                throw new RuntimeException(" =lgd= 音频编码线程必须先停止===");

            }

            audioEncoderThread = new Thread() {

                @Override

                public void run() {

                    Log.d(TAG, "===liuguodong=====Audio 编码线程 启动...");

                    presentationTimeUs = System.currentTimeMillis() * 1000;

                    aEncoderEnd = false;

                    aEncoder.configure(audioFormat, null, null,

                            MediaCodec.CONFIGURE_FLAG_ENCODE);

                    aEncoder.start();

                    while (audioEncoderLoop && !Thread.interrupted()) {

                        try {

                            byte[] data = audioQueue.take();

                            encodeAudioData(data);

                        } catch (InterruptedException e) {

                            e.printStackTrace();

                            break;

                        }

                    }

                    if (aEncoder != null) {

                        //停止音频编码器

                        aEncoder.stop();

                        //释放音频编码器

                        aEncoder.release();

                        aEncoder = null;

                    }

     

                    audioQueue.clear();

                    Log.d(TAG, "= =lgd= ==Audio 编码线程 退出...");

                }

            };

            audioEncoderLoop = true;

            audioEncoderThread.start();

        }

     

     

    demo地址:

    https://github.com/printlybyte/AndroidPCMtoAACpushing

     

    展开全文
  • Android录音机应用

    千次阅读 2016-08-03 11:18:23
    一、不显示“删除栏”:...SoundRecorder/src/com/android/soundrecorder/ListActivity.java public void onCreateContextMenu() {  if(!StorageUtil.isForPoliceUse()){  menu.add(0, MENU_DELETE, 1, getString(R.s
  • Android 录音机的实现

    热门讨论 2013-02-21 13:47:34
    大体思路:暂停一次,便保存一次生成一个文件,点击停止时候,将所有生生成文件合并,除第一个生成文件的其他文件前六个字节删除
  • 本人业余开发的一款基于android平台的录音机,由于图片资源分辨率的原因,在480*320分辨率或更低分辨率的机器上可以正常显示布局
  • android 录音机笔记

    2014-03-04 11:21:55
    最近看了一下一个录音机的代码SoundRecorder https://github.com/MiCode/SoundRecorder.git  有点小小总结吧. 他的代码基本上是分层的.如上图.不过虽然分层了,我还是觉得好点复杂.看别人的完成工程是不是都有这种...
  • 项目app录音问题:手机系统录音机在录音过程中退到后台录音,此时打开项目app开始录像,因为录音功能被系统录音机占用,导致项目app不能正常使用录音功能,录制下来的视频文件不能正常播放。检测出现问题的原因,...
  • Android 录音机模块分析

    千次阅读 2014-04-13 22:57:49
    1. 录音机提供的功能

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 924
精华内容 369
关键字:

android录音机