javaandroidaudioandroid-mediaplayeraudioeffect

Android MediaPlayer with AudioEffect : Getting Error (-22,0)


Well, simply this is my requirement..

I have a WAV file, I want to open it, add some effects, and play.

I am using MediaPlayer to play file, and PresetReverb to add some effects.

This is my code

public void playSound(){
   String fullPath = MainActivity.this.filePath + "tmpaudio.wav";

   final MediaPlayer player = new MediaPlayer();
   player.setDataSource(this, Uri.parse(fullPath));

   PresetReverb pReverb = new PresetReverb(0,player.getAudioSessionId());
   pReverb.setPreset(PresetReverb.PRESET_LARGEROOM);
   pReverb.setEnabled(true);
   player.attachAuxEffect(eReverb.getId());
   player.setAuxEffectSendLevel(1.0f);

   //prepare for playback
   player.prepare();

   // Media prepared listener
   player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
      public void onPrepared(MediaPlayer mp) {
         //play
         player.start();
      } 
   });
}

When I run this code, I am getting a log in logcat(Not logged by me.)

05-02 12:02:42.356: E/MediaPlayer(17250): Error (-22,0)

But when I comment these lines

PresetReverb pReverb = new PresetReverb(0,player.getAudioSessionId());
pReverb.setPreset(PresetReverb.PRESET_LARGEROOM);
pReverb.setEnabled(true);
player.attachAuxEffect(eReverb.getId());
player.setAuxEffectSendLevel(1.0f);

MediaPlayer is playing the file successfully. So nothing wrong with my WAV file.

Being somewhat let down, I tried EnvironmentalReverb, instead of PresetReverb,

EnvironmentalReverb eReverb = new EnvironmentalReverb(1, player.getAudioSessionId());
eReverb.setDecayHFRatio((short) 1000);
eReverb.setDecayTime(10000);
eReverb.setDensity((short) 1000);
eReverb.setDiffusion((short) 1000);
eReverb.setReverbLevel((short) -1000);
eReverb.setEnabled(true);
player.attachAuxEffect(eReverb.getId());
player.setAuxEffectSendLevel(1.0f);

there also I got the same error (Error (-22,0)).

So either I am missing something so obvious, or there is some problem with AudioEffect family classes (in documentation or api itself). Can anyone shed some light?

EDIT : I forgot to add, when I debugged the code the error is logged when

 player.start();

is executed. I have removed exception handling parts before posting the code segment above. But I am positive, no exception is caught when I executed.

EDIT AGAIN :

From this link I came to understand error -22 is PVMFErrLicenseRequiredPreviewAvailable

/*
 Error due to the lack of a valid license for the content.  However
 a preview is available.
 */
const PVMFStatus PVMFErrLicenseRequiredPreviewAvailable = (-22);

I googled with PVMFErrLicenseRequiredPreviewAvailable, and I got this document. And on page 87

14.10.5 Preview of DRM Content without a Valid License Available

A variation on the scenario covered in Section 14.10.3 is the case where there is no valid license for full playback of a piece of content, but there it can be previewed. This scenario might be a common way of initially distributing content so that consumers can preview it before deciding to purchase a full license. In this case the Init() method will return with the code PVMFErrLicenseRequiredPreviewAvailable, which indicates that a license is required for full playback but a preview is available. In order to play the preview, the application must remove the current source then add it back with a flag set on the local data source to indicate preview mode.

Now the WAV files I played is generated by me using SpeechToText tool in android SDK itself. I don't know what license holding me from playing this file.


Solution

  • Well, I got it working at last. I reread the documentation, played with the code and suddenly everything worked. These are my findings in case it helps anyone in future.

    In the case of PresetReverb

    In presetReverb documentation

    The PresetReverb is an output mix auxiliary effect and should be created on Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to it and a send level must be specified. Use the effect ID returned by getId() method to designate this particular effect when attaching it to the MediaPlayer or AudioTrack.

    That says

    PresetReverb pReverb = new PresetReverb(0,player.getAudioSessionId()); <== error
    

    is not allowed. You can only use global audio session 0 to construct PresetReverb.

    PresetReverb pReverb  = new PresetReverb(1,0); <== correct
    

    Now we can attach it with MediaPlayer or AudioTrack using

    player.attachAuxEffect(pReverb.getId());
    player.setAuxEffectSendLevel(1.0f);
    

    My full PresetReverb code is

    PresetReverb pReverb    = new PresetReverb(1,0);
    player.attachAuxEffect(pReverb.getId());
    pReverb.setPreset(PresetReverb.PRESET_LARGEROOM);
    pReverb.setEnabled(true);
    player.setAuxEffectSendLevel(1.0f);
    

    Note : If you are looking for good reverb effect or echo, better use EnvironmentalReverb. I am somewhat disappointed with the performance of PresetReverb.

    In the case of EnvironmentalReverb

    From the documentation of EnvironmentalReverb

    The EnvironmentalReverb is an output mix auxiliary effect and should be created on Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to it and a send level must be specified. Use the effect ID returned by getId() method to designate this particular effect when attaching it to the MediaPlayer or AudioTrack.

    Exactly like PresetReverb, but when I wrote

    Log.e("DEBUG","sessionId : " + player.getAudioSessionId()); <== printed "454"
    EnvironmentalReverb  eReverb 
                 = new EnvironmentalReverb(0,player.getAudioSessionId()); //should be error as per documentation
    //player.attachAuxEffect(eReverb.getId());   <== Yes this one was commented
    

    there was no error and I get good reverb effect and echo. So It seems like a bug in documentation. Also when we pass session id of player to the constructor(player.getAudioSessionId()), there seems to be no need to attach player with EnvironmentalReverb instance. Strange..

    For sake of completeness this one also worked just as documentation says.

    EnvironmentalReverb  eReverb 
                 = new EnvironmentalReverb(0,0); 
    player.attachAuxEffect(eReverb.getId());     <== No,not comment this one
    

    For other AudioEffect childrens (Equalizer, BassBoost, Virtualizer)

    These ones were not the part of my question. But for everyone who sees this in the future..

    NOTE: attaching insert effects (equalizer, bass boost, virtualizer) to the global audio output mix by use of session 0 is deprecated.

    see documentation

    Regarding Error (-22,0)

    Well, fair to say it was not an informative error message. I don't know why an Invalid license error comes up when we mess up audio session. Anyway when I corrected the audio session parameter this error vanished. Thats all I know.