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

        这里是这几天做的实验,在安卓上的软件程序主要参考《疯狂的安卓讲义》,电路中的一些东西参考的是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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="fill_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent">

    <Button android:layout_height="wrap_content" android:text="开始边录边放" 
            android:layout_width="fill_parent" android:id="@+id/btnRecord"></Button>
    <Button android:id="@+id/btnStop" android:layout_width="fill_parent" 
            android:text="停止" android:layout_height="wrap_content"></Button>
    <Button android:layout_width="fill_parent" android:layout_height="wrap_content" 
            android:id="@+id/btnExit" android:text="退出"></Button>
    <TextView android:id="@+id/TextView01" android:layout_height="wrap_content" 
            android:layout_width="fill_parent" android:text="程序音量调节"></TextView>
    <SeekBar android:layout_width="fill_parent" android:id="@+id/skbVolume" 
            android:layout_height="wrap_content"></SeekBar>
    <TableRow android:layout_width="match_parent" android:layout_height="wrap_content" 
            android:id="@+id/tableRow1">
        <Button android:id="@+id/read" android:layout_height="wrap_content" 
            android:layout_width="wrap_content" android:text="读"></Button>
        <Button android:id="@+id/write" android:layout_height="wrap_content" 
            android:layout_width="wrap_content" android:text="写"></Button>
    </TableRow>
    <EditText android:hint="EditText" android:id="@+id/edit1" 
              android:layout_width="match_parent" android:layout_height="wrap_content"></EditText>
    <EditText android:layout_width="match_parent" android:hint="EditText" 
              android:id="@+id/edit2" android:layout_height="wrap_content"></EditText>

</LinearLayout> 

主程序如下:

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<bufferReadResult; i++)
					//{
						/*
						if( i%32 ==0 ) tmpBuf[i] = 1;
						if( i%32 ==1 ) tmpBuf[i] = 11;
						if( i%32 ==2 ) tmpBuf[i] = 1;
						if( i%32 ==3 ) tmpBuf[i] = 11;
						if( i%32 ==4 ) tmpBuf[i] = 1;
						if( i%32 ==5 ) tmpBuf[i] = 11;
						if( i%32 ==6 ) tmpBuf[i] = 1;
						if( i%32 ==7 ) tmpBuf[i] = 11;
						if( i%32 ==8 ) tmpBuf[i] = 1;
						if( i%32 ==9 ) tmpBuf[i] = 11;
						if( i%32 ==10 ) tmpBuf[i] = 1;
						if( i%32 ==11 ) tmpBuf[i] = 11;
						if( i%32 ==12 ) tmpBuf[i] = 1;
						if( i%32 ==13 ) tmpBuf[i] = 11;
						if( i%32 ==14 ) tmpBuf[i] = 1;
						if( i%32 ==15 ) tmpBuf[i] = 11;
						
						if( i%32 ==16 ) tmpBuf[i] = 1;
						if( i%32 ==17 ) tmpBuf[i] = -11;
						if( i%32 ==18 ) tmpBuf[i] = 1;
						if( i%32 ==19 ) tmpBuf[i] = -11;
						if( i%32 ==20 ) tmpBuf[i] = 1;
						if( i%32 ==21 ) tmpBuf[i] = -11;
						if( i%32 ==22 ) tmpBuf[i] = 1;
						if( i%32 ==23 ) tmpBuf[i] = -11;
						if( i%32 ==24 ) tmpBuf[i] = 1;
						if( i%32 ==25 ) tmpBuf[i] = -11;
						if( i%32 ==26 ) tmpBuf[i] = 1;
						if( i%32 ==27 ) tmpBuf[i] = -11;
						if( i%32 ==28 ) tmpBuf[i] = 1;
						if( i%32 ==29 ) tmpBuf[i] = -11;
						if( i%32 ==30 ) tmpBuf[i] = 1;
						if( i%32 ==31 ) tmpBuf[i] = -11;
						*/
						/*
						if( i%32 ==0 ) tmpBuf[i] = 1;
						if( i%32 ==1 ) tmpBuf[i] = 11;
						if( i%32 ==2 ) tmpBuf[i] = 1;
						if( i%32 ==3 ) tmpBuf[i] = 11;
						if( i%32 ==4 ) tmpBuf[i] = 1;
						if( i%32 ==5 ) tmpBuf[i] = 11;
						if( i%32 ==6 ) tmpBuf[i] = 1;
						if( i%32 ==7 ) tmpBuf[i] = 11;
						if( i%32 ==8 ) tmpBuf[i] = 1;
						if( i%32 ==9 ) tmpBuf[i] = 11;
						if( i%32 ==10 ) tmpBuf[i] = 1;
						if( i%32 ==11 ) tmpBuf[i] = 11;
						if( i%32 ==12 ) tmpBuf[i] = 1;
						if( i%32 ==13 ) tmpBuf[i] = 11;
						if( i%32 ==14 ) tmpBuf[i] = 1;
						if( i%32 ==15 ) tmpBuf[i] = 11;
						
						if( i%32 ==16 ) tmpBuf[i] = -1;
						if( i%32 ==17 ) tmpBuf[i] = 11;
						if( i%32 ==18 ) tmpBuf[i] = -1;
						if( i%32 ==19 ) tmpBuf[i] = 11;
						if( i%32 ==20 ) tmpBuf[i] = -1;
						if( i%32 ==21 ) tmpBuf[i] = 11;
						if( i%32 ==22 ) tmpBuf[i] = -1;
						if( i%32 ==23 ) tmpBuf[i] = 11;
						if( i%32 ==24 ) tmpBuf[i] = -1;
						if( i%32 ==25 ) tmpBuf[i] = 11;
						if( i%32 ==26 ) tmpBuf[i] = -1;
						if( i%32 ==27 ) tmpBuf[i] = 11;
						if( i%32 ==28 ) tmpBuf[i] = -1;
						if( i%32 ==29 ) tmpBuf[i] = 11;
						if( i%32 ==30 ) tmpBuf[i] = -1;
						if( i%32 ==31 ) tmpBuf[i] = 11;
						*/
					//}
					Mywrite(tmpBuf);
					//写入数据即播放
					int xiedeshu = audioTrack.write(tmpBuf, 0, tmpBuf.length);
					
				}
				audioTrack.stop();
				audioRecord.stop();
			} 
			catch (Throwable t) 
			{
				Toast.makeText(acti.this, t.getMessage(), 1000);
			}
		}
	};
}

最后记得在AndroidManifest.xml文件中加入权限

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission> 

程序将把录到的PCM文件保存到SD上的myraoqin.bin文件中,这个文件就是16bit单通道文件,可以用GoldWave软件打开进行播放测试。


  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值