简体   繁体   English

访问Android媒体流以进行音频可视化

[英]Accessing the Android media stream for audio visualization

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 out how to do this with the Android API. 问题是,我似乎无法弄清楚如何使用Android API执行此操作。

My app would pick up the currently playing media stream and then depending upon the volume that is playing at that time, it would display more or less bars on the screen. 我的应用程序会选择当前播放的媒体流,然后根据当时正在播放的音量,它会在屏幕上显示更多或更少的小节。

How can I do this? 我怎样才能做到这一点? It looks like I could do something like this using the microphone, but I want to be able to do it for music, podcasts, etc. 看起来我可以使用麦克风做这样的事情,但我希望能够为音乐,播客等做到这一点。

It looks like in 2.3 things have changed here, there is permissions now 看起来在2.3这里已经改变了,现在有权限

<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. 并且在aosp中有一个AudioCapture助手类可以帮助动态壁纸正确地完成它。 https://android.googlesource.com/platform/packages/wallpapers/MusicVisualization/+/gingerbread-release/src/com/android/musicvis/AudioCapture.java 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. AOSP中的一个进口与公共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. 这都是2.3 api,它显然返回了一个低分辨率的音频流,但不足以录音。

The MusicVisualization wallpaper source is available at the AOSP . MusicVisualization壁纸源可在AOSP获得 It basically seems to involve calling MediaPlayer.snoop() , an undocumented method added in Eclair. 它基本上似乎涉及调用MediaPlayer.snoop() ,一个在Eclair中添加的未记录的方法。

Basically what roskit said except with a minor return value modification from 基本上是什么roskit说,除了一个小的返回值修改

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;
           }
    }

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;
    }
}

A slightly faster snoop() would be to call Class.getMethod() once, and then to use a custom parseInt() instead of Integer.parseInt() ... 稍微快一点的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;
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM