Доступ к медиа-потоку Android для аудио-визуализации
По сути, я хочу сделать аудио-визуализатор. Я знаю, что это возможно, потому что мой телефон поставляется с несколькими живыми обоями, которые делают это. Проблема в том, что я не могу понять, как это сделать с помощью Android API.
Мое приложение получило бы воспроизводимый в данный момент поток мультимедиа, а затем, в зависимости от громкости, воспроизводимой в это время, оно отображало бы более или менее полосы на экране.
Как я могу это сделать? Похоже, что я мог бы сделать что-то подобное с помощью микрофона, но я хочу быть в состоянии сделать это для музыки, подкастов и т. Д.
6 ответов
Источник обоев MusicVisualization доступен в AOSP. Похоже, это связано с MediaPlayer.snoop()
недокументированный метод добавлен в Eclair.
Похоже, что в 2.3 все изменилось, сейчас есть разрешения
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
А в aosp есть класс помощника AudioCapture, который поможет живым обоям сделать это правильно. https://android.googlesource.com/platform/packages/wallpapers/MusicVisualization/+/gingerbread-release/src/com/android/musicvis/AudioCapture.java
Редактировать:
Один из импортов в AOSP не соответствует общедоступному API.import android.media.audiofx.Visualizer;
этоimport android.media.Visualizer;
если это вызывает головную боль, не забудьте переключиться. Это все 2,3 API, он, очевидно, возвращает аудиопоток с низким разрешением, а не достаточно хорошо для записи.
В основном то, что сказал Роскит, за исключением незначительного изменения возвращаемого значения из
return 0;
в
return Integer.parseInt( (m.invoke(c, outData, kind)).toString() );
Другими словами:
public static int snoop(short [] outData, int kind){
try {
Class c = MediaPlayer.class;
Method m = c.getMethod("snoop", outData.getClass(), Integer.TYPE);
m.setAccessible(true);
return Integer.parseInt( (m.invoke(c, outData, kind)).toString() );
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return 1;
}
}
Вот как я это сделал из своего приложения:
protected short [] mAudioData = new short[1024];
Тогда в цикле:
int res = snoop(mAudioData, 0);
А вот и сама функция:
public static int snoop(short [] outData, int kind){
try {
Class c = MediaPlayer.class;
Method m = c.getMethod("snoop", outData.getClass(), Integer.TYPE);
m.setAccessible(true);
m.invoke(c, outData, kind);
return 0;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return 1;
}
}
Немного быстрее snoop()
будет звонить Class.getMethod()
один раз, а затем использовать пользовательский parseInt()
вместо Integer.parseInt()
...
private static Method mSnoop;
//..(http://nadeausoftware.com/node/97)..
private static int customParseInt( final String s )
{
// Check for a sign.
int num = 0;
int sign = -1;
final int len = s.length( );
final char ch = s.charAt( 0 );
if ( ch == '-' )
sign = 1;
else
num = '0' - ch;
// Build the number.
int i = 1;
while ( i < len )
num = num*10 + '0' - s.charAt( i++ );
return sign * num;
}
private static int snoop(short [] outData, int kind)
{
if ( mSnoop != null )
{
try
{
return customParseInt( (mSnoop.invoke( MediaPlayer.class , outData, kind)).toString() );
}
catch ( Exception e )
{
Log.e( TAG, "Failed to MediaPlayer.snoop()!", e );
return 1;
}
}
else
{
try {
Class c = MediaPlayer.class;
Method m = c.getMethod("snoop", outData.getClass(), Integer.TYPE);
m.setAccessible(true);
mSnoop = m;
return customParseInt( (m.invoke(c, outData, kind)).toString() );
}
catch (Exception e)
{
Log.e( TAG, "Failed to MediaPlayer.snoop()!", e );
return 1;
}
}
}
Посмотрите http://code.google.com/p/moonblink/wiki/Tricorder для примера.