Ответ 1
Источник обоев MusicVisualization доступен на AOSP. В основном это связано с вызовом MediaPlayer.snoop()
, недокументированный метод, добавленный в Eclair.
В принципе, я хочу сделать аудио визуализатор. Я знаю, это возможно, потому что мой телефон пришел с несколькими живыми обоями, которые это делают. Проблема в том, что я не могу понять, как это сделать с Android API.
Мое приложение подхватит текущий воспроизводимый медиа-поток, а затем в зависимости от того, что воспроизводится в то время, оно отображает больше или меньше баров на экране.
Как я могу это сделать? Похоже, я могу сделать что-то подобное с помощью микрофона, но я хочу, чтобы это можно было сделать для музыки, подкастов и т.д.
Источник обоев MusicVisualization доступен на AOSP. В основном это связано с вызовом MediaPlayer.snoop()
, недокументированный метод, добавленный в Eclair.
Похоже, что в 2.3 здесь все изменилось, теперь есть разрешения
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
И есть вспомогательный класс AudioCapture в aosp, чтобы помочь живым обоям сделать это правильно. https://android.googlesource.com/platform/packages/wallpapers/MusicVisualization/+/gingerbread-release/src/com/android/musicvis/AudioCapture.java
Edit:
Один из импорта в AOSP не соответствует публичному api. import android.media.audiofx.Visualizer;
это import android.media.Visualizer;
если он дает головную боль, обязательно переключитесь. Это все 2.3 api, он, по-видимому, возвращает аудиопоток низкого разрешения, но не достаточно хорош для записи.
В основном то, что сказал roskit, за исключением незначительной модификации возвращаемого значения из
return 0;
to
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 для примера.