安卓手机和单片机音频通信

2019-04-13 10:58发布

        这里是这几天做的实验,在安卓上的软件程序主要参考《疯狂的安卓讲义》,电路中的一些东西参考的是HiJack项目中的电路。在这里,我真的突然感慨学好模电真的很重要。在没有参考HIJACK之前,我对这些音频信号手足无措。在这个小项目学习完后,我决定再重新学习模电,感觉这一次重温应该能让我这模电知识再上一个台阶。 下面我从几个方面记录一下自己的学习过程,给自己留个记录以后翻阅,也可以给大家一个参考; 1).耳机线的接口; 2).话筒和耳机的声音原理; 3).安卓中的程序编写
1.耳机接口问题         一般MP3中的耳机就真的是耳机,其中没有话筒,分为三段左声道、右声道、地。而我们的手机中原装的耳机是带有话筒的,这种耳机分为四段分别为左声道、右声道、地、MIC。就是这手机带的四段带话筒的耳机还不同,像国内的一些手机厂商和国外的厂商,这种耳机的地和MIC可能是对换了的。有条件的可以折几个测测就知道。
2.其实话筒接收的是振动信号,以些振动信号产生变化的电流,通过耳机接口传到手机里面的处理电路,最后被采样。所以,这里用以下电路,参考HIJACK中的,单片机用PWM来输出。 用的是1KHZ占空比为50%的PWM波,最后在安卓中保存话筒采集到的数据,用的是16bit单通道格式保存为PCM,显示的波形图如下:振动幅值都是最大的-32768,32767.
在手机播放500HZ的音频时,我在耳机中测到的输入波形如下图:C1即为在左声道测得的波形,Z1、Z2为放大后的。 从手机通过耳机到单片机的电路如下:
3.安卓中的程序主要是涉及两个模块,一个是音频的录放,另一个就是文件的存储; 程序如下:APK编写的时候是支持android2.3.1以上的,大家可以在资源里下载http://download.csdn.net/detail/raoqin/5884203
界面文件main.xml                                                                 
主程序如下: package packname.test; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.RandomAccessFile; import android.app.Activity; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.SeekBar; import android.widget.Toast; public class acti extends Activity { /** Called when the activity is first created. */ //---- final String FILE_NAME = "/myraoqin.bin"; File sdCardDir = null; File targetFile = null; RandomAccessFile raf = null; //---- Button btnRecord, btnStop, btnExit; SeekBar skbVolume;//调节音量 boolean isRecording = false;//是否录放的标记 static final int frequency = 44100; static final int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;//AudioFormat.ENCODING_PCM_16BIT; int recBufSize,playBufSize; AudioRecord audioRecord; AudioTrack audioTrack; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); setTitle("Lhyraoqin"); //----------------------------SD卡文件 //获取SD卡的目录 try { sdCardDir = Environment.getExternalStorageDirectory(); targetFile = new File(sdCardDir.getCanonicalPath() + FILE_NAME ); //以指定文件创建 randomaccessfile对象 raf = new RandomAccessFile( targetFile, "rw" ); } catch(Exception e) { e.printStackTrace(); } //获取两个按钮 Button read = (Button) findViewById(R.id.read); Button write = (Button) findViewById(R.id.write); //获取两个文本框 final EditText edit1 = (EditText) findViewById(R.id.edit1); final EditText edit2 = (EditText) findViewById(R.id.edit2); //为write按钮绑定事件监听器 write.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View source) { //将edit1中的内容写入文件中 Mywrite(edit1.getText().toString()); edit1.setText(""); } }); read.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //读取指定文件中的内容并显示出来 edit2.setText(Myread()); } }); //-------------------------SD文件 //----- recBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding); playBufSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding); // ----------------------------------------- audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, recBufSize); audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, frequency, channelConfiguration, audioEncoding, playBufSize, AudioTrack.MODE_STREAM); //------------------------------------------ btnRecord = (Button) this.findViewById(R.id.btnRecord); btnRecord.setOnClickListener(new ClickEvent()); btnStop = (Button) this.findViewById(R.id.btnStop); btnStop.setOnClickListener(new ClickEvent()); btnExit = (Button) this.findViewById(R.id.btnExit); btnExit.setOnClickListener(new ClickEvent()); skbVolume=(SeekBar)this.findViewById(R.id.skbVolume); skbVolume.setMax(100);//音量调节的极限 skbVolume.setProgress(70);//设置seekbar的位置值 audioTrack.setStereoVolume(0.7f, 0.7f);//设置当前音量大小 skbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { float vol=(float)(seekBar.getProgress())/(float)(seekBar.getMax()); audioTrack.setStereoVolume(vol, vol);//设置音量 } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // TODO Auto-generated method stub } }); } //------------ //读写文件用的 // // //---------------- private String Myread() { try { //如果手机插入了SD卡,而且应用程序具有访问SD的权限 if(Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED) ) { //获取SD卡的目录 File sdCardDir = Environment.getExternalStorageDirectory(); //获取指定文件对应的输入流 FileInputStream fis = new FileInputStream(sdCardDir.getCanonicalPath() + FILE_NAME); //将指定输入流包装成Bufferreader BufferedReader br = new BufferedReader(new InputStreamReader(fis)); StringBuilder sb = new StringBuilder(""); String line = null; while((line = br.readLine()) != null) { sb.append(line); } return sb.toString(); } /* //打开文件输入流 FileInputStream fis = openFileInput("myFILE_NAME"); byte[] buff = new byte[1024]; int hasRead = 0; StringBuilder sb = new StringBuilder(""); while( (hasRead = fis.read(buff)) > 0 ) { sb.append(new String(buff, 0, hasRead)); } return sb.toString(); */ } catch (Exception e) { e.printStackTrace(); } return null; } //------------------------- //读写文件用的 // // //--------------------------------- private void Mywrite(String content) { try { //如果手机插入了SD卡,而且应用程序具有访问SD的权限 if(Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED) ) { /* //获取SD卡的目录 File sdCardDir = Environment.getExternalStorageDirectory(); File targetFile = new File(sdCardDir.getCanonicalPath() + FILE_NAME ); //以指定文件创建 randomaccessfile对象 RandomAccessFile raf = new RandomAccessFile( targetFile, "rw" ); */ //输出文件内容 raf.write(content.getBytes()); byte[] mybytes = new byte[3]; //raf.close(); } /* //以追加模式打开文件输出流 FileOutputStream fos = openFileOutput("myFILE_NAME", MODE_APPEND); //将fileoutputstream包装成printstream PrintStream ps = new PrintStream(fos); //输出文件内容 ps.println(content); ps.close(); */ } catch(Exception e) { e.printStackTrace(); } } //------------------------- //读写文件用的 // // //--------------------------------- private void Mywrite(byte[] mybytes) { try { //如果手机插入了SD卡,而且应用程序具有访问SD的权限 if(Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED) ) { //输出文件内容 raf.write(mybytes); } } catch(Exception e) { e.printStackTrace(); } } //------------------------- // // // //--------------------------------- @Override protected void onDestroy() { try { raf.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } super.onDestroy(); android.os.Process.killProcess(android.os.Process.myPid()); } class ClickEvent implements View.OnClickListener { @Override public void onClick(View v) { if (v == btnRecord) { isRecording = true; new RecordPlayThread().start();// 开一条线程边录边放 } else if (v == btnStop) { isRecording = false; } else if (v == btnExit) { isRecording = false; acti.this.finish(); } } } class RecordPlayThread extends Thread { public void run() { try { byte[] buffer = new byte[recBufSize]; audioRecord.startRecording();//开始录制 audioTrack.play();//开始播放 while (isRecording) { //从MIC保存数据到缓冲区 int bufferReadResult = audioRecord.read(buffer, 0, recBufSize); byte[] tmpBuf = new byte[bufferReadResult]; System.arraycopy(buffer, 0, tmpBuf, 0, bufferReadResult); //for(int i=0; i
最后记得在AndroidManifest.xml文件中加入权限
程序将把录到的PCM文件保存到SD上的myraoqin.bin文件中,这个文件就是16bit单通道文件,可以用GoldWave软件打开进行播放测试。