Basically, I want to make an audio visualizer. I know it\'s possible, because my phone came with a few live wallpapers that do it. The problem is, I can\'t seem to figure
The MusicVisualization wallpaper source is available at the AOSP. It basically seems to involve calling MediaPlayer.snoop(), an undocumented method added in Eclair.
It looks like in 2.3 things have changed here, there is permissions now
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
And there is a AudioCapture helper class in the aosp to help the live wallpapers do it properly. https://android.googlesource.com/platform/packages/wallpapers/MusicVisualization/+/gingerbread-release/src/com/android/musicvis/AudioCapture.java
Edit:
One of the imports in the AOSP doesn't match the public api.
import android.media.audiofx.Visualizer;
it is
import android.media.Visualizer;
if it gives a headache make sure to switch. This is all 2.3 api, it apparently returns a low-resolution audio stream for viz, but not good enough for recording.
A slightly faster snoop()
would be to call Class.getMethod()
once, and then to use a custom parseInt()
instead of 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;
}
}
}
This is how I did it from my application:
protected short [] mAudioData = new short[1024];
Then in the loop:
int res = snoop(mAudioData, 0);
And here is the function itself:
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;
}
}
Look at http://code.google.com/p/moonblink/wiki/Tricorder for an example.
Basically what roskit said except with a minor return value modification from
return 0;
to
return Integer.parseInt( (m.invoke(c, outData, kind)).toString() );
In other words:
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;
}
}