#ABC2011w Gingerbread API紹介セッション SIPとAudioFx - ReDo

2011年1月10日

#ABC2011w Gingerbread API紹介セッション SIPとAudioFx

※本エントリはABC2011Winter開催後に更新しました。

Android Bazaar and Conference 2011 Winter
http://www.android-group.jp/abc2011w/conference/index.html

の「Gingerbread」(API紹介セッション)でイカの2つほどを喋ってきました。
・SIP-based VoIP
・Mixable audio effects

20110109_yasuda.jpg
おい、人多すぎだろう。盛況で皆様ご足労いただき大変ありがとうございます、の図

SIP-based VoIP 補足説明。

  • APIとしてはSDPもいじれませんしcodecも固定です。「SIP API」ではなくて「SIPベースのVoIPクライアント API」です。
  • サーバについては、自分にAsteriskを1日でセットアップする様なスキルがないため、Windowsで動作しそうなfreeのものを数個試して3CXに落ち着きました。良いソフトかは分かりませんがインストールは楽でした。
  • 「SipDemoの音質が悪い」についてはSipDroid同士と比較して、の意です。LAN内でこれは微妙じゃないかな、と感じました。
  • 発信側がNGの件ですが、「SipDemoはいじらないと動かないよ」ということではなく、「SIPは環境によって動く動かないが激しいけど、Proxyサーバのアドレスとドメイン(realm)が違ってもAndroidのSIP APIは大丈夫だよ」ということです。所属ドメインと通話先ドメインが違う"クロスドメイン"については、未対応だとExceptionが発生します。
  • SDPはWireSharkの画面キャプチャをそのまま張ってますが固定です。

Mixable audio effects 補足説明。

  • Reverbの動作がよくない件は原因が特定できておりません。BassBoostやVirtualizerがきちんと動いたことから考えるときっとくだらないパラメータミスとかだと思っているのですが...。
  • 音楽プレーヤ以外の使い道は5分考えても出てきませんでした。「イコライザ+FFTでの耳コピ支援」とか「Vocal消してカラオケ」とか音楽プレーヤベースならなんとか既存のブツがいくつかAndroidでも実現できそうに思えますが...。
  • setEnabled(true)忘れるなよ!絶対だぞ!

続きに「改」の主要Javaコードを1つずつ置いてます。あんまり見られたコードではありませんが、ApiDemos, SipDemoとして公開されてるコードと比較の上、何かの(?)参考にでもしてください。

SipDemo内、WalkieTalkieActivity.java・改

/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.sip;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.*;
import android.net.sip.*;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.ToggleButton;

import java.text.ParseException;

/**
 * Handles all calling, receiving calls, and UI interaction in the WalkieTalkie
 * app.
 */
public class WalkieTalkieActivity extends Activity implements View.OnTouchListener {

    public String sipAddress = null;

    public SipManager manager = null;
    public SipProfile me = null;
    public SipAudioCall call = null;
    public IncomingCallReceiver callReceiver;

    private static final int CALL_ADDRESS = 1;
    private static final int SET_AUTH_INFO = 2;
    private static final int UPDATE_SETTINGS_DIALOG = 3;
    private static final int HANG_UP = 4;
    private static final int SEND_DTMF = 5;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.walkietalkie);

        ToggleButton pushToTalkButton = (ToggleButton) findViewById(R.id.pushToTalk);
        pushToTalkButton.setOnTouchListener(this);

        // Set up the intent filter. This will be used to fire an
        // IncomingCallReceiver when someone calls the SIP address used by this
        // application.
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.SipDemo.INCOMING_CALL");
        callReceiver = new IncomingCallReceiver();
        this.registerReceiver(callReceiver, filter);

        // "Push to talk" can be a serious pain when the screen keeps turning
        // off.
        // Let's prevent that.
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        initializeManager();
    }

    @Override
    public void onStart() {
        super.onStart();
        // When we get back from the preference setting Activity, assume
        // settings have changed, and re-login with new auth info.
        initializeManager();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (call != null) {
            call.close();
        }

        closeLocalProfile();

        if (callReceiver != null) {
            this.unregisterReceiver(callReceiver);
        }
    }

    public void initializeManager() {
        if (manager == null) {
            manager = SipManager.newInstance(this);
        }

        initializeLocalProfile();
    }

    /**
     * Logs you into your SIP provider, registering this device as the location
     * to send SIP calls to for your SIP address.
     */
    public void initializeLocalProfile() {
        if (manager == null) {
            return;
        }

        if (me != null) {
            closeLocalProfile();
        }

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
        String username = prefs.getString("namePref", "");
        String domain = prefs.getString("domainPref", "");
        String proxy = prefs.getString("proxyPref", "");
        String password = prefs.getString("passPref", "");

        if (username.length() == 0 || domain.length() == 0 || password.length() == 0) {
            showDialog(UPDATE_SETTINGS_DIALOG);
            return;
        }

        try {
            SipProfile.Builder builder = new SipProfile.Builder(username, domain);
            if (!"".equals(proxy)) {
                builder.setOutboundProxy(proxy);
            }
            builder.setPassword(password);
            me = builder.build();

            Intent i = new Intent();
            i.setAction("android.SipDemo.INCOMING_CALL");
            PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
            manager.open(me, pi, null);

            // This listener must be added AFTER manager.open is called,
            // Otherwise the methods aren't guaranteed to fire.

            manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
                public void onRegistering(String localProfileUri) {
                    updateStatus("Registering with SIP Server...");
                }

                public void onRegistrationDone(String localProfileUri, long expiryTime) {
                    updateStatus("Ready");
                }

                public void onRegistrationFailed(String localProfileUri, int errorCode, String errorMessage) {
                    updateStatus("Registration failed.  Please check settings.");
                }
            });
        } catch (ParseException pe) {
            updateStatus("Connection Error.");
        } catch (SipException se) {
            updateStatus("Connection error.");
        }
    }

    /**
     * Closes out your local profile, freeing associated objects into memory and
     * unregistering your device from the server.
     */
    public void closeLocalProfile() {
        if (manager == null) {
            return;
        }
        try {
            if (me != null) {
                manager.close(me.getUriString());
            }
        } catch (Exception ee) {
            Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee);
        }
    }

    /**
     * Make an outgoing call.
     */
    public void initiateCall() {

        updateStatus(sipAddress);

        try {
            SipAudioCall.Listener listener = new SipAudioCall.Listener() {
                // Much of the client's interaction with the SIP Stack will
                // happen via listeners. Even making an outgoing call, don't
                // forget to set up a listener to set things up once the call is
                // established.
                @Override
                public void onCallEstablished(SipAudioCall call) {
                    call.startAudio();
                    call.setSpeakerMode(true);
                    if (call.isMuted()) {
                        call.toggleMute();
                    }
                    updateStatus(call);
                }

                @Override
                public void onCallEnded(SipAudioCall call) {
                    updateStatus("Ready.");
                }

                @Override
                public void onError(SipAudioCall call, int errorCode, String errorMessage) {
                    updateStatus("Error: " + Integer.toString(errorCode) + "\n" + errorMessage);
                    super.onError(call, errorCode, errorMessage);
                }
            };

            call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);

        } catch (Exception e) {
            Log.i("WalkieTalkieActivity/InitiateCall", "Error when trying to close manager.", e);
            if (me != null) {
                try {
                    manager.close(me.getUriString());
                } catch (Exception ee) {
                    Log.i("WalkieTalkieActivity/InitiateCall", "Error when trying to close manager.", ee);
                    ee.printStackTrace();
                }
            }
            if (call != null) {
                call.close();
            }
        }
    }

    /**
     * Updates the status box at the top of the UI with a messege of your
     * choice.
     * @param status The String to display in the status box.
     */
    public void updateStatus(final String status) {
        // Be a good citizen. Make sure UI changes fire on the UI thread.
        this.runOnUiThread(new Runnable() {
            public void run() {
                TextView labelView = (TextView) findViewById(R.id.sipLabel);
                labelView.setText(status);
            }
        });
    }

    /**
     * Updates the status box with the SIP address of the current call.
     * @param call The current, active call.
     */
    public void updateStatus(SipAudioCall call) {
        String useName = call.getPeerProfile().getDisplayName();
        if (useName == null) {
            useName = call.getPeerProfile().getUserName();
        }
        updateStatus(useName + "@" + call.getPeerProfile().getSipDomain());
    }

    /**
     * Updates whether or not the user's voice is muted, depending on whether
     * the button is pressed.
     * @param v The View where the touch event is being fired.
     * @param event The motion to act on.
     * @return boolean Returns false to indicate that the parent view should
     * handle the touch event as it normally would.
     */
    public boolean onTouch(View v, MotionEvent event) {
        if (call == null) {
            return false;
        } else if (event.getAction() == MotionEvent.ACTION_DOWN && call != null && call.isMuted()) {
            call.toggleMute();
        } else if (event.getAction() == MotionEvent.ACTION_UP && !call.isMuted()) {
            call.toggleMute();
        }
        return false;
    }

    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, CALL_ADDRESS, 0, "Call someone");
        menu.add(0, SET_AUTH_INFO, 0, "Edit your SIP Info.");
        menu.add(0, HANG_UP, 0, "End Current Call.");
        menu.add(0, SEND_DTMF, 0, "Send DTMF");

        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case CALL_ADDRESS:
            showDialog(CALL_ADDRESS);
            break;
        case SET_AUTH_INFO:
            updatePreferences();
            break;
        case HANG_UP:
            if (call != null) {
                try {
                    call.endCall();
                } catch (SipException se) {
                    Log.d("WalkieTalkieActivity/onOptionsItemSelected", "Error ending call.", se);
                }
                call.close();
            }
            break;
        case SEND_DTMF:
            if (call != null) {
                call.sendDtmf(1);
                call.sendDtmf(2);
                call.sendDtmf(3);
            }
            break;
        }
        return true;
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case CALL_ADDRESS:

            LayoutInflater factory = LayoutInflater.from(this);
            final View textBoxView = factory.inflate(R.layout.call_address_dialog, null);
            return new AlertDialog.Builder(this).setTitle("Call Someone.").setView(textBoxView).setPositiveButton(
                android.R.string.ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        EditText textField = (EditText) (textBoxView.findViewById(R.id.calladdress_edit));
                        sipAddress = textField.getText().toString();
                        initiateCall();

                    }
                }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    // Noop.
                }
            }).create();

        case UPDATE_SETTINGS_DIALOG:
            return new AlertDialog.Builder(this).setMessage("Please update your SIP Account Settings.")
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        updatePreferences();
                    }
                }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        // Noop.
                    }
                }).create();
        }
        return null;
    }

    public void updatePreferences() {
        Intent settingsActivity = new Intent(getBaseContext(), SipSettings.class);
        startActivity(settingsActivity);
    }
}

ApiDemos内、AudioFxDemo.java・改

/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package youten.redo.audiofxdemo;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.audiofx.BassBoost;
import android.media.audiofx.EnvironmentalReverb;
import android.media.audiofx.Equalizer;
import android.media.audiofx.PresetReverb;
import android.media.audiofx.Virtualizer;
import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

public class AudioFxDemo extends Activity {
    private static final String TAG = "AudioFxDemo";
    private static final float VISUALIZER_HEIGHT_DIP = 50f;

    private static final int MENU_TOGGLE_BASS_BOOST = 1;
    private static final int MENU_SELECT_PRESET_REVERB = 2;
    private static final CharSequence[] presetReverbs =
        { "LARGE HALL", "LARGE ROOM", "MEDIUM HALL", "MEDIUM ROOM", "NONE", "PLATE", "SMALL ROOM" };
    private static final int MENU_TOGGLE_ENVIRONMENTAL_REVERB = 3;
    private static final int MENU_TOGGLE_VIRTUALIZER = 4;

    private MediaPlayer mMediaPlayer;
    private Visualizer mVisualizer;
    private Equalizer mEqualizer;
    private BassBoost mBassBoost;
    private PresetReverb mPresetReverb;
    // private EnvironmentalReverb mEnvironmentalReverb;
    private Virtualizer mVirtualizer;

    private LinearLayout mLinearLayout;
    private VisualizerView mVisualizerView;
    private TextView mStatusTextView;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        setVolumeControlStream(AudioManager.STREAM_MUSIC);

        mStatusTextView = new TextView(this);

        mLinearLayout = new LinearLayout(this);
        mLinearLayout.setOrientation(LinearLayout.VERTICAL);
        mLinearLayout.addView(mStatusTextView);

        setContentView(mLinearLayout);

        // Create the MediaPlayer
        mMediaPlayer = MediaPlayer.create(this, R.raw.test_cbr);
        Log.d(TAG, "MediaPlayer audio session ID: " + mMediaPlayer.getAudioSessionId());

        setupVisualizerFxAndUI();
        setupEqualizerFxAndUI();
        setupBassBoost();
        setupPresetReverb();
        // setupEnvironmentalReverb();
        setupmVirtualizer();

        // Make sure the visualizer is enabled only when you actually want to
        // receive data, and
        // when it makes sense to receive data.
        mVisualizer.setEnabled(true);

        // When the stream ends, we don't need to collect any more data. We
        // don't do this in
        // setupVisualizerFxAndUI because we likely want to have more,
        // non-Visualizer related code
        // in this callback.
        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            public void onCompletion(MediaPlayer mediaPlayer) {
                mVisualizer.setEnabled(false);
            }
        });

        // loop
        mMediaPlayer.setLooping(true);
        mMediaPlayer.start();
        mStatusTextView.setText("Playing audio...");
    }

    private void setupEqualizerFxAndUI() {
        // Create the Equalizer object (an AudioEffect subclass) and attach it
        // to our media player,
        // with a default priority (0).
        mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());
        mEqualizer.setEnabled(true);

        TextView eqTextView = new TextView(this);
        eqTextView.setText("Equalizer:");
        mLinearLayout.addView(eqTextView);

        short bands = mEqualizer.getNumberOfBands();

        final short minEQLevel = mEqualizer.getBandLevelRange()[0];
        final short maxEQLevel = mEqualizer.getBandLevelRange()[1];

        for (short i = 0; i < bands; i++) {
            final short band = i;

            TextView freqTextView = new TextView(this);
            freqTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
            freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);
            freqTextView.setText((mEqualizer.getCenterFreq(band) / 1000) + " Hz");
            mLinearLayout.addView(freqTextView);

            LinearLayout row = new LinearLayout(this);
            row.setOrientation(LinearLayout.HORIZONTAL);

            TextView minDbTextView = new TextView(this);
            minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
            minDbTextView.setText((minEQLevel / 100) + " dB");

            TextView maxDbTextView = new TextView(this);
            maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));
            maxDbTextView.setText((maxEQLevel / 100) + " dB");

            LinearLayout.LayoutParams layoutParams =
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            layoutParams.weight = 1;
            SeekBar bar = new SeekBar(this);
            bar.setLayoutParams(layoutParams);
            bar.setMax(maxEQLevel - minEQLevel);
            bar.setProgress(mEqualizer.getBandLevel(band));

            bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    mEqualizer.setBandLevel(band, (short) (progress + minEQLevel));
                }

                public void onStartTrackingTouch(SeekBar seekBar) {
                }

                public void onStopTrackingTouch(SeekBar seekBar) {
                }
            });

            row.addView(minDbTextView);
            row.addView(bar);
            row.addView(maxDbTextView);

            mLinearLayout.addView(row);
        }
    }

    private void setupVisualizerFxAndUI() {
        // Create a VisualizerView (defined below), which will render the
        // simplified audio
        // wave form to a Canvas.
        mVisualizerView = new VisualizerView(this);
        mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
            (int) (VISUALIZER_HEIGHT_DIP * getResources().getDisplayMetrics().density)));
        mLinearLayout.addView(mVisualizerView);

        // Create the Visualizer object and attach it to our media player.
        mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId());
        mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
        mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
            public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
                mVisualizerView.updateVisualizer(bytes);
            }

            public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
            }
        }, Visualizer.getMaxCaptureRate() / 2, true, false);
    }

    private void setupBassBoost() {
        // Create the BassBoost object and attach it to our media player.
        mBassBoost = null;
        try {
            mBassBoost = new BassBoost(0, mMediaPlayer.getAudioSessionId());
            mBassBoost.setEnabled(true);
            short roundedStrength = mBassBoost.getRoundedStrength();
            Log.d(TAG, "BaseBoost: supported=" + mBassBoost.getStrengthSupported() + " roundedStrength = "
                + Short.toString(roundedStrength));
        } catch (UnsupportedOperationException e) {
            Toast.makeText(this, "BaseBoost: " + e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    private void setupPresetReverb() {
        // Create the PresetReverb object and attach it to our media player.
        mPresetReverb = null;
        try {
            mPresetReverb = new PresetReverb(0, mMediaPlayer.getAudioSessionId());
            mPresetReverb.setEnabled(true);
        } catch (UnsupportedOperationException e) {
            Toast.makeText(this, "PresetReverb: " + e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    // private void setupEnvironmentalReverb() {
    // // Create the EnvironmentalReverb object and attach it to our media
    // // player.
    // mEnvironmentalReverb = null;
    // try {
    // mEnvironmentalReverb = new EnvironmentalReverb(0,
    // mMediaPlayer.getAudioSessionId());
    // mEnvironmentalReverb.setEnabled(true);
    // } catch (UnsupportedOperationException e) {
    // Toast.makeText(this, "EnvironmentalReverb: " + e.getMessage(),
    // Toast.LENGTH_LONG).show();
    // }
    // }

    private void setupmVirtualizer() {
        // Create the Virtualizer object and attach it to our media player.
        mVirtualizer = null;
        try {
            mVirtualizer = new Virtualizer(0, mMediaPlayer.getAudioSessionId());
            mVirtualizer.setEnabled(true);
        } catch (UnsupportedOperationException e) {
            Toast.makeText(this, "Virtualizer: " + e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (isFinishing() && mMediaPlayer != null) {
            mVisualizer.release();
            mEqualizer.release();
            if (mBassBoost != null) {
                mBassBoost.release();
            }
            if (mPresetReverb != null) {
                mPresetReverb.release();
            }
            // if (mEnvironmentalReverb != null) {
            // mEnvironmentalReverb.release();
            // }
            if(mVirtualizer != null) {
                mVirtualizer.release();
            }
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    @Override
    protected void onResume() {
        // TODO 縦横Orientation変化とかで落ちるのでResumeをちゃんとつくる。
        super.onResume();
    }

    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_TOGGLE_BASS_BOOST, 0, "Bass on/off");
        menu.add(0, MENU_SELECT_PRESET_REVERB, 0, "Reverb");
        // menu.add(0, MENU_TOGGLE_ENVIRONMENTAL_REVERB, 0,
        // "EnvironmentalReverb on/off");
        menu.add(0, MENU_TOGGLE_VIRTUALIZER, 0, "Virtualizer");
        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_TOGGLE_BASS_BOOST:
            short strength = mBassBoost.getRoundedStrength();
            Log.d(TAG, "BassBoost : strength=" + strength);
            if (strength > 0) {
                mBassBoost.setStrength((short) 0); // off
            } else {
                mBassBoost.setStrength((short) 1000); // on
            }
            // Toast
            Toast.makeText(AudioFxDemo.this, mBassBoost.getProperties().toString(), Toast.LENGTH_SHORT).show();
            break;
        case MENU_SELECT_PRESET_REVERB:
            showDialog(MENU_SELECT_PRESET_REVERB);
            break;
        // case MENU_TOGGLE_ENVIRONMENTAL_REVERB:
        // if(mEnvironmentalReverb.getEnabled()){
        // mEnvironmentalReverb.setEnabled(false); // off
        // }else {
        // mEnvironmentalReverb.setReverbLevel((short)-1000);
        // mEnvironmentalReverb.setReverbDelay(50);
        // mEnvironmentalReverb.setRoomLevel((short)-1000);
        // mEnvironmentalReverb.setEnabled(true); // on
        // }
        // break;
        case MENU_TOGGLE_VIRTUALIZER:
            short virtualizerStrength = mVirtualizer.getRoundedStrength();
            Log.d(TAG, "Virtualizer : strength=" + virtualizerStrength);
            if (virtualizerStrength > 0) {
                mVirtualizer.setStrength((short) 0); // off
            } else {
                mVirtualizer.setStrength((short) 1000); // on
            }
            // Toast
            Toast.makeText(AudioFxDemo.this, mVirtualizer.getProperties().toString(), Toast.LENGTH_SHORT).show();
            break;
        default:
        }
        return true;
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        Dialog dialog = null;
        switch (id) {
        case MENU_SELECT_PRESET_REVERB:
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Select PresetReverb");
            builder.setItems(presetReverbs, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    switch (which) {
                    case 0: // LARGE HALL
                         mPresetReverb.setPreset(PresetReverb.PRESET_LARGEHALL);
                        break;
                    case 1: // LARGE ROOM
                         mPresetReverb.setPreset(PresetReverb.PRESET_LARGEROOM);
                        break;
                    case 2: // MEDIUM HALL
                         mPresetReverb.setPreset(PresetReverb.PRESET_MEDIUMHALL);
                        break;
                    case 3: // MEDIUM ROOM
                         mPresetReverb.setPreset(PresetReverb.PRESET_MEDIUMROOM);
                        break;
                    case 4: // NONE
                         mPresetReverb.setPreset(PresetReverb.PRESET_NONE);
                        break;
                    case 5: // PLATE
                         mPresetReverb.setPreset(PresetReverb.PRESET_PLATE);
                        break;
                    case 6: // SMALL ROOM
                         mPresetReverb.setPreset(PresetReverb.PRESET_SMALLROOM);
                        break;
                    default:
                    }

                    // Toast
                    Toast.makeText(AudioFxDemo.this, mPresetReverb.getProperties().toString(), Toast.LENGTH_SHORT).show();
                }
            });
            dialog = builder.create();
            break;
        default:
            dialog = null;
        }
        return dialog;
    }
}

/**
 * A simple class that draws waveform data received from a
 * {@link Visualizer.OnDataCaptureListener#onWaveFormDataCapture }
 */
class VisualizerView extends View {
    private byte[] mBytes;
    private float[] mPoints;
    private Rect mRect = new Rect();

    private Paint mForePaint = new Paint();

    public VisualizerView(Context context) {
        super(context);
        init();
    }

    private void init() {
        mBytes = null;

        mForePaint.setStrokeWidth(4f);
        mForePaint.setAntiAlias(true);
        mForePaint.setColor(Color.rgb(164, 198, 57));
    }

    public void updateVisualizer(byte[] bytes) {
        mBytes = bytes;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mBytes == null) {
            return;
        }

        if (mPoints == null || mPoints.length < mBytes.length * 4) {
            mPoints = new float[mBytes.length * 4];
        }

        mRect.set(0, 0, getWidth(), getHeight());

        for (int i = 0; i < mBytes.length - 1; i++) {
            mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);
            mPoints[i * 4 + 1] = mRect.height() / 2 + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2) / 128;
            mPoints[i * 4 + 2] = mRect.width() * (i + 1) / (mBytes.length - 1);
            mPoints[i * 4 + 3] = mRect.height() / 2 + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2) / 128;
        }

        canvas.drawLines(mPoints, mForePaint);
    }
}

コメントする