com.hkm.mmedic.contents.VisualizerFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.hkm.mmedic.contents.VisualizerFragment.java

Source

/*
 *    Copyright (C) 2014 Haruki Hasegawa
 *
 *    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.hkm.mmedic.contents;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.ActionBar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.Spinner;
import android.widget.ToggleButton;

import com.hkm.mmedic.R;
import com.hkm.mmedic.framework.AppEvent;
import com.hkm.mmedic.framework.AppEventBus;
import com.hkm.mmedic.model.EventDefs;
import com.hkm.mmedic.model.EventDefs.PlayerControlNotifyEvents;
import com.hkm.mmedic.model.EventDefs.VisualizerNotifyEvents;
import com.hkm.mmedic.model.EventDefs.VisualizerReqEvents;
import com.hkm.mmedic.model.VisualizerStateStore;
import com.hkm.mmedic.utils.ActionBarTileBuilder;
import com.hkm.mmedic.visualizer.AudioLevelMeterSurfaceView;
import com.hkm.mmedic.visualizer.BaseAudioVisualizerSurfaceView;
import com.hkm.mmedic.visualizer.FftVisualizerSurfaceView;
import com.hkm.mmedic.visualizer.WaveformVisualizerSurfaceView;
import com.h6ah4i.android.media.audiofx.IVisualizer;
import com.h6ah4i.android.media.audiofx.IVisualizer.MeasurementPeakRms;

public class VisualizerFragment extends ContentsBaseFragment
        implements CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {

    // fields
    private volatile WaveformVisualizerSurfaceView mWaveformVisualizerView;
    private volatile FftVisualizerSurfaceView mFftVisualizerView;
    private volatile AudioLevelMeterSurfaceView mPeakLevelMeterView;
    private volatile AudioLevelMeterSurfaceView mRmsLevelMeterView;
    private ToggleButton mToggleButtonEnableWaveform;
    private ToggleButton mToggleButtonEnableFft;
    private ToggleButton mToggleButtonEnablePeak;
    private ToggleButton mToggleButtonEnableRms;
    private Spinner mSpinnerScalingMode;

    private PeriodicMeasureThread mPeriodicMeasureThread;

    private AppEventReceiver mAppEventReceiver;
    boolean mDuringSetupViews;

    private IVisualizer.OnDataCaptureListener mOnDataCaptureListener = new IVisualizer.OnDataCaptureListener() {

        @Override
        public void onWaveFormDataCapture(IVisualizer visualizer, byte[] waveform, int samplingRate) {
            VisualizerFragment.this.onWaveFormDataCapture(visualizer, waveform, samplingRate);
        }

        @Override
        public void onFftDataCapture(IVisualizer visualizer, byte[] fft, int samplingRate) {
            VisualizerFragment.this.onFftDataCapture(visualizer, fft, samplingRate);
        }
    };

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_visualizer, container, false);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        obtainViewReferences();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mWaveformVisualizerView = null;
        mFftVisualizerView = null;
        mToggleButtonEnableWaveform = null;
        mToggleButtonEnableFft = null;
    }

    @Override
    public void onStart() {
        super.onStart();

        mAppEventReceiver = new AppEventReceiver(this);
        eventBus().register(mAppEventReceiver);

        mPeriodicMeasureThread = new PeriodicMeasureThread(this);
    }

    @Override
    public void onStop() {
        super.onStop();

        eventBus().unregister(mAppEventReceiver);
        mAppEventReceiver = null;
    }

    @Override
    public void onResume() {
        super.onResume();

        setupViews();
        resumeVisualizerViews();
        updateCaptureSettings();
        updateMeasurementsSettings();
    }

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

        pauseVisualizerViews();
        cleanupVisualizer();
    }

    @Override
    protected void onUpdateActionBarAndOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onUpdateActionBarAndOptionsMenu(menu, inflater);

        ActionBar actionBar = getActionBar();

        actionBar.setTitle(ActionBarTileBuilder.makeTitleString(getActivity(), R.string.title_visualizer));
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        ViewCompat.setAlpha(buttonView, buttonView.isChecked() ? 0.5f : 1.0f);

        if (mDuringSetupViews)
            return;

        switch (buttonView.getId()) {
        case R.id.toggle_button_visualizer_enable_waveform:
            postAppEvent(VisualizerReqEvents.SET_WAVEFORM_ENABLED, (isChecked ? 1 : 0), 0);
            break;
        case R.id.toggle_button_visualizer_enable_fft:
            postAppEvent(VisualizerReqEvents.SET_FFT_ENABLED, (isChecked ? 1 : 0), 0);
            break;
        case R.id.toggle_button_visualizer_enable_peak:
            postAppEvent(VisualizerReqEvents.SET_MEASURE_PEAK_ENABLED, (isChecked ? 1 : 0), 0);
            break;
        case R.id.toggle_button_visualizer_enable_rms:
            postAppEvent(VisualizerReqEvents.SET_MEASURE_RMS_ENABLED, (isChecked ? 1 : 0), 0);
            break;
        }
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        switch (parent.getId()) {
        case R.id.spinner_visualizer_scaling_mode:
            postAppEvent(VisualizerReqEvents.SET_SCALING_MODE, position, 0);
            break;
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }

    /* package */void onReceiveAppEvent(AppEvent event) {
        switch (event.category) {
        case EventDefs.Category.NOTIFY_PLAYER_CONTROL:
            onNotifyPlayerControlEvent(event);
            break;
        case EventDefs.Category.NOTIFY_VISUALIZER:
            onNotifyVisualizerEvent(event);
            break;
        }
    }

    private void onNotifyPlayerControlEvent(AppEvent event) {
        switch (event.event) {
        case PlayerControlNotifyEvents.PLAYER_STATE_CHANGED:
            onPlayerStateChanged(event.arg1);
            break;
        }
    }

    private void onNotifyVisualizerEvent(AppEvent event) {
        switch (event.event) {
        case VisualizerNotifyEvents.WAVEFORM_ENABLED_STATE_UPDATED:
        case VisualizerNotifyEvents.FFT_ENABLED_STATE_UPDATED:
        case VisualizerNotifyEvents.SCALING_MODE_UPDATED:
            updateCaptureSettings();
            break;
        case VisualizerNotifyEvents.MEASURE_PEAK_ENABLED_STATE_UPDATED:
        case VisualizerNotifyEvents.MEASURE_RMS_ENABLED_STATE_UPDATED:
            updateMeasurementsSettings();
            break;
        }
    }

    private void onPlayerStateChanged(int state) {
        if (state == PlayerControlNotifyEvents.STATE_IDLE) {
            updateCaptureSettings();
            updateMeasurementsSettings();
        }
    }

    private void obtainViewReferences() {
        mWaveformVisualizerView = (WaveformVisualizerSurfaceView) findViewById(
                R.id.surfaceview_visualizer_waveform);
        mFftVisualizerView = (FftVisualizerSurfaceView) findViewById(R.id.surfaceview_visualizer_fft);
        mPeakLevelMeterView = (AudioLevelMeterSurfaceView) findViewById(R.id.surfaceview_visualizer_peak);
        mRmsLevelMeterView = (AudioLevelMeterSurfaceView) findViewById(R.id.surfaceview_visualizer_rms);

        mToggleButtonEnableWaveform = findToggleButtonByIdAndSetListener(
                R.id.toggle_button_visualizer_enable_waveform);
        mToggleButtonEnableFft = findToggleButtonByIdAndSetListener(R.id.toggle_button_visualizer_enable_fft);
        mToggleButtonEnablePeak = findToggleButtonByIdAndSetListener(R.id.toggle_button_visualizer_enable_peak);
        mToggleButtonEnableRms = findToggleButtonByIdAndSetListener(R.id.toggle_button_visualizer_enable_rms);

        mSpinnerScalingMode = findSpinnerByIdAndSetListener(R.id.spinner_visualizer_scaling_mode);
    }

    private void setupViews() {
        VisualizerStateStore state = getStateStore();
        Context context = getActivity();

        mDuringSetupViews = true;

        mToggleButtonEnableWaveform.setChecked(state.isCaptureWaveformEnabled());
        mToggleButtonEnableFft.setChecked(state.isCaptureFftEnabled());
        mToggleButtonEnablePeak.setChecked(state.isMeasurementPeakEnabled());
        mToggleButtonEnableRms.setChecked(state.isMeasurementRmsEnabled());

        {
            final ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context,
                    R.array.visualizer_scaling_mode_names, android.R.layout.simple_spinner_item);
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            mSpinnerScalingMode.setAdapter(adapter);
        }

        mSpinnerScalingMode.setSelection(state.getScalingMode());

        mDuringSetupViews = false;
    }

    private static void safeResumeVizualizerView(BaseAudioVisualizerSurfaceView v) {
        if (v != null) {
            v.onResume();
        }
    }

    private static void safePauseVizualizerView(BaseAudioVisualizerSurfaceView v) {
        if (v != null) {
            v.onResume();
        }
    }

    private void resumeVisualizerViews() {
        safeResumeVizualizerView(mWaveformVisualizerView);
        safeResumeVizualizerView(mFftVisualizerView);
    }

    private void pauseVisualizerViews() {
        safePauseVizualizerView(mWaveformVisualizerView);
        safePauseVizualizerView(mFftVisualizerView);
    }

    private void updateCaptureSettings() {
        final IVisualizer visualizer = getVisualizer();
        final VisualizerStateStore state = getStateStore();

        final boolean captureWaveform = state.isCaptureWaveformEnabled();
        final boolean captureFft = state.isCaptureFftEnabled();
        final int scalingMode = state.getScalingMode();

        if (visualizer != null) {
            // stop visualizer
            stopVisualizer(visualizer);

            if (captureFft || captureWaveform) {
                // capturing enabled

                // use maximum rate & size
                int rate = visualizer.getMaxCaptureRate();
                int size = visualizer.getCaptureSizeRange()[1];

                visualizer.setCaptureSize(size);
                visualizer.setScalingMode(scalingMode);
                visualizer.setDataCaptureListener(mOnDataCaptureListener, rate, captureWaveform, captureFft);
            } else {
                // capturing disabled
                visualizer.setDataCaptureListener(null, 0, false, false);
            }

            // start visualizer
            startVisualizer(visualizer, state);
        }
    }

    private void updateMeasurementsSettings() {
        final IVisualizer visualizer = getVisualizer();
        final VisualizerStateStore state = getStateStore();

        final boolean measurePeak = state.isMeasurementPeakEnabled();
        final boolean measureRms = state.isMeasurementRmsEnabled();

        if (visualizer != null) {
            // stop visualizer
            stopVisualizer(visualizer);

            if (measurePeak || measureRms) {
                // measurements enabled
                visualizer.setMeasurementMode(IVisualizer.MEASUREMENT_MODE_PEAK_RMS);
            } else {
                // measurements disabled
                visualizer.setMeasurementMode(IVisualizer.MEASUREMENT_MODE_NONE);
            }

            // start visualizer
            startVisualizer(visualizer, state);
        }
    }

    private VisualizerStateStore getStateStore() {
        return getAppController().getVisualizerStateStore();
    }

    private IVisualizer getVisualizer() {
        return getAppController().getVisualizer();
    }

    private void cleanupVisualizer() {
        IVisualizer visualizer = getVisualizer();

        if (mPeriodicMeasureThread != null) {
            mPeriodicMeasureThread.stop();
        }

        if (visualizer != null) {
            visualizer.setEnabled(false);

            visualizer.setDataCaptureListener(null, 0, false, false);
        }
    }

    private void startVisualizer(IVisualizer visualizer, VisualizerStateStore state) {
        boolean waveform = state.isCaptureWaveformEnabled();
        boolean fft = state.isCaptureFftEnabled();
        boolean peak = state.isMeasurementPeakEnabled();
        boolean rms = state.isMeasurementRmsEnabled();

        if (visualizer != null) {
            visualizer.setEnabled(waveform | fft | peak | rms);

            // start measuring
            if (peak || rms) {
                mPeriodicMeasureThread.start(visualizer, peak, rms);
            }
        }
    }

    private void stopVisualizer(IVisualizer visualizer) {
        // stop measuring
        mPeriodicMeasureThread.stop();

        if (visualizer != null) {
            visualizer.setEnabled(false);
        }
    }

    /* package */void onWaveFormDataCapture(IVisualizer visualizer, byte[] waveform, int samplingRate) {
        WaveformVisualizerSurfaceView view = mWaveformVisualizerView;

        if (view != null) {
            view.updateAudioData(waveform, samplingRate);
        }
    }

    /* package */void onFftDataCapture(IVisualizer visualizer, byte[] fft, int samplingRate) {
        FftVisualizerSurfaceView view = mFftVisualizerView;

        if (view != null) {
            view.updateAudioData(fft, samplingRate);
        }
    }

    /* package */void onMeasurementPeaksUpdated(int level) {
        AudioLevelMeterSurfaceView view = mPeakLevelMeterView;

        if (view != null) {
            view.updateAudioLevel(level);
        }
    }

    /* package */void onMeasurementRmsUpdated(int level) {
        AudioLevelMeterSurfaceView view = mRmsLevelMeterView;

        if (view != null) {
            view.updateAudioLevel(level);
        }
    }

    private void postAppEvent(int event, int arg1, float arg2) {
        eventBus().post(new AppEvent(EventDefs.Category.VISUALIZER, event, arg1, arg2));
    }

    private static class AppEventReceiver extends AppEventBus.Receiver<VisualizerFragment> {
        private static final int[] FILTER = new int[] { EventDefs.Category.NOTIFY_PLAYER_CONTROL,
                EventDefs.Category.NOTIFY_VISUALIZER, };

        public AppEventReceiver(VisualizerFragment holder) {
            super(holder, FILTER);
        }

        @Override
        protected void onReceiveAppEvent(VisualizerFragment holder, AppEvent event) {
            holder.onReceiveAppEvent(event);
        }
    }

    private static class PeriodicMeasureThread {
        VisualizerFragment mFragment;
        Thread mThread;
        volatile boolean mStopRequested;

        public PeriodicMeasureThread(VisualizerFragment fragment) {
            mFragment = fragment;
        }

        public void captureThreadFunc(IVisualizer visualizer, boolean peak, boolean rms) {
            MeasurementPeakRms measurement = new MeasurementPeakRms();
            while (!mStopRequested) {
                try {
                    int result;

                    try {
                        result = visualizer.getMeasurementPeakRms(measurement);
                    } catch (IllegalStateException e) {
                        result = IVisualizer.ERROR_INVALID_OPERATION;
                    }

                    if (result == IVisualizer.SUCCESS) {

                        if (peak) {
                            mFragment.onMeasurementPeaksUpdated(measurement.mPeak);
                        }

                        if (rms) {
                            mFragment.onMeasurementRmsUpdated(measurement.mRms);
                        }
                    }

                    Thread.sleep(50);
                } catch (InterruptedException e) {
                }
            }
        }

        public void start(final IVisualizer visualizer, final boolean peak, final boolean rms) {
            stop();

            mStopRequested = false;
            mThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    captureThreadFunc(visualizer, peak, rms);
                }
            });
            mThread.start();
        }

        public void stop() {
            if (mThread != null) {
                mStopRequested = true;
                mThread.interrupt();

                while (true) {
                    try {
                        mThread.join();
                        break;
                    } catch (InterruptedException e) {
                    }
                }
                mThread = null;
            }

        }
    }
}