管理音频焦点
情景:当你的app隐退到后台,而其他也有播放能力的app浮现在前台,这个时候,你可能要暂停你原有app的播放功能,和解除监听Media Button,把控制权交给前台的APP。
这就需要监听音频的焦点。
在开始播放之前,请求焦点,使用AudioManager的requestAudioFocus方法。
当你请求音频焦点,你可以指定你要监听的流类型(比如STREAM_MUSIC)和指定你要占有焦点多久。
当然从编程的角度来看,app获取焦点,其它app失去焦点,你应该都需要有所反应。
示例:请求音频焦点
01 |
AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE); |
03 |
// Request audio focus for playback |
04 |
int result = am.requestAudioFocus(focusChangeListener, |
05 |
// Use the music stream. |
06 |
AudioManager.STREAM_MUSIC, |
07 |
// Request permanent focus. |
08 |
AudioManager.AUDIOFOCUS_GAIN); |
10 |
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { |
应对失去焦点的监听:
01 |
private OnAudioFocusChangeListener focusChangeListener = |
02 |
new OnAudioFocusChangeListener() { |
04 |
public void onAudioFocusChange( int focusChange) { |
06 |
(AudioManager)getSystemService(Context.AUDIO_SERVICE); |
08 |
switch (focusChange) { |
09 |
case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) : |
10 |
// Lower the volume while ducking. |
11 |
mediaPlayer.setVolume( 0 .2f, 0 .2f); |
14 |
case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) : |
18 |
case (AudioManager.AUDIOFOCUS_LOSS) : |
20 |
ComponentName component = |
21 |
new ComponentName(AudioPlayerActivity. this , |
22 |
MediaControlReceiver. class ); |
23 |
am.unregisterMediaButtonEventReceiver(component); |
26 |
case (AudioManager.AUDIOFOCUS_GAIN) : |
27 |
// Return the volume to normal and resume if paused. |
28 |
mediaPlayer.setVolume(1f, 1f); |
放弃音频焦点:
2 |
(AudioManager)getSystemService(Context.AUDIO_SERVICE); |
4 |
am.abandonAudioFocus(focusChangeListener); |
当你戴上耳机的时候,你可能需要降低音量或者先暂停播放,如何监听这种输出方式的改变呢?
答:
1 |
private class NoisyAudioStreamReceiver extends BroadcastReceiver { |
3 |
public void onReceive(Context context, Intent intent) { |
4 |
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals |
5 |
(intent.getAction())) { |
录音
使用AudioRecord类去录音。创建一个AudioRecorder,指定资源,频率,通道配置,音频编码,和缓冲区大小。
1 |
int bufferSize = AudioRecord.getMinBufferSize(frequency, |
4 |
AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, |
5 |
frequency, channelConfiguration, |
6 |
audioEncoding, bufferSize); |
频率、音频编码、和通道配置会影响录音的大小和质量。
出去私有的考虑,Android需要RECORD_AUDIO权限:
1 |
< uses-permission android:name=”android.permission.RECORD_AUDIO”/> |
当AudioRecorder对象被初始化,然后可以通过startRecording方法去开始异步录音,使用read方法将原始的音频数据放入录音缓冲区:
1 |
audioRecord.startRecording(); |
3 |
[ ... populate the buffer ... ] |
4 |
int bufferReadResult = audioRecord.read(buffer, 0 , bufferSize); |
录下的原始音频数据后,拿什么播放呢?
答:使用AudioTrack去播放该类音频。
录音的例子:
01 |
int frequency = 11025 ; |
02 |
int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; |
03 |
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; |
06 |
new File(Environment.getExternalStorageDirectory(), “raw.pcm”); |
08 |
// Create the new file. |
11 |
} catch (IOException e) { |
12 |
Log.d(TAG, “IO Exception”, e); |
16 |
OutputStream os = new FileOutputStream(file); |
17 |
BufferedOutputStream bos = new BufferedOutputStream(os); |
18 |
DataOutputStream dos = new DataOutputStream(bos); |
20 |
int bufferSize = AudioRecord.getMinBufferSize(frequency, |
23 |
short [] buffer = new short [bufferSize]; |
25 |
// Create a new AudioRecord object to record the audio. |
26 |
AudioRecord audioRecord = |
27 |
new AudioRecord(MediaRecorder.AudioSource.MIC, |
30 |
audioEncoding, bufferSize); |
31 |
audioRecord.startRecording(); |
34 |
int bufferReadResult = audioRecord.read(buffer, 0 , bufferSize); |
35 |
for ( int i = 0 ; i < bufferReadResult; i++) |
36 |
dos.writeShort(buffer[i]); |
41 |
} catch (Throwable t) { |
42 |
Log.d(TAG, “An error occurred during recording”, t); |
AudioTrack播放声音
1 |
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, |
6 |
AudioTrack.MODE_STREAM); |
注意前面的参数要与你之前录音的参数一致。
2 |
audioTrack.write(audio, 0 , audioLength); |
write方法将原始的音频数据加入到播放缓冲区中。
创建Sound Pool
一般用来播放短促的声音,支持多音频同步播放。
直接看例子:
02 |
SoundPool sp = new SoundPool(maxStreams, AudioManager.STREAM_MUSIC, 0 ); |
04 |
int track1 = sp.load( this , R.raw.track1, 0 ); |
05 |
int track2 = sp.load( this , R.raw.track2, 0 ); |
06 |
int track3 = sp.load( this , R.raw.track3, 0 ); |
08 |
track1Button.setOnClickListener( new OnClickListener() { |
09 |
public void onClick(View v) { |
10 |
sp.play(track1, 1 , 1 , 0 , - 1 , 1 ); |
14 |
track2Button.setOnClickListener( new OnClickListener() { |
15 |
public void onClick(View v) { |
16 |
sp.play(track2, 1 , 1 , 0 , 0 , 1 ); |
20 |
track3Button.setOnClickListener( new OnClickListener() { |
21 |
public void onClick(View v) { |
22 |
sp.play(track3, 1 , 1 , 0 , 0 , 0 .5f); |
26 |
stopButton.setOnClickListener( new OnClickListener() { |
27 |
public void onClick(View v) { |
34 |
chipmunkButton.setOnClickListener( new OnClickListener() { |
35 |
public void onClick(View v) { |
36 |
sp.setRate(track1, 2f); |
Android2.2(Api Level 8)引入两个非常方便的方法,autoPause和autoResume,分别会暂停和运行状态,所有活跃的音频流。
若不再需要这些音频集合,就可以soundPool.release();去释放资源。
照相机拍照
使用Intents去拍照:
1 |
startActivityForResult( |
2 |
new Intent(MediaStore.ACTION_IMAGE_CAPTURE), TAKE_PICTURE); |
当然对应的onActivityResult,默认的返回的照片会以缩略图的形式。
如果想获取完整大小的图片,则需要先指定存储的目标文件,下面例子展示:
01 |
// Create an output file. |
02 |
File file = new File(Environment.getExternalStorageDirectory(), |
04 |
Uri outputFileUri = Uri.fromFile(file); |
06 |
// Generate the Intent. |
07 |
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); |
08 |
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri); |
10 |
// Launch the camera app. |
11 |
startActivityForResult(intent, TAKE_PICTURE); |
注意:一旦你以这种方式启动后,就不会有缩略图返回了,所以所接收到得Intent将为null。
下面这个例子的onActivityResult对这两种情况做了处理:
02 |
protected void onActivityResult( int requestCode, |
03 |
int resultCode, Intent data) { |
04 |
if (requestCode == TAKE_PICTURE) { |
05 |
// Check if the result includes a thumbnail Bitmap |
07 |
if (data.hasExtra(“data”)) { |
08 |
Bitmap thumbnail = data.getParcelableExtra(“data”); |
09 |
imageView.setImageBitmap(thumbnail); |
12 |
// If there is no thumbnail image data, the image |
13 |
// will have been stored in the target output URI. |
15 |
// Resize the full image to fit in out image view. |
16 |
int width = imageView.getWidth(); |
17 |
int height = imageView.getHeight(); |
19 |
BitmapFactory.Options factoryOptions = new |
20 |
BitmapFactory.Options(); |
22 |
factoryOptions.inJustDecodeBounds = true ; |
23 |
BitmapFactory.decodeFile(outputFileUri.getPath(), |
26 |
int imageWidth = factoryOptions.outWidth; |
27 |
int imageHeight = factoryOptions.outHeight; |
29 |
// Determine how much to scale down the image |
30 |
int scaleFactor = Math.min(imageWidth/width, |
33 |
// Decode the image file into a Bitmap sized to fill the View |
34 |
factoryOptions.inJustDecodeBounds = false ; |
35 |
factoryOptions.inSampleSize = scaleFactor; |
36 |
factoryOptions.inPurgeable = true ; |
39 |
BitmapFactory.decodeFile(outputFileUri.getPath(), |
42 |
imageView.setImageBitmap(bitmap); |
直接控制照相机
首先这个少不了:
1 |
< uses-permission android:name=”android.permission.CAMERA”/> |
获取Camera通过:
Camera camera = Camera.open();
当你使用完了,记得释放资源哦:
camera.release();
照相机的属性
1 |
Camera.Parameters parameters = camera.getParameters(); |
通过此,你可以找到很多关于照相机的属性,有些参数是基于平台版本的。
你可以获得焦点的长度,还有相对水平和垂直的角度,分别通过getFocalLength和get[Horizontal/Vertical]ViewAngle。
Android 2.3(Api Level 9)引入getFocusDistance方法,你可以用来估计镜头和对象之间的距离,此方法会注入一个浮点数组,包含近、远、最优焦点距离;
01 |
float [] focusDistances = new float [ 3 ]; |
03 |
parameters.getFocusDistances(focusDistances); |
06 |
focusDistances[Camera.Parameters.FOCUS_DISTANCE_NEAR_INDEX]; |
08 |
focusDistances[Camera.Parameters.FOCUS_DISTANCE_FAR_INDEX]; |
10 |
focusDistances[Camera.Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX]; |
照相机设置和图像参数
设置参数的方法,类似于set*,从而修改Parameter对象,修改完之后:
camera.setParameters(parameters);
具体参数细节就不介绍了。
使用照相机预览
同样SurfaceView又派上用场了。
看段框架代码:
01 |
public class CameraActivity extends Activity implements |
02 |
SurfaceHolder.Callback { |
04 |
private static final String TAG = “CameraActivity”; |
06 |
private Camera camera; |
09 |
public void onCreate(Bundle savedInstanceState) { |
10 |
super .onCreate(savedInstanceState); |
11 |
setContentView(R.layout.main); |
13 |
SurfaceView surface = (SurfaceView)findViewById(R.id.surfaceView); |
14 |
SurfaceHolder holder = surface.getHolder(); |
15 |
holder.addCallback( this ); |
16 |
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); |
17 |
holder.setFixedSize( 400 , 300 ); |
20 |
public void surfaceCreated(SurfaceHolder holder) { |
22 |
camera.setPreviewDisplay(holder); |
23 |
camera.startPreview(); |
24 |
// TODO Draw over the preview if required. |
25 |
} catch (IOException e) { |
26 |
Log.d(TAG, “IO Exception”, e); |
31 |
public void surfaceDestroyed(SurfaceHolder holder) { |
35 |
public void surfaceChanged(SurfaceHolder holder, int format, |
36 |
int width, int height) { |
40 |
protected void onPause() { |
46 |
protected void onResume() { |
48 |
camera = Camera.open(); |
调用camera的setPreviewCallback方法,传入一个PreviewCallback的实现,重写onPreviewFrame方法。
01 |
camera.setPreviewCallback( new PreviewCallback() { |
02 |
public void onPreviewFrame( byte [] data, Camera camera) { |
05 |
Size previewSize = camera.getParameters().getPreviewSize(); |
06 |
YuvImage image = new YuvImage(data, ImageFormat.NV21, |
07 |
previewSize.width, previewSize.height, null ); |
08 |
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
11 |
new Rect( 0 , 0 ,previewSize.width, previewSize.height), |
12 |
quality, outputStream); |
14 |
// TODO Do something with the preview image. |
Android 4.0加入了人脸识别的API这里就不多说了。
拍照
前面这些都配置过了,那么如何拍照呢?
答:使用camera对象的takePicture方法,传入一个ShutterCallback和两个PictureCallback实现(一个为了RAW,另外一个为了JPEG编码的图像)。
例子:框架代码,拍照和保存JPEG图像到SD卡:
01 |
private void takePicture() { |
02 |
camera.takePicture(shutterCallback, rawCallback, jpegCallback); |
05 |
ShutterCallback shutterCallback = new ShutterCallback() { |
06 |
public void onShutter() { |
07 |
// TODO Do something when the shutter closes. |
11 |
PictureCallback rawCallback = new PictureCallback() { |
12 |
public void onPictureTaken( byte [] data, Camera camera) { |
13 |
// TODO Do something with the image RAW data. |
17 |
PictureCallback jpegCallback = new PictureCallback() { |
18 |
public void onPictureTaken( byte [] data, Camera camera) { |
19 |
// Save the image JPEG data to the SD card |
20 |
FileOutputStream outStream = null ; |
22 |
String path = Environment.getExternalStorageDirectory() + |
25 |
outStream = new FileOutputStream(path); |
26 |
outStream.write(data); |
28 |
} catch (FileNotFoundException e) { |
29 |
Log.e(TAG, “File Note Found”, e); |
30 |
} catch (IOException e) { |
31 |
Log.e(TAG, “IO Exception”, e); |
- android之多媒体篇(一)
Android 4.0.3(Api Level 15)支持的多媒体格式. 注意:有些设备可能支持其他的文件格式. 1.Audio AAC LC/LTP.HE-AACv1(AAC+).AMR-NB.AM ...
- android之多媒体篇(三)
录像 Android提供了2种方案去录像. 方案一: 最简单的方式就是使用Intents去启动App来帮助你完成.这个方案使你能够指定输出的位置和视频的质量.这方案通常是最好的方法,应该可以用在多种情 ...
- 【转】android 电容屏(二):驱动调试之基本概念篇
关键词:android 电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung ...
- Android APP压力测试(二)之Monkey信息自动收集脚本
Android APP压力测试(二) 之Monkey信息自动收集脚本 前言: 上一篇Monkey介绍基本搬抄官方介绍,主要是为了自己查阅方便.本文重点介绍我在进行Monkey时如何自动收集相关信息 ...
- Android项目实战(二十八):Zxing二维码实现及优化
前言: 多年之前接触过zxing实现二维码,没想到今日项目中再此使用竟然使用的还是zxing,百度之,竟是如此牛的玩意. 当然,项目中我们也许只会用到二维码的扫描和生成两个功能,所以不必下载完整的ja ...
- Android多线程分析之二:Thread的实现
Android多线程分析之二:Thread的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多线程分析之一 ...
- Android中插件开发篇之----动态加载Activity(免安装运行程序)
一.前言 又到周末了,时间过的很快,今天我们来看一下Android中插件开发篇的最后一篇文章的内容:动态加载Activity(免安装运行程序),在上一篇文章中说道了,如何动态加载资源(应用换肤原理解析 ...
- Android Studio系列教程二--基本设置与运行
Android Studio系列教程二--基本设置与运行 2014 年 11 月 28 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处! 上面一篇博客,介绍了Studio的 ...
- Android高手进阶教程(二十八)之---Android ViewPager控件的使用(基于ViewPager的横向相册)!!!
分类: Android高手进阶 Android基础教程 2012-09-14 18:10 29759人阅读 评论(35) 收藏 举报 android相册layoutobjectclassloade ...
随机推荐
- leetcode:Integer to Roman(整数转化为罗马数字)
Question: Given an integer, convert it to a roman numeral. Input is guaranteed to be within the rang ...
- extern "c" 的作用
作用:实现C和C++混合编程. 原理:C和C++编译器编译之后,函数名会编译成不同的名字,链接阶段名字查找会找不到目标,后面实例中会详解. 用法:①.c文件中定义的函数,.cpp文件要调用时,该.cp ...
- Python:字符串
一.序列的概念 序列是容器类型,顾名思义,可以想象,“成员”们站成了有序的队列,我们从0开始进行对每个成员进行标记,0,1,2,3,...,这样,便可以通过下标访问序列的一个或几个成员,就像C语言中的 ...
- 如何创建Asp.net MVC ViewModel
ASP.NET MVC View Model Patterns Since MVC has been released I have observed much confusion about how ...
- hadoop2.5.2学习及实践笔记(二)—— 编译源代码及导入源码至eclipse
生产环境中hadoop一般会选择64位版本,官方下载的hadoop安装包中的native库是32位的,因此运行64位版本时,需要自己编译64位的native库,并替换掉自带native库. 源码包下的 ...
- POJ 2395 Out of Hay(MST)
[题目链接]http://poj.org/problem?id=2395 [解题思路]找最小生成树中权值最大的那条边输出,模板过的,出现了几个问题,开的数据不够大导致运行错误,第一次用模板,理解得不够 ...
- 麒麟OS剽窃
今年对于我们的IT行业来说可以算是耻辱的一年. 首先是“汉芯丑闻”,上海交大研制了一个所谓的国内第一个完全拥有自主知识产 权的DSP芯片(数字信号微处理器)——“汉芯”,研制人陈进教授以此领取政府一亿 ...
- Kali Linux 安装教程-转
rootoorotor昨天折腾了 Kali Linux 1.0,把大概的配置过程记录下来,希望对想接触或使用Kali Linux的同学有所帮助. 请注意: 1.本文为面向新手的教程,没技术含量,没 ...
- vs常用插件之javsscript插件
1.JSEnhancements 折叠JS和CSS代码 http://visualstudiogallery.msdn.microsoft.com/0696ad60-1c68-4b2a-9646-4b ...
- commondline 之二 执行类
E:\cn\zno\commandline\Test.class package cn.zno.commandline; import java.lang.management.ManagementF ...