text:
import boto
import datetime
from pyspark import SparkConf
from pyspark.sql import SparkSession
from pyspark.sql.functions import explode, lit
import smtplib

def date_from_epoch(epoch, offset):
    """Return YYYY-MM-DD from timestamp (epoch) and offset"""
    if epoch is None or offset is None:
        return None
    dt = datetime.datetime.fromtimestamp(epoch) + \
        datetime.timedelta(hours=offset/100)
    return datetime.datetime.strftime(dt, '%Y-%m-%d')

def time_from_epoch(epoch, offset):
    """Return HH:MM from timestamp (epoch) and offset"""
    if epoch is None or offset is None:
        return None
    dt = datetime.datetime.fromtimestamp(epoch) + \
        datetime.timedelta(hours=offset/100)
    return datetime.datetime.strftime(dt, '%H:%M')

def myzip(*cols):
    """Zip columns function used to zip forecast lists together"""
    return zip(*cols)

def myreshape(a, i=4):
    """Flatten ['0', '1', '2', '3', ['4a', '4b', '4c']] to
    onel list ['0', '1', '2', '3','4a', '4b', '4c']"""
    start = list(a[:i])
    end = a[i]
    return [start + list(x) for x in end]

def path_s3():
    """Obtain all raw data filepaths from S3"""
    keys = bucket.get_all_keys()
    pathS3 = []
    for k in keys:
        if ('weather-1' in k.name) or ('weather3-4' in k.name):
            pathS3.append((k.name, ))
    pathS3DF = spark.sparkContext.parallelize(pathS3).toDF() \
        .selectExpr('_1 as path')
    return pathS3DF

def path_to_dfs(path):
    """Given a filepath, extract and normalize data into DFs"""
    path_text = path[0]['path']

    # Source of file where the data came from
    source = int('2017'+path_text[path_text.find('/')+1:path_text.find('/w')]
                 .replace('/', ''))

    # Raw Data / Clean / Cache
    pathRawDF = spark.read.json('s3a://myweatherproject/' + path_text)
    pathCleanDF = pathRawDF.filter(
        pathRawDF['current_observation.display_location.city'] != '')
    pathCleanDF.cache()

    # Table = city, Key = city
    # Information about the city
    pathCityDF = pathCleanDF.selectExpr(
        'current_observation.display_location.city',
        'current_observation.display_location.state',
        'current_observation.display_location.country',
        'current_observation.display_location.zip',
        'almanac.airport_code',
        'current_observation.display_location.elevation',
        'current_observation.display_location.latitude',
        'current_observation.display_location.longitude',
        'current_observation.local_tz_offset'
        ).distinct().withColumn('source', lit(source))

    # Table = nearby, Key = city, nearby
    # For each city, contains all the nearby locations.
    pathNearby1DF = pathCleanDF.selectExpr(
        'current_observation.display_location.city',
        'explode(location.nearby_weather_stations.pws.station.neighborhood) \
            as nearby') \
        .unionAll(pathCleanDF.selectExpr(
            'current_observation.display_location.city',
            'explode(location.nearby_weather_stations.airport.station.city) \
            as nearby'
            )).distinct()
    pathNearbyDF = pathNearby1DF.filter((pathNearby1DF['nearby'] != '')) \
        .withColumn('source', lit(source))

    # Table = cityDay, Key = ciy, date
    # Stats for the given key: historcial temps, sun/moon schedule.
    pathCityDayDF = pathCleanDF.selectExpr(
        'current_observation.display_location.city',
        'date_from_epoch(cast(current_observation.local_epoch as int),\
             cast(current_observation.local_tz_offset as int)) as date',
        'almanac.temp_high.normal.F as normal_high',
        'almanac.temp_high.record.F as record_high',
        'almanac.temp_high.recordyear as record_high_year',
        'almanac.temp_low.normal.F as normal_low',
        'almanac.temp_low.record.F as record_low',
        'almanac.temp_low.recordyear as record_low_year',
        'sun_phase.sunrise.hour as sunrise_hour',
        'sun_phase.sunrise.minute as sunrise_minute',
        'sun_phase.sunset.hour as sunset_hour',
        'sun_phase.sunset.minute as sunset_minute',
        'moon_phase.moonrise.hour as moonrise_hour',
        'moon_phase.moonrise.minute as moonrise_minute',
        'moon_phase.moonset.hour as moonset_hour',
        'moon_phase.moonset.minute as moonset_minute'
        ).distinct().withColumn('source', lit(source))

    # Table = weather, Key = city, date, time
    # Current weather data for the given key.
    pathWeatherDF = pathCleanDF.selectExpr(
        'current_observation.display_location.city',
        'date_from_epoch(cast(current_observation.local_epoch as int),\
             cast(current_observation.local_tz_offset as int)) as date',
        'time_from_epoch(cast(current_observation.local_epoch as int),\
             cast(current_observation.local_tz_offset as int)) as time',
        'current_observation.temp_f as temp',
        'current_observation.feelslike_f as feelslike',
        'current_observation.windchill_f as windchill',
        'current_observation.dewpoint_f as dewpoint',
        'current_observation.UV',
        'current_observation.precip_1hr_in as precip',
        'current_observation.pressure_in as pressure',
        'current_observation.relative_humidity as humidity',
        'current_observation.visibility_mi',
        'current_observation.wind_dir',
        'current_observation.wind_gust_mph',
        'current_observation.wind_mph'
        ).distinct().withColumn('source', lit(source))

    # Table = forecast, Key = city, date, time, fdate, ftime
    # fdate/ftime are forecast date/time
    # Forecast weather data for the given key
    # Step 1: Convert current epochs to date/time and switch to RDD
    pathForecast1RDD = pathCleanDF.selectExpr(
        'current_observation.display_location.city',
        'date_from_epoch(cast(current_observation.local_epoch as int),\
             cast(current_observation.local_tz_offset as int)) as date',
        'time_from_epoch(cast(current_observation.local_epoch as int),\
             cast(current_observation.local_tz_offset as int)) as time',
        'cast(current_observation.local_tz_offset as int) as offset',
        'hourly_forecast.FCTTIME.epoch as epoch',
        'hourly_forecast.temp.english as temp',
        'hourly_forecast.feelslike.english as feelslike',
        'hourly_forecast.windchill.english as windchill',
        'hourly_forecast.dewpoint.english as dewpoint',
        'hourly_forecast.uvi as UV',
        'hourly_forecast.humidity as humidity',
        'hourly_forecast.wdir.dir as wind_dir',
        'hourly_forecast.wspd.english as wind_mph',
        ).rdd
    # Step 2: Zip forecast stats lists into 1 list of tuples
    pathForecast2RDD = pathForecast1RDD.map(lambda x: (
        x['city'],
        x['date'],
        x['time'],
        x['offset'],
        myzip(
            x['epoch'],
            x['temp'],
            x['feelslike'],
            x['windchill'],
            x['dewpoint'],
            x['UV'],
            x['humidity'],
            x['wind_dir'],
            x['wind_mph'])))
    # Step 3: Reshape and flatten into 1 row per key
    pathForecast3RDD = pathForecast2RDD.map(myreshape).flatMap(lambda x: x)
    # Step 4: From RDD back to DF
    pathForecast1DF = spark.createDataFrame(pathForecast3RDD, [
        'city',
        'date',
        'time',
        'offset',
        'epoch',
        'temp',
        'feelslike',
        'windchill',
        'dewpoint',
        'UV',
        'humidity',
        'wind_dir',
        'wind_mph'])
    # Step 5: Convert future epochs to fdate/ftime
    pathForecastDF = pathForecast1DF.selectExpr(
        'city',
        'date',
        'time',
        'date_from_epoch(cast(epoch as int),\
             cast(offset as int)) as fdate',
        'time_from_epoch(cast(epoch as int),\
             cast(offset as int)) as ftime',
        'temp',
        'feelslike',
        'windchill',
        'dewpoint',
        'UV',
        'humidity',
        'wind_dir',
        'wind_mph').withColumn('source', lit(source))

    return pathCityDF, pathNearbyDF, pathCityDayDF, pathWeatherDF, \
        pathForecastDF

def load_or_create():
    """Load parquet files if they exist.
    Otherwise, create DFs from first S3 file
    """
    # Try to load all of the existing dataframes
    try:
        path1DF = spark.read.parquet("pathDF.parquet").cache()
        city1DF = spark.read.parquet("cityDF.parquet").cache()
        nearby1DF = spark.read.parquet("nearbyDF.parquet").cache()
        cityDay1DF = spark.read.parquet("cityDayDF.parquet").cache()
        weather1DF = spark.read.parquet("weatherDF.parquet").cache()
        forecast1DF = spark.read.parquet("forecastDF.parquet").cache()

        # Cache DF so your first action isn't trying to overwrite these files
        a, b, c, d, e, f = path1DF.count(), city1DF.count(), \
            nearby1DF.count(), cityDay1DF.count(), weather1DF.count(), \
            forecast1DF.count()
    # Otherwise, create the needed dataframes from scratch using 1 path
    except:
        path = pathS3DF.take(1)
        path1DF = spark.sparkContext.parallelize(path).toDF()
        city1DF, nearby1DF, cityDay1DF, weather1DF, forecast1DF = \
            path_to_dfs(path)
    return path1DF, city1DF, nearby1DF, cityDay1DF, weather1DF, forecast1DF

def load_or_create_troubleshoot():
    """To troubleshoot, replace the load_or_create function in main
    This forces recreation of dataframes from scratch"""
    path = pathS3DF.take(1)
    pathDF = spark.sparkContext.parallelize(path).toDF()
    cityDF, nearbyDF, cityDayDF, weatherDF, forecastDF = path_to_dfs(path)
    return pathDF, cityDF, nearbyDF, cityDayDF, weatherDF, forecastDF

def append_new():
    """Append new data from paths in pathS3DF that aren't in pathDF"""
    # Identify new paths
    pathS3DF.createOrReplaceTempView('pathS3V')
    path1DF.createOrReplaceTempView('path1V')
    newPathDF = spark.sql(
        'select \
        path \
        from pathS3V \
        where path not in (select path from path1V)')

    # Stop if no new paths
    if newPathDF.count() == 0:
        return 0, 0, 0, 0, 0, 0, 0

    # Initialize output DFs
    path2DF = path1DF
    city2DF = city1DF
    nearby2DF = nearby1DF
    cityDay2DF = cityDay1DF
    weather2DF = weather1DF
    forecast2DF = forecast1DF

    # Append data for each new path to the respective dataframe
    appended_count = 0
    for path in newPathDF.collect()[0:100]:
        pathPathDF = spark.sparkContext.parallelize([path]).toDF()
        pathCityDF, pathNearbyDF, pathCityDayDF, pathWeatherDF, \
            pathForecastDF = path_to_dfs([path])

        path2DF = path2DF.unionAll(pathPathDF)
        city2DF = city2DF.unionAll(pathCityDF)
        nearby2DF = nearby2DF.unionAll(pathNearbyDF)
        cityDay2DF = cityDay2DF.unionAll(pathCityDayDF)
        weather2DF = weather2DF.unionAll(pathWeatherDF)
        forecast2DF = forecast2DF.unionAll(pathForecastDF)

        appended_count += 1

    return appended_count, path2DF, city2DF, nearby2DF, cityDay2DF, \
        weather2DF, forecast2DF

def enforce_keys():
    """Enforce keys by taking most recent record if duplicates"""
    city2DF.createOrReplaceTempView('city2V')
    nearby2DF.createOrReplaceTempView('nearby2V')
    cityDay2DF.createOrReplaceTempView('cityDay2V')
    weather2DF.createOrReplaceTempView('weather2V')
    forecast2DF.createOrReplaceTempView('forecast2V')

    # path - no change
    path3DF = path2DF
    # cityDF: city
    city3DF = spark.sql(
        'select * \
        from \
            (select * \
            ,row_number() over(partition by city order by source desc) as rk \
            from city2V) \
        where rk=1').drop('rk')
    # nearbyDF: city, nearby
    nearby3DF = spark.sql(
        'select * \
        from \
            (select * \
            ,row_number() over(partition by city, nearby \
                order by source desc) as rk \
            from nearby2V) \
        where rk=1').drop('rk')
    # cityDayDF: city, date
    cityDay3DF = spark.sql(
        'select * \
        from \
            (select * \
            ,row_number() over(partition by city, date \
                order by source desc) as rk \
            from cityDay2V) \
        where rk=1').drop('rk')
    # weatherDF: city, date, time
    weather3DF = spark.sql(
        'select * \
        from \
            (select * \
            ,row_number() over(partition by city, date, time \
                order by source desc) as rk \
            from weather2V) \
        where rk=1').drop('rk')
    # forecastDF: city, date, time, fdate, ftime
    forecast3DF = spark.sql(
        'select * \
        from \
            (select * \
            , row_number() over(partition by city, date, time, fdate, ftime \
                order by source desc) as rk \
            from forecast2V) \
        where rk=1').drop('rk')

    return path3DF, city3DF, nearby3DF, cityDay3DF, weather3DF, forecast3DF

def write_parquet():
    """Write DFs to parquet for re-use next time"""
    # Cache output files for continued use in analysis.
    path3DF.cache()
    city3DF.cache()
    nearby3DF.cache()
    cityDay3DF.cache()
    weather3DF.cache()
    forecast3DF.cache()

    # Write files
    path3DF.write.parquet("pathDF.parquet", mode='overwrite')
    city3DF.write.parquet("cityDF.parquet", mode='overwrite')
    nearby3DF.write.parquet("nearbyDF.parquet", mode='overwrite')
    cityDay3DF.write.parquet("cityDayDF.parquet", mode='overwrite')
    weather3DF.write.parquet("weatherDF.parquet", mode='overwrite')
    forecast3DF.write.parquet("forecastDF.parquet", mode='overwrite')

    # Uncache read-in DFs so you read from file next time
    path1DF.unpersist()
    city1DF.unpersist()
    nearby1DF.unpersist()
    cityDay1DF.unpersist()
    weather1DF.unpersist()
    forecast1DF.unpersist()
    return

def send_email():
    sender = 'student.seeber@galvanize.it'
    receivers = ['mseeber101@gmail.com']
    header = 'To: ' + receivers[0] + '\n' + 'From: ' + sender + '\n' + \
        'MIME-Version: 1.0\nContent-type: text/html \nSubject: Normalize Job\n'
    message = header + """
    <!DOCTYPE html>
    <html>
    Normalize Job Complete <br>
    Appended Count: """ + str(appended_count) + """
    </html>
    """
    smtpObj = smtplib.SMTP('localhost')
    smtpObj.sendmail(sender, receivers, message)
    return

if __name__ == "__main__":
    ############################################################
    # Obtain New Data From S3, Append, Normalize, Write Parquet
    ############################################################
    # Start Spark
    conf = SparkConf()
    conf.set('spark.driver.maxResultSize', '5g')
    spark = SparkSession.builder.config(conf=conf).getOrCreate()
    spark.sparkContext.setLogLevel('ERROR')

    # Connect to bucket
    conn = boto.connect_s3(host='s3.amazonaws.com')
    bucket_name = 'myweatherproject'
    bucket = conn.get_bucket(bucket_name)

    # Register UDFs
    spark.udf.register("date_from_epoch", date_from_epoch)
    spark.udf.register("time_from_epoch", time_from_epoch)

    # New DF listing all raw data files in S3
    pathS3DF = path_s3()

    # Load Noramlized DFs if they exist, otherwise create from first path.
    path1DF, city1DF, nearby1DF, cityDay1DF, weather1DF, forecast1DF = \
        load_or_create()

    # Append data from all new paths to the DFs
    appended_count, path2DF, city2DF, nearby2DF, cityDay2DF, weather2DF, \
        forecast2DF = append_new()

    # If there were new paths appended then proceed
    if path2DF != 0:
        # After appending duplicates, time to enforce keys!
        path3DF, city3DF, nearby3DF, cityDay3DF, weather3DF, forecast3DF = \
            enforce_keys()

        # Write DF to parquet
        write_parquet()

    # Send Completion E-mail
    send_email()


text:
package com.h6ah4i.android.example.openslmediaplayer.app.model;

import java.io.PrintWriter;
import java.io.StringWriter;

import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;

import com.h6ah4i.android.example.openslmediaplayer.app.framework.AppEvent;
import com.h6ah4i.android.example.openslmediaplayer.app.framework.AppEventBus;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.BassBoostNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.BassBoostReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.EnvironmentalReverbNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.EnvironmentalReverbReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.EqualizerNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.EqualizerReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.HQEqualizerNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.HQEqualizerReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.HQVisualizerNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.HQVisualizerReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.LoudnessEnhancerNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.LoudnessEnhancerReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.NavigationDrawerReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.PlayerControlNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.PlayerControlReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.PreAmpNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.PreAmpReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.PresetReverbNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.PresetReverbReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.VirtualizerNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.VirtualizerReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.VisualizerNotifyEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.model.EventDefs.VisualizerReqEvents;
import com.h6ah4i.android.example.openslmediaplayer.app.utils.EnvironmentalReverbPresetsUtil;
import com.h6ah4i.android.example.openslmediaplayer.app.utils.EqualizerUtil;
import com.h6ah4i.android.example.openslmediaplayer.app.utils.HQEqualizerUtil;
import com.h6ah4i.android.example.openslmediaplayer.app.utils.MediaMetadataBuilder;
import com.h6ah4i.android.example.openslmediaplayer.app.utils.NotificationBuilder;
import com.h6ah4i.android.media.IBasicMediaPlayer;
import com.h6ah4i.android.media.IMediaPlayerFactory;
import com.h6ah4i.android.media.IReleasable;
import com.h6ah4i.android.media.audiofx.IAudioEffect;
import com.h6ah4i.android.media.audiofx.IBassBoost;
import com.h6ah4i.android.media.audiofx.IEnvironmentalReverb;
import com.h6ah4i.android.media.audiofx.IEqualizer;
import com.h6ah4i.android.media.audiofx.IHQVisualizer;
import com.h6ah4i.android.media.audiofx.ILoudnessEnhancer;
import com.h6ah4i.android.media.audiofx.IPreAmp;
import com.h6ah4i.android.media.audiofx.IPresetReverb;
import com.h6ah4i.android.media.audiofx.IVirtualizer;
import com.h6ah4i.android.media.audiofx.IVisualizer;
import com.h6ah4i.android.media.opensl.OpenSLMediaPlayerFactory;
import com.h6ah4i.android.media.standard.StandardMediaPlayer;
import com.h6ah4i.android.media.standard.StandardMediaPlayerFactory;

public class GlobalAppController implements IReleasable {
    // constants
    public static final int PLAYER_STATE_IDLE = PlayerControlNotifyEvents.STATE_IDLE;
    public static final int PLAYER_STATE_INITIALIZED = PlayerControlNotifyEvents.STATE_INITIALIZED;
    public static final int PLAYER_STATE_PREPARING = PlayerControlNotifyEvents.STATE_PREPARING;
    public static final int PLAYER_STATE_PREPARED = PlayerControlNotifyEvents.STATE_PREPARED;
    public static final int PLAYER_STATE_STARTED = PlayerControlNotifyEvents.STATE_STARTED;
    public static final int PLAYER_STATE_PAUSED = PlayerControlNotifyEvents.STATE_PAUSED;
    public static final int PLAYER_STATE_STOPPED = PlayerControlNotifyEvents.STATE_STOPPED;
    public static final int PLAYER_STATE_PLAYBACK_COMPLETED = PlayerControlNotifyEvents.STATE_PLAYBACK_COMPLETED;
    public static final int PLAYER_STATE_END = PlayerControlNotifyEvents.STATE_END;
    public static final int PLAYER_STATE_ERROR = PlayerControlNotifyEvents.STATE_ERROR;

    private static final String PARCEL_KEY_MEDIA_PLAYER_STATE_STORE = "GlobalAppController.MediaPlayerStateStore";
    private static final String PARCEL_KEY_BASS_BOOST_STATE_STORE = "GlobalAppController.BassBoostStateStore";
    private static final String PARCEL_KEY_VIRTUALIZER_STATE_STORE = "GlobalAppController.VirtualizerStateStore";
    private static final String PARCEL_KEY_EQUALIZER_STATE_STORE = "GlobalAppController.EqualizerStateStore";
    private static final String PARCEL_KEY_LOUDNESS_ENHANCER_STATE_STORE = "GlobalAppController.LoudnessEnhancerStateStore";
    private static final String PARCEL_KEY_ENVIRONMENTAL_REVERB_STATE_STORE = "GlobalAppController.EnvironmentalReverbStateStore";
    private static final String PARCEL_KEY_PRESET_REVERB_STATE_STORE = "GlobalAppController.PresetReverbStateStore";
    private static final String PARCEL_KEY_VISUALIZER_STATE_STORE = "GlobalAppController.VisualizerStateStore";
    private static final String PARCEL_KEY_HQ_EQUALIZER_STATE_STORE = "GlobalAppController.HQEqualizerStateStore";
    private static final String PARCEL_KEY_PREAMP_STATE_STORE = "GlobalAppController.PreAmpStateStore";
    private static final String PARCEL_KEY_HQ_VISUALIZER_STATE_STORE = "GlobalAppController.HQVisualizerStateStore";

    private static final int FG_STATE_INACTIVE = -1;
    private static final int FG_STATE_PLAYER_0 = 1;
    private static final int FG_STATE_PLAYER_1 = 2;

    // internal classes
    private static class AppEventReceiver extends AppEventBus.Receiver<GlobalAppController> {
        private static final int[] FILTER = new int[] {
                EventDefs.Category.NAVIGATION_DRAWER,
                EventDefs.Category.PLAYER_CONTROL,
                EventDefs.Category.BASSBOOST,
                EventDefs.Category.VIRTUALIZER,
                EventDefs.Category.EQUALIZER,
                EventDefs.Category.LOUDNESS_EHNAHCER,
                EventDefs.Category.PRESET_REVERB,
                EventDefs.Category.ENVIRONMENTAL_REVERB,
                EventDefs.Category.VISUALIZER,
                EventDefs.Category.HQ_EQUALIZER,
                EventDefs.Category.PRE_AMP,
                EventDefs.Category.HQ_VISUALIZER,
        };

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

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

    // fields
    private Context mContext;
    private AppEventBus mEventBus;
    private AppEventReceiver mAppEventReceiver;

    private IMediaPlayerFactory mFactory;
    private IBasicMediaPlayer[] mMediaPlayer = new IBasicMediaPlayer[2];
    private IBassBoost mBassBoost;
    private IVirtualizer mVirtualizer;
    private IEqualizer mEqualizer;
    private ILoudnessEnhancer mLoudnessEnhancer;
    private IEnvironmentalReverb mEnvironmentalReverb;
    private IPresetReverb mPresetReverb;
    private IVisualizer mVisualizer;
    private IEqualizer mHQEqualizer;
    private IPreAmp mPreAmp;
    private IHQVisualizer mHQVisualizer;

    private int mActivePlayerIndex = 0;
    private boolean mNextPlayerPrepared = false;
    private boolean mSwapPlayerPending = false;
    private int[] mPlayerState = new int[] {
            PLAYER_STATE_END, PLAYER_STATE_END
    };
    private MediaMetadata[] mMetadata = new MediaMetadata[2];
    private Service mHolderService;
    private int mCurrentForegroundState = FG_STATE_INACTIVE;

    // --- these fields are saved to / restored from Bundle --
    private MediaPlayerStateStore mPlayerStateStore;
    private BassBoostStateStore mBassBoostStateStore;
    private VirtualizerStateStore mVirtualizerStateStore;
    private EqualizerStateStore mEqualizerStateStore;
    private LoudnessEnhancerStateStore mLoudnessEnhancerStateStore;
    private EnvironmentalReverbStateStore mEnvironmentalReverbStateStore;
    private PresetReverbStateStore mPresetReverbStateStore;
    private VisualizerStateStore mVisualizerStateStore;
    private HQEqualizerStateStore mHQEqualizerStateStore;
    private PreAmpStateStore mPreAmpStateStore;
    private HQVisualizerStateStore mHQVisualizerStateStore;
    //

    // MediaPlayer event listeners
    private IBasicMediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new IBasicMediaPlayer.OnBufferingUpdateListener() {
        @Override
        public void onBufferingUpdate(IBasicMediaPlayer mp, int percent) {
            GlobalAppController.this.onPlayerBufferingUpdate(mp);
        }
    };

    private IBasicMediaPlayer.OnCompletionListener mOnCompletionListener = new IBasicMediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(IBasicMediaPlayer mp) {
            GlobalAppController.this.onPlayerCompletion(mp);
        }
    };

    private IBasicMediaPlayer.OnErrorListener mOnErrorListener = new IBasicMediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(IBasicMediaPlayer mp, int what, int extra) {
            return GlobalAppController.this.onPlayerError(mp, what, extra);
        }
    };

    private IBasicMediaPlayer.OnInfoListener mOnInfoListener = new IBasicMediaPlayer.OnInfoListener() {
        @Override
        public boolean onInfo(IBasicMediaPlayer mp, int what, int extra) {
            return GlobalAppController.this.onPlayerInfo(mp, what, extra);
        }
    };

    private IBasicMediaPlayer.OnPreparedListener mOnPreparedListener = new IBasicMediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(IBasicMediaPlayer mp) {
            GlobalAppController.this.onPlayerPrepared(mp);
        }
    };

    private IBasicMediaPlayer.OnSeekCompleteListener mOnSeekCompleteListener = new IBasicMediaPlayer.OnSeekCompleteListener() {
        @Override
        public void onSeekComplete(IBasicMediaPlayer mp) {
            GlobalAppController.this.onPlayerSeekComplete(mp);
        }
    };

    public GlobalAppController(Context context, Bundle savedInstanceState) {
        mContext = context;

        // setup event bus
        mEventBus = new AppEventBus(mContext);

        // setup event receiver
        mAppEventReceiver = new AppEventReceiver(this);
        mEventBus.register(mAppEventReceiver);

        if (savedInstanceState == null) {
            // create audio effects state stores
            mPlayerStateStore = new MediaPlayerStateStore();
            mBassBoostStateStore = new BassBoostStateStore();
            mVirtualizerStateStore = new VirtualizerStateStore();
            mEqualizerStateStore = new EqualizerStateStore();
            mLoudnessEnhancerStateStore = new LoudnessEnhancerStateStore();
            mEnvironmentalReverbStateStore = new EnvironmentalReverbStateStore();
            mPresetReverbStateStore = new PresetReverbStateStore();
            mVisualizerStateStore = new VisualizerStateStore();
            mHQEqualizerStateStore = new HQEqualizerStateStore();
            mPreAmpStateStore = new PreAmpStateStore();
            mHQVisualizerStateStore = new HQVisualizerStateStore();
        } else {
            // restore from savedInstanceState
            mPlayerStateStore = (MediaPlayerStateStore) savedInstanceState
                    .getParcelable(PARCEL_KEY_MEDIA_PLAYER_STATE_STORE);
            mBassBoostStateStore = (BassBoostStateStore) savedInstanceState
                    .getParcelable(PARCEL_KEY_BASS_BOOST_STATE_STORE);
            mVirtualizerStateStore = (VirtualizerStateStore) savedInstanceState
                    .getParcelable(PARCEL_KEY_VIRTUALIZER_STATE_STORE);
            mEqualizerStateStore = (EqualizerStateStore) savedInstanceState
                    .getParcelable(PARCEL_KEY_EQUALIZER_STATE_STORE);
            mLoudnessEnhancerStateStore = (LoudnessEnhancerStateStore) savedInstanceState
                    .getParcelable(PARCEL_KEY_LOUDNESS_ENHANCER_STATE_STORE);
            mEnvironmentalReverbStateStore = (EnvironmentalReverbStateStore) savedInstanceState
                    .getParcelable(PARCEL_KEY_ENVIRONMENTAL_REVERB_STATE_STORE);
            mPresetReverbStateStore = (PresetReverbStateStore) savedInstanceState
                    .getParcelable(PARCEL_KEY_PRESET_REVERB_STATE_STORE);
            mVisualizerStateStore = (VisualizerStateStore) savedInstanceState.getParcelable(
                    PARCEL_KEY_VISUALIZER_STATE_STORE);
            mHQEqualizerStateStore = (HQEqualizerStateStore) savedInstanceState.getParcelable(
                    PARCEL_KEY_HQ_EQUALIZER_STATE_STORE);
            mPreAmpStateStore = (PreAmpStateStore) savedInstanceState.getParcelable(
                    PARCEL_KEY_PREAMP_STATE_STORE);
            mHQVisualizerStateStore = (HQVisualizerStateStore) savedInstanceState.getParcelable(
                    PARCEL_KEY_HQ_VISUALIZER_STATE_STORE);
        }

        // create media player factory
        setPlayerImplType(getPlayerStateStore().getPlayerImplType(), true);
    }

    @Override
    public void release() {
        releaseAllPlayerResources();
        releaseFactory();

        mOnBufferingUpdateListener = null;
        mOnCompletionListener = null;
        mOnErrorListener = null;
        mOnInfoListener = null;
        mOnPreparedListener = null;
        mOnSeekCompleteListener = null;

        mEventBus.unregister(mAppEventReceiver);
        mAppEventReceiver = null;

        mEventBus = null;
        mContext = null;
    }

    public void onSaveInstanceState(Bundle outState) {
        outState.putParcelable(
                PARCEL_KEY_MEDIA_PLAYER_STATE_STORE,
                mPlayerStateStore);
        outState.putParcelable(
                PARCEL_KEY_BASS_BOOST_STATE_STORE,
                mBassBoostStateStore);
        outState.putParcelable(
                PARCEL_KEY_VIRTUALIZER_STATE_STORE,
                mVirtualizerStateStore);
        outState.putParcelable(
                PARCEL_KEY_EQUALIZER_STATE_STORE,
                mEqualizerStateStore);
        outState.putParcelable(
                PARCEL_KEY_ENVIRONMENTAL_REVERB_STATE_STORE,
                mEnvironmentalReverbStateStore);
        outState.putParcelable(
                PARCEL_KEY_PRESET_REVERB_STATE_STORE,
                mPresetReverbStateStore);
        outState.putParcelable(
                PARCEL_KEY_VISUALIZER_STATE_STORE,
                mVisualizerStateStore);
        outState.putParcelable(
                PARCEL_KEY_HQ_EQUALIZER_STATE_STORE,
                mHQEqualizerStateStore);
        outState.putParcelable(
                PARCEL_KEY_PREAMP_STATE_STORE,
                mPreAmpStateStore);
    }

    public AppEventBus eventBus() {
        return mEventBus;
    }

    public MediaPlayerStateStore getPlayerStateStore() {
        return mPlayerStateStore;
    }

    public BassBoostStateStore getBassBoostStateStore() {
        return mBassBoostStateStore;
    }

    public VirtualizerStateStore getVirtualizerStateStore() {
        return mVirtualizerStateStore;
    }

    public EqualizerStateStore getEqualizerStateStore() {
        return mEqualizerStateStore;
    }

    public LoudnessEnhancerStateStore getLoudnessEnhancerStateStore() {
        return mLoudnessEnhancerStateStore;
    }

    public EnvironmentalReverbStateStore getEnvironmentalReverbStateStore() {
        return mEnvironmentalReverbStateStore;
    }

    public PresetReverbStateStore getPresetReverbStateStore() {
        return mPresetReverbStateStore;
    }

    public VisualizerStateStore getVisualizerStateStore() {
        return mVisualizerStateStore;
    }

    public HQEqualizerStateStore getHQEqualizerStateStore() {
        return mHQEqualizerStateStore;
    }

    public PreAmpStateStore getPreAmpStateStore() {
        return mPreAmpStateStore;
    }

    public HQVisualizerStateStore getHQVisualizerStateStore() {
        return mHQVisualizerStateStore;
    }

    public int getActivePlayerState() {
        return getPlayerState(getActivePlayerIndex());
    }

    public int getPlayerState(int index) {
        return mPlayerState[index];
    }

    private MediaMetadata getActiveMediaMetadata() {
        return mMetadata[getActivePlayerIndex()];
    }

    public float getNormalizedPlayerCurrentPosition() {
        int currentPosition = getActivePlayerCurrentPosition();
        int duration = getActivePlayerDuration();

        return (duration == 0) ? 0.0f : Math.min((float) currentPosition / duration, 1.0f);
    }

    public int getActivePlayerCurrentPosition() {
        IBasicMediaPlayer player = getActivePlayer();

        switch (getActivePlayerState()) {
            case PLAYER_STATE_STARTED:
            case PLAYER_STATE_PAUSED:
            case PLAYER_STATE_PLAYBACK_COMPLETED:
                return (player != null) ? player.getCurrentPosition() : 0;
            default:
                return 0;
        }
    }

    public int getActivePlayerDuration() {
        IBasicMediaPlayer player = getActivePlayer();

        switch (getActivePlayerState()) {
            case PLAYER_STATE_PREPARED:
            case PLAYER_STATE_STARTED:
            case PLAYER_STATE_PAUSED:
            case PLAYER_STATE_PLAYBACK_COMPLETED:
            case PLAYER_STATE_STOPPED:
                return (player != null) ? player.getDuration() : 0;
            default:
                return 0;
        }
    }

    /* package */void onPlayerSeekComplete(IBasicMediaPlayer mp) {
    }

    /* package */void onPlayerBufferingUpdate(IBasicMediaPlayer mp) {
    }

    /* package */void onPlayerCompletion(IBasicMediaPlayer mp) {
        int index = getMediaPlayerIndex(mp);

        if (index >= 0) {
            int origPlayerState = getPlayerState(index);

            if (getPlayerState(index) != PLAYER_STATE_ERROR) {
                setPlayerState(index, PLAYER_STATE_PLAYBACK_COMPLETED);
            }

            if (origPlayerState == PLAYER_STATE_STARTED) {
                if (swapActivePlayer(index)) {
                    setPlayerState(getActivePlayerIndex(), PLAYER_STATE_STARTED);
                }
            } else {
                mSwapPlayerPending = true;
            }
        }
    }

    private boolean swapActivePlayer(int index) {
        int next = nextPlayerIndex(index);

        if (mNextPlayerPrepared) {
            // next player is automatically started

            mActivePlayerIndex = next;

            return true;
        } else {
            return false;
        }
    }

    /* package */boolean onPlayerError(IBasicMediaPlayer mp, int what, int extra) {
        int index = getMediaPlayerIndex(mp);

        if (index >= 0) {
            postInfoOrErrorAppEvent(
                    EventDefs.Category.NOTIFY_PLAYER_CONTROL,
                    PlayerControlNotifyEvents.NOTIFY_PLAYER_ERROR,
                    index, 0,
                    what, extra);

            setPlayerState(index, PLAYER_STATE_ERROR);
        }

        // false: raise onCompletion event
        return false;
    }

    /* package */boolean onPlayerInfo(IBasicMediaPlayer mp, int what, int extra) {
        int index = getMediaPlayerIndex(mp);

        if (index >= 0) {
            postInfoOrErrorAppEvent(
                    EventDefs.Category.NOTIFY_PLAYER_CONTROL,
                    PlayerControlNotifyEvents.NOTIFY_PLAYER_INFO,
                    index, 0,
                    what, extra);
        }

        return false;
    }

    /* package */void onPlayerPrepared(IBasicMediaPlayer mp) {
        int index = getMediaPlayerIndex(mp);

        if (index >= 0) {
            setPlayerState(index, PLAYER_STATE_PREPARED);

            applyMediaPlayerStates(mp, getPlayerStateStore());
            applyAttachedEuxEffectSettings(
                    mp, getPlayerStateStore().getSelectedAuxEffectType());
        }

        // If both players are successfully prepared
        // set them up as a next player
        if (getPlayerState(0) == PLAYER_STATE_PREPARED &&
                getPlayerState(1) == PLAYER_STATE_PREPARED) {

            IBasicMediaPlayer player0 = getPlayer(0);
            IBasicMediaPlayer player1 = getPlayer(1);

            player0.setNextMediaPlayer(player1);
            player1.setNextMediaPlayer(player0);

            mNextPlayerPrepared = true;
        }
    }

    /* package */void onReceiveAppEvent(AppEvent event) {
        switch (event.category) {
            case EventDefs.Category.NAVIGATION_DRAWER:
                onReceiveNavigationDrawerEvent(event);
                break;
            case EventDefs.Category.PLAYER_CONTROL:
                onReceivePlayerControlEvent(event);
                break;
            case EventDefs.Category.BASSBOOST:
                onReceiveBassBoostEvent(event);
                break;
            case EventDefs.Category.VIRTUALIZER:
                onReceiveVirtualizerEvent(event);
                break;
            case EventDefs.Category.EQUALIZER:
                onReceiveEqualizerEvent(event);
                break;
            case EventDefs.Category.LOUDNESS_EHNAHCER:
                onReceiveLoudnessEnhancerEvent(event);
                break;
            case EventDefs.Category.PRESET_REVERB:
                onReceivePresetReverbEvent(event);
                break;
            case EventDefs.Category.ENVIRONMENTAL_REVERB:
                onReceiveEnvironmentalReverbEvent(event);
                break;
            case EventDefs.Category.VISUALIZER:
                onReceiveVisualizerEvent(event);
                break;
            case EventDefs.Category.HQ_EQUALIZER:
                onReceiveHQEqualizerEvent(event);
                break;
            case EventDefs.Category.PRE_AMP:
                onReceivePreAmpEvent(event);
                break;
            case EventDefs.Category.HQ_VISUALIZER:
                onReceiveHQVisualizerEvent(event);
                break;
        }
    }

    private void onReceiveNavigationDrawerEvent(AppEvent event) {
        switch (event.event) {
            case NavigationDrawerReqEvents.PLAYER_SET_IMPL_TYPE: {
                int type = event.arg1;
                setPlayerImplType(type, false);
            }
                break;
            case NavigationDrawerReqEvents.CLICK_ITEM_ENABLE_SWITCH: {
                // enable/disable audio effects

                boolean enabled = (event.arg2 != 0);

                switch (event.arg1) {
                    case NavigationDrawerReqEvents.SECTION_INDEX_BASSBOOST:
                        setBassBoostEnabled(enabled);
                        break;
                    case NavigationDrawerReqEvents.SECTION_INDEX_VIRTUALIZER:
                        setVirtualizerEnabled(enabled);
                        break;
                    case NavigationDrawerReqEvents.SECTION_INDEX_EQUALIZER:
                        setEqualizerEnabled(enabled);
                        break;
                    case NavigationDrawerReqEvents.SECTION_INDEX_LOUDNESS_ENHANCER:
                        setLoudnessEnhancerEnabled(enabled);
                        break;
                    case NavigationDrawerReqEvents.SECTION_INDEX_PRESET_REVERB:
                        setPresetReverbEnabled(enabled);
                        break;
                    case NavigationDrawerReqEvents.SECTION_INDEX_ENVIRONMENTAL_REVERB:
                        setEnvironmentalReverbEnabled(enabled);
                        break;
                    case NavigationDrawerReqEvents.SECTION_INDEX_HQ_EQUALIZER:
                        setHQEqualizerEnabled(enabled);
                        break;
                }
            }
                break;
        }
    }

    private void onReceivePlayerControlEvent(AppEvent event) {
        MediaPlayerStateStore state = getPlayerStateStore();
        switch (event.event) {
            case PlayerControlReqEvents.SONG_PICKED: {
                Uri uri = (Uri) event.extras.getParcelable(PlayerControlReqEvents.EXTRA_URI);
                state.setMediaUri(event.arg1, uri);
            }
                break;
            case PlayerControlReqEvents.PLAYER_CREATE: {
                releaseAllPlayerResources();

                createPlayer(0);
                createPlayer(1);

                createNormalAudioEffects();
                createAuxAudioEffects();
                createVisualizer();
                createHQEqualizer();
                createPreAmp();
                createHQVisualizer();

                applyBassBoostStates(mBassBoost, mBassBoostStateStore);
                applyVirtualizerStates(mVirtualizer, mVirtualizerStateStore);
                applyEqualizerStates(mEqualizer, mEqualizerStateStore);
                applyLoudnessEnhancerStates(mLoudnessEnhancer, mLoudnessEnhancerStateStore);
                applyEnvironmentalReverbStates(mEnvironmentalReverb, mEnvironmentalReverbStateStore);
                applyPresetReverbStates(mPresetReverb, mPresetReverbStateStore);
                applyHQEqualizerStates(mHQEqualizer, mHQEqualizerStateStore);

                playerSetLooping(0, state.isLooping());
                playerSetLooping(1, state.isLooping());

                playerSetVolume(0, state.getVolumeLeft(), state.getVolumeRight());
                playerSetVolume(1, state.getVolumeLeft(), state.getVolumeRight());

                playerSetAuxSendLevel(0, state.getAuxEffectSendLevel());
                playerSetAuxSendLevel(1, state.getAuxEffectSendLevel());

                setPlayerState(0, PLAYER_STATE_IDLE);
                setPlayerState(1, PLAYER_STATE_IDLE);

                // enable pre.amp
                setPreAmpEnabled(true);
            }
                break;
            case PlayerControlReqEvents.PLAYER_SET_DATA_SOURCE: {

                playerSetDataSource(0);
                playerSetDataSource(1);
            }
                break;
            case PlayerControlReqEvents.PLAYER_PREPARE: {
                playerPrepare(0);
                playerPrepare(1);
            }
                break;
            case PlayerControlReqEvents.PLAYER_PREPARE_ASYNC: {
                playerPrepareAsync(0);
                playerPrepareAsync(1);
            }
                break;
            case PlayerControlReqEvents.PLAYER_START: {

                // process pending player swapping
                if (mSwapPlayerPending) {
                    mSwapPlayerPending = false;

                    if (!getPlayerStateStore().isLooping()) {
                        swapActivePlayer(getActivePlayerIndex());
                    }
                }

                playerStart(getActivePlayerIndex());
            }
                break;
            case PlayerControlReqEvents.PLAYER_PAUSE: {
                playerPause(getActivePlayerIndex());
            }
                break;
            case PlayerControlReqEvents.PLAYER_STOP: {
                playerStop(0);
                playerStop(1);
                resetPlayerStateControlVariables();
            }
                break;
            case PlayerControlReqEvents.PLAYER_RESET: {
                playerReset(0);
                playerReset(1);
                resetPlayerStateControlVariables();
            }
                break;
            case PlayerControlReqEvents.PLAYER_RELEASE: {
                playerRelease(0);
                playerRelease(1);
                resetPlayerStateControlVariables();
            }
                break;
            case PlayerControlReqEvents.PLAYER_SEEK_TO: {
                float position = event.getArg2AsFloat();
                activePlayerSeekTo(position);
            }
                break;
            case PlayerControlReqEvents.PLAYER_SET_VOLUME_LEFT: {
                state.setVolumeLeft(event.getArg2AsFloat());

                // apply
                playerSetVolume(0, state.getVolumeLeft(), state.getVolumeRight());
                playerSetVolume(1, state.getVolumeLeft(), state.getVolumeRight());
            }
                break;
            case PlayerControlReqEvents.PLAYER_SET_VOLUME_RIGHT: {
                state.setVolumeRight(event.getArg2AsFloat());

                // apply
                playerSetVolume(0, state.getVolumeLeft(), state.getVolumeRight());
                playerSetVolume(1, state.getVolumeLeft(), state.getVolumeRight());
            }
                break;
            case PlayerControlReqEvents.PLAYER_SET_LOOPING: {
                boolean looping = (event.arg1 != 0);

                state.setLooping(looping);

                // apply
                playerSetLooping(0, state.isLooping());
                playerSetLooping(1, state.isLooping());
            }
                break;
            case PlayerControlReqEvents.PLAYER_ATTACH_AUX_EFFECT: {
                int effectType = event.arg1;

                if (state.getSelectedAuxEffectType() == effectType)
                    return;

                state.setSelectedAuxEffectType(effectType);

                // apply
                checkStateAndApplyAttachedAuxEffectSettings(0);
                checkStateAndApplyAttachedAuxEffectSettings(1);
            }
                break;
            case PlayerControlReqEvents.PLAYER_SET_AUX_SEND_LEVEL: {
                state.setAuxEffectSendLevel(event.getArg2AsFloat());

                // apply
                playerSetAuxSendLevel(0, state.getAuxEffectSendLevel());
                playerSetAuxSendLevel(1, state.getAuxEffectSendLevel());
            }
                break;
        }
    }

    private void resetPlayerStateControlVariables() {
        mActivePlayerIndex = 0;
        mNextPlayerPrepared = false;
        mSwapPlayerPending = false;
    }

    private void playerPrepare(int index) {
        IBasicMediaPlayer player = getPlayer(index);

        if (player != null) {
            try {
                player.prepare();
                setPlayerState(index, PLAYER_STATE_PREPARED);
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "prepare", e);
            }
        }
    }

    private void playerPrepareAsync(int index) {
        IBasicMediaPlayer player = getPlayer(index);

        if (player != null) {
            try {
                player.prepareAsync();
                setPlayerState(index, PLAYER_STATE_PREPARING);
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "prepareAsync", e);
            }
        }
    }

    private void playerStart(int index) {
        IBasicMediaPlayer player = getPlayer(index);
        boolean isErrorState = getPlayerState(index) == PLAYER_STATE_ERROR;

        if (player != null && !isErrorState) {
            try {
                player.start();
                setPlayerState(index, PLAYER_STATE_STARTED);
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "start", e);
            }
        }
    }

    private void playerPause(int index) {
        IBasicMediaPlayer player = getPlayer(index);
        boolean isErrorState = getPlayerState(index) == PLAYER_STATE_ERROR;

        if (player != null && !isErrorState) {
            try {
                player.pause();
                setPlayerState(index, PLAYER_STATE_PAUSED);
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "pause", e);
            }
        }
    }

    private void playerStop(int index) {
        IBasicMediaPlayer player = getPlayer(index);
        boolean isErrorState = getPlayerState(index) == PLAYER_STATE_ERROR;

        if (player != null && !isErrorState) {
            try {
                player.stop();
                setPlayerState(index, PLAYER_STATE_STOPPED);
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "stop", e);
            }
        }
    }

    private void playerReset(int index) {
        IBasicMediaPlayer player = getPlayer(index);

        if (player != null) {
            try {
                player.reset();
                mMetadata[index] = null;
                setPlayerState(index, PLAYER_STATE_IDLE);
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "reset", e);
            }
        }
    }

    private void playerRelease(int index) {
        IBasicMediaPlayer player = getPlayer(index);

        if (player != null) {
            try {
                player.release();
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "release", e);
            }

            mMediaPlayer[index] = null;
            mMetadata[index] = null;
            setPlayerState(index, PLAYER_STATE_END);
        }
    }

    private void activePlayerSeekTo(float position) {
        int index = getActivePlayerIndex();
        IBasicMediaPlayer player = getPlayer(index);

        int msec = (int) (position * getActivePlayerDuration());

        if (player != null) {
            try {
                player.seekTo(msec);
                mSwapPlayerPending = false;
            } catch (Exception e) {
                notifyPlayerExceptionOccurred(index, "seekTo(msec = " + msec + ")", e);
            }
        }
    }

    private void playerSetVolume(int index, float leftVolume, float rightVolume) {
        IBasicMediaPlayer player = getPlayer(index);

        if (player != null) {
            player.setVolume(leftVolume, rightVolume);
        }
    }

    private void playerSetLooping(int index, boolean looping) {
        IBasicMediaPlayer player = getPlayer(index);

        if (player != null) {
            player.setLooping(looping);
        }
    }

    private void playerSetDataSource(int index) {
        IBasicMediaPlayer player = getPlayer(index);
        Uri uri = getPlayerStateStore().getMediaUri(index);

        if (player != null && uri != null) {
            try {
                MediaMetadata metadata;
                metadata = MediaMetadataBuilder.create(mContext, uri);
                player.setDataSource(mContext, uri);
                mMetadata[index] = metadata;
                setPlayerState(index, PLAYER_STATE_INITIALIZED);
            } catch (final Exception e) {
                notifyPlayerExceptionOccurred(index, "setDataSource", e);
            }
        }
    }

    private void playerSetAuxSendLevel(int index, float level) {
        IBasicMediaPlayer player = getPlayer(index);

        if (player != null) {
            player.setAuxEffectSendLevel(level);
        }
    }

    private void checkStateAndApplyAttachedAuxEffectSettings(int index) {
        switch (getPlayerState(index)) {
            case PLAYER_STATE_PREPARED:
            case PLAYER_STATE_STARTED:
            case PLAYER_STATE_PAUSED:
            case PLAYER_STATE_PLAYBACK_COMPLETED:
                applyAttachedEuxEffectSettings(
                        getPlayer(index),
                        getPlayerStateStore().getSelectedAuxEffectType());
                break;
        }
    }

    private void onReceiveBassBoostEvent(AppEvent event) {
        switch (event.event) {
            case BassBoostReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setBassBoostEnabled(enabled);
            }
                break;
            case BassBoostReqEvents.SET_STRENGTH: {
                float strength = event.getArg2AsFloat();
                IBassBoost bassboost = getBassBoost();
                BassBoostStateStore state = getBassBoostStateStore();

                state.setNormalizedStrength(strength);

                if (bassboost != null) {
                    bassboost.setStrength(state.getSettings().strength);
                }
            }
                break;
        }
    }

    private void onReceiveVirtualizerEvent(AppEvent event) {
        switch (event.event) {
            case VirtualizerReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setVirtualizerEnabled(enabled);
            }
                break;
            case VirtualizerReqEvents.SET_STRENGTH: {
                float strength = event.getArg2AsFloat();
                IVirtualizer virtualizer = getVirtualizer();
                VirtualizerStateStore state = getVirtualizerStateStore();

                state.setNormalizedStrength(strength);

                if (virtualizer != null) {
                    virtualizer.setStrength(state.getSettings().strength);
                }
            }
                break;
        }
    }

    private void onReceiveEqualizerEvent(AppEvent event) {
        switch (event.event) {
            case EqualizerReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setEqualizerEnabled(enabled);
            }
                break;
            case EqualizerReqEvents.SET_PRESET: {
                short preset = (short) event.arg1;
                IEqualizer equalizer = getEqualizer();
                EqualizerStateStore state = getEqualizerStateStore();

                state.getSettings().curPreset = preset;

                // apply
                if (equalizer != null) {
                    equalizer.usePreset(state.getSettings().curPreset);
                }

                // update band levels
                state.setSettings(
                        EqualizerUtil.PRESETS[state.getSettings().curPreset].settings);

                // notify
                postAppEvent(
                        EventDefs.Category.NOTIFY_EQUALIZER,
                        EqualizerNotifyEvents.PRESET_UPDATED, preset, 0);
                postAppEvent(
                        EventDefs.Category.NOTIFY_EQUALIZER,
                        EqualizerNotifyEvents.BAND_LEVEL_UPDATED,
                        -1 /* all bands */, 0);
            }
                break;
            case EqualizerReqEvents.SET_BAND_LEVEL: {
                short band = (short) event.arg1;
                float level = event.getArg2AsFloat();
                IEqualizer equalizer = getEqualizer();
                EqualizerStateStore state = getEqualizerStateStore();

                state.setNormalizedBandLevel(band, level, true);

                // apply
                if (equalizer != null) {
                    equalizer.setBandLevel(
                            band, state.getSettings().bandLevels[band]);
                }

                // notify
                postAppEvent(
                        EventDefs.Category.NOTIFY_EQUALIZER,
                        EqualizerNotifyEvents.BAND_LEVEL_UPDATED, band, 0);
            }
                break;
        }
    }

    private void onReceiveLoudnessEnhancerEvent(AppEvent event) {
        switch (event.event) {
            case LoudnessEnhancerReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setLoudnessEnhancerEnabled(enabled);
            }
                break;
            case LoudnessEnhancerReqEvents.SET_TARGET_GAIN: {
                float gainmB = event.getArg2AsFloat();
                ILoudnessEnhancer loudnessEnhancer = getLoudnessEnhancer();
                LoudnessEnhancerStateStore state = getLoudnessEnhancerStateStore();

                state.setNormalizedTargetGainmB(gainmB);

                if (loudnessEnhancer != null) {
                    loudnessEnhancer.setTargetGain(state.getSettings().targetGainmB);
                }
            }
                break;
        }
    }

    private void onReceivePresetReverbEvent(AppEvent event) {
        IPresetReverb presetreverb = getPresetReverb();
        PresetReverbStateStore state = getPresetReverbStateStore();

        switch (event.event) {
            case PresetReverbReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setPresetReverbEnabled(enabled);
            }
                break;
            case PresetReverbReqEvents.SET_PRESET:
                state.getSettings().preset = (short) event.arg1;

                // apply
                if (presetreverb != null) {
                    presetreverb.setPreset(state.getSettings().preset);
                }

                // notify preset changed
                postAppEvent(
                        EventDefs.Category.NOTIFY_PRESET_REVERB,
                        PresetReverbNotifyEvents.PRESET_UPDATED,
                        0, 0);
                break;
        }
    }

    private void onReceiveEnvironmentalReverbEvent(AppEvent event) {
        IEnvironmentalReverb envreverb = getEnvironmentalReverb();
        EnvironmentalReverbStateStore state = getEnvironmentalReverbStateStore();

        switch (event.event) {
            case EnvironmentalReverbReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setEnvironmentalReverbEnabled(enabled);
            }
                break;
            case EnvironmentalReverbReqEvents.SET_PRESET:
                state.setPreset(event.arg1);
                if (state.getPreset() >= 0) {
                    state.setSettings(EnvironmentalReverbPresetsUtil.getPreset(state.getPreset()));
                }

                // apply
                if (envreverb != null) {
                    envreverb.setProperties(state.getSettings());
                }

                // notify preset changed
                postAppEvent(
                        EventDefs.Category.NOTIFY_ENVIRONMENTAL_REVERB,
                        EnvironmentalReverbNotifyEvents.PRESET_UPDATED,
                        0, 0);

                // notify all parameters updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_ENVIRONMENTAL_REVERB,
                        EnvironmentalReverbNotifyEvents.PARAMETER_UPDATED,
                        EnvironmentalReverbReqEvents.PARAM_INDEX_ALL,
                        0);

                break;
            case EnvironmentalReverbReqEvents.SET_PARAMETER:
                state.setPreset(-1); // unset current preset

                state.setNormalizedParameter(event.arg1, event.getArg2AsFloat());

                // apply
                if (envreverb != null) {
                    applyEnvironmentalReverbPresetParam(envreverb, state, event.arg1);
                }

                // notify parameter changed
                postAppEvent(
                        EventDefs.Category.NOTIFY_ENVIRONMENTAL_REVERB,
                        EnvironmentalReverbNotifyEvents.PARAMETER_UPDATED,
                        event.arg1, 0);

                // notify preset changed
                postAppEvent(
                        EventDefs.Category.NOTIFY_ENVIRONMENTAL_REVERB,
                        EnvironmentalReverbNotifyEvents.PRESET_UPDATED,
                        0, 0);
                break;
        }
    }

    private void onReceiveVisualizerEvent(AppEvent event) {
        VisualizerStateStore state = getVisualizerStateStore();

        // NOTE:
        // Only save the enabled state, and don't apply to
        // the effect object here.

        switch (event.event) {
            case VisualizerReqEvents.SET_WAVEFORM_ENABLED: {
                boolean enabled = (event.arg1 != 0);

                state.setCaptureWaveformEnabled(enabled);

                // notify enabled state updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_VISUALIZER,
                        VisualizerNotifyEvents.WAVEFORM_ENABLED_STATE_UPDATED,
                        state.isCaptureWaveformEnabled() ? 1 : 0,
                        0);
            }
                break;
            case VisualizerReqEvents.SET_FFT_ENABLED: {
                boolean enabled = (event.arg1 != 0);

                state.setCaptureFftEnabled(enabled);

                // notify enabled state updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_VISUALIZER,
                        VisualizerNotifyEvents.FFT_ENABLED_STATE_UPDATED,
                        state.isCaptureFftEnabled() ? 1 : 0,
                        0);
            }
                break;
            case VisualizerReqEvents.SET_SCALING_MODE:
                int scalingMode = event.arg1;

                state.setScalingMode(scalingMode);

                // notify enabled state updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_VISUALIZER,
                        VisualizerNotifyEvents.SCALING_MODE_UPDATED,
                        state.getScalingMode(),
                        0);
                break;
            case VisualizerReqEvents.SET_MEASURE_PEAK_ENABLED: {
                boolean enabled = (event.arg1 != 0);

                state.setMeasurementPeakEnabled(enabled);

                // notify enabled state updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_VISUALIZER,
                        VisualizerNotifyEvents.MEASURE_PEAK_ENABLED_STATE_UPDATED,
                        state.isMeasurementPeakEnabled() ? 1 : 0,
                        0);
            }
                break;
            case VisualizerReqEvents.SET_MEASURE_RMS_ENABLED: {
                boolean enabled = (event.arg1 != 0);

                state.setMeasurementRmsEnabled(enabled);

                // notify enabled state updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_VISUALIZER,
                        VisualizerNotifyEvents.MEASURE_RMS_ENABLED_STATE_UPDATED,
                        state.isMeasurementRmsEnabled() ? 1 : 0,
                        0);
            }
                break;
        }
    }

    private void onReceiveHQEqualizerEvent(AppEvent event) {
        switch (event.event) {
            case HQEqualizerReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setHQEqualizerEnabled(enabled);
            }
                break;
            case HQEqualizerReqEvents.SET_PRESET: {
                short preset = (short) event.arg1;
                IEqualizer equalizer = getHQEqualizer();
                HQEqualizerStateStore state = getHQEqualizerStateStore();

                state.getSettings().curPreset = preset;

                // apply
                if (equalizer != null) {
                    equalizer.usePreset(state.getSettings().curPreset);
                }

                // update band levels
                state.setSettings(
                        HQEqualizerUtil.PRESETS[state.getSettings().curPreset].settings);

                // notify
                postAppEvent(
                        EventDefs.Category.NOTIFY_HQ_EQUALIZER,
                        HQEqualizerNotifyEvents.PRESET_UPDATED, preset, 0);
                postAppEvent(
                        EventDefs.Category.NOTIFY_HQ_EQUALIZER,
                        HQEqualizerNotifyEvents.BAND_LEVEL_UPDATED,
                        -1 /* all bands */, 0);
            }
                break;
            case HQEqualizerReqEvents.SET_BAND_LEVEL: {
                short band = (short) event.arg1;
                float level = event.getArg2AsFloat();
                IEqualizer equalizer = getHQEqualizer();
                HQEqualizerStateStore state = getHQEqualizerStateStore();

                state.setNormalizedBandLevel(band, level, true);

                // apply
                if (equalizer != null) {
                    equalizer.setBandLevel(
                            band, state.getSettings().bandLevels[band]);
                }

                // notify
                postAppEvent(
                        EventDefs.Category.NOTIFY_HQ_EQUALIZER,
                        HQEqualizerNotifyEvents.BAND_LEVEL_UPDATED, band, 0);
            }
                break;
        }
    }

    private void onReceivePreAmpEvent(AppEvent event) {
        switch (event.event) {
            case PreAmpReqEvents.SET_ENABLED: {
                boolean enabled = (event.arg1 != 0);
                setPreAmpEnabled(enabled);
            }
                break;
            case PreAmpReqEvents.SET_LEVEL: {
                float level = event.getArg1AsFloat();
                IPreAmp preamp = getPreAmp();
                PreAmpStateStore state = getPreAmpStateStore();

                state.setLevelFromUI(level);

                // apply
                if (preamp != null) {
                    preamp.setLevel(state.getLevel());
                }

                // notify
                postAppEvent(
                        EventDefs.Category.NOTIFY_PRE_AMP,
                        PreAmpNotifyEvents.LEVEL_UPDATED, 0, 0);
            }
                break;
        }
    }

    private void onReceiveHQVisualizerEvent(AppEvent event) {
        HQVisualizerStateStore state = getHQVisualizerStateStore();

        // NOTE:
        // Only save the enabled state, and don't apply to
        // the effect object here.

        switch (event.event) {
            case HQVisualizerReqEvents.SET_WAVEFORM_ENABLED: {
                boolean enabled = (event.arg1 != 0);

                state.setCaptureWaveformEnabled(enabled);

                // notify enabled state updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_HQ_VISUALIZER,
                        HQVisualizerNotifyEvents.WAVEFORM_ENABLED_STATE_UPDATED,
                        state.isCaptureWaveformEnabled() ? 1 : 0,
                        0);
            }
                break;
            case HQVisualizerReqEvents.SET_FFT_ENABLED: {
                boolean enabled = (event.arg1 != 0);

                state.setCaptureFftEnabled(enabled);

                // notify enabled state updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_HQ_VISUALIZER,
                        HQVisualizerNotifyEvents.FFT_ENABLED_STATE_UPDATED,
                        state.isCaptureFftEnabled() ? 1 : 0,
                        0);
            }
                break;
            case HQVisualizerReqEvents.SET_WINDOW_TYPE: {
                int windowType = event.arg1;

                state.setWindowType(windowType);

                // notify window type updated
                postAppEvent(
                        EventDefs.Category.NOTIFY_HQ_VISUALIZER,
                        HQVisualizerNotifyEvents.WINDOW_TYPE_UPDATED,
                        state.getWindowType(),
                        0);
            }
                break;
        }
    }

    private void applyAttachedEuxEffectSettings(IBasicMediaPlayer player, int type) {
        IEnvironmentalReverb envReverb = getEnvironmentalReverb();
        IPresetReverb presetReverb = getPresetReverb();
        int effectId = 0;

        switch (type) {
            case PlayerControlReqEvents.AUX_EEFECT_TYPE_NONE:
                break;
            case PlayerControlReqEvents.AUX_EEFECT_TYPE_ENVIRONMENAL_REVERB:
                if (envReverb != null) {
                    effectId = envReverb.getId();
                }
                break;
            case PlayerControlReqEvents.AUX_EEFECT_TYPE_PRESET_REVERB:
                if (presetReverb != null) {
                    effectId = presetReverb.getId();
                }
                break;
        }

        if (player != null) {
            player.attachAuxEffect(effectId);
        }
    }

    private void setPlayerState(int index, int state) {
        if (mPlayerState[index] == state)
            return;

        mPlayerState[index] = state;

        onPlayerStateChanged(index, state);
    }

    private void onPlayerStateChanged(int index, int state) {
        // set foreground if needed
        checkAndUpdateForegroundState();

        // broadcast event
        postAppEvent(
                EventDefs.Category.NOTIFY_PLAYER_CONTROL,
                PlayerControlNotifyEvents.PLAYER_STATE_CHANGED,
                index, mPlayerState[index]);
    }

    private static void applyEnvironmentalReverbPresetParam(
            IEnvironmentalReverb envreverb,
            EnvironmentalReverbStateStore state, int index) {
        IEnvironmentalReverb.Settings settings = state.getSettings();

        switch (index) {
            case EnvironmentalReverbStateStore.PARAM_INDEX_DECAY_HF_RATIO:
                envreverb.setDecayHFRatio(settings.decayHFRatio);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_DECAY_TIME:
                envreverb.setDecayTime(settings.decayTime);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_DENSITY:
                envreverb.setDensity(settings.density);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_DIFFUSION:
                envreverb.setDiffusion(settings.diffusion);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_REFLECTIONS_DELAY:
                envreverb.setReflectionsDelay(settings.reflectionsDelay);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_REFLECTIONS_LEVEL:
                envreverb.setReflectionsLevel(settings.reflectionsLevel);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_REVERB_DELAY:
                envreverb.setReverbDelay(settings.reverbDelay);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_REVERB_LEVEL:
                envreverb.setReverbLevel(settings.reverbLevel);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_ROOM_HF_LEVEL:
                envreverb.setRoomHFLevel(settings.roomHFLevel);
                break;
            case EnvironmentalReverbStateStore.PARAM_INDEX_ROOM_LEVEL:
                envreverb.setRoomLevel(settings.roomLevel);
                break;
        }
    }

    private void postAppEvent(int category, int event, int arg1, int arg2) {
        eventBus().post(new AppEvent(category, event, arg1, arg2));
    }

    private void postInfoOrErrorAppEvent(int category, int event, int arg1, int arg2, int what,
            int extra) {
        AppEvent eventObj = new AppEvent(category, event, arg1, arg2);

        eventObj.extras = new Bundle();
        eventObj.extras.putInt(PlayerControlNotifyEvents.EXTRA_ERROR_INFO_WHAT, what);
        eventObj.extras.putInt(PlayerControlNotifyEvents.EXTRA_ERROR_INFO_EXTRA, extra);

        eventBus().post(eventObj);
    }

    private void setPlayerImplType(int type, boolean force) {
        MediaPlayerStateStore state = getPlayerStateStore();

        switch (type) {
            case MediaPlayerStateStore.PLAYER_IMPL_TYPE_STANDARD:
            case MediaPlayerStateStore.PLAYER_IMPL_TYPE_OPENSL:
                break;
            default:
                throw new IllegalArgumentException();
        }

        if (!force && state.getPlayerImplType() == type)
            return;

        releaseAllPlayerResources();
        releaseFactory();

        state.setPlayerImplType(type);

        switch (type) {
            case MediaPlayerStateStore.PLAYER_IMPL_TYPE_STANDARD:
                mFactory = new StandardMediaPlayerFactory(mContext);
                break;
            case MediaPlayerStateStore.PLAYER_IMPL_TYPE_OPENSL:
                mFactory = new OpenSLMediaPlayerFactory(mContext);
                break;
        }
    }

    private void applyMediaPlayerStates(
            IBasicMediaPlayer player,
            MediaPlayerStateStore states) {
        player.setLooping(states.isLooping());
        player.setVolume(states.getVolumeLeft(), states.getVolumeRight());
        player.setAuxEffectSendLevel(states.getAuxEffectSendLevel());
    }

    private void applyBassBoostStates(
            IBassBoost bassboost, BassBoostStateStore states) {
        if (bassboost == null)
            return;

        bassboost.setProperties(states.getSettings());
        bassboost.setEnabled(states.isEnabled());
    }

    private void applyVirtualizerStates(
            IVirtualizer virtualizer, VirtualizerStateStore states) {
        if (virtualizer == null)
            return;

        virtualizer.setProperties(states.getSettings());
        virtualizer.setEnabled(states.isEnabled());
    }

    private void applyEqualizerStates(
            IEqualizer equalizer, EqualizerStateStore states) {
        if (equalizer == null)
            return;

        equalizer.setProperties(states.getSettings());
        equalizer.setEnabled(states.isEnabled());
    }

    private void applyLoudnessEnhancerStates(
            ILoudnessEnhancer loudnessEnhancer, LoudnessEnhancerStateStore states) {
        if (loudnessEnhancer == null)
            return;

        loudnessEnhancer.setProperties(states.getSettings());
        loudnessEnhancer.setEnabled(states.isEnabled());
    }

    private void applyPresetReverbStates(
            IPresetReverb presetreverb, PresetReverbStateStore states) {
        if (presetreverb == null)
            return;

        presetreverb.setProperties(states.getSettings());
        presetreverb.setEnabled(states.isEnabled());
    }

    private void applyEnvironmentalReverbStates(
            IEnvironmentalReverb envreverb, EnvironmentalReverbStateStore states) {
        if (envreverb == null)
            return;

        envreverb.setProperties(states.getSettings());
        envreverb.setEnabled(states.isEnabled());
    }

    private void applyHQEqualizerStates(
            IEqualizer equalizer, HQEqualizerStateStore states) {
        if (equalizer == null)
            return;

        equalizer.setProperties(states.getSettings());
        equalizer.setEnabled(states.isEnabled());
    }

    private boolean createPlayer(int index) {
        if (mMediaPlayer[index] == null) {
            IBasicMediaPlayer player = mFactory.createMediaPlayer();

            player.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
            player.setOnCompletionListener(mOnCompletionListener);
            player.setOnErrorListener(mOnErrorListener);
            player.setOnInfoListener(mOnInfoListener);
            player.setOnPreparedListener(mOnPreparedListener);
            player.setOnSeekCompleteListener(mOnSeekCompleteListener);

            mMediaPlayer[index] = player;
        }

        return (mMediaPlayer[index] != null);
    }

    private void createNormalAudioEffects() {
        IMediaPlayerFactory factory = mFactory;
        IBasicMediaPlayer player0 = getPlayer(0);
        IBasicMediaPlayer player1 = getPlayer(1);

        if (player0 instanceof StandardMediaPlayer) {
            // Share same audio session
            player1.setAudioSessionId(player0.getAudioSessionId());
        } else {
            // OpenSLMediaPlayer does not support setAudioSessionId() method.
            // However audio effects are shared if player instances were created
            // by the same factory instance.
        }

        try {
            mBassBoost = factory.createBassBoost(player0);
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
        try {
            mVirtualizer = factory.createVirtualizer(player0);
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
        try {
            mEqualizer = factory.createEqualizer(player0);
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
        try {
            mLoudnessEnhancer = factory.createLoudnessEnhancer(player0);
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
    }

    private void createAuxAudioEffects() {
        try {
            mEnvironmentalReverb = mFactory.createEnvironmentalReverb();
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }

        try {
            mPresetReverb = mFactory.createPresetReverb();
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
    }

    private void createVisualizer() {
        IMediaPlayerFactory factory = mFactory;
        IBasicMediaPlayer player0 = mMediaPlayer[0];

        try {
            mVisualizer = factory.createVisualizer(player0);
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
    }

    private void createHQEqualizer() {
        IMediaPlayerFactory factory = mFactory;

        try {
            mHQEqualizer = factory.createHQEqualizer();
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
    }

    private void createPreAmp() {
        IMediaPlayerFactory factory = mFactory;

        try {
            mPreAmp = factory.createPreAmp();
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
    }

    private void createHQVisualizer() {
        IMediaPlayerFactory factory = mFactory;

        try {
            mHQVisualizer = factory.createHQVisualizer();
        } catch (UnsupportedOperationException e) {
            // the effect is not supported
        }
    }

    public int getActivePlayerIndex() {
        return mActivePlayerIndex;
    }

    private IBasicMediaPlayer getActivePlayer() {
        return getPlayer(getActivePlayerIndex());
    }

    private IBasicMediaPlayer getPlayer(int index) {
        return mMediaPlayer[index];
    }

    private IBassBoost getBassBoost() {
        return mBassBoost;
    }

    private IVirtualizer getVirtualizer() {
        return mVirtualizer;
    }

    private IEqualizer getEqualizer() {
        return mEqualizer;
    }

    private ILoudnessEnhancer getLoudnessEnhancer() {
        return mLoudnessEnhancer;
    }

    private IEnvironmentalReverb getEnvironmentalReverb() {
        return mEnvironmentalReverb;
    }

    private IPresetReverb getPresetReverb() {
        return mPresetReverb;
    }

    public IVisualizer getVisualizer() {
        return mVisualizer;
    }

    private IEqualizer getHQEqualizer() {
        return mHQEqualizer;
    }

    private IPreAmp getPreAmp() {
        return mPreAmp;
    }

    public IHQVisualizer getHQVisualizer() {
        return mHQVisualizer;
    }

    private void releaseAllPlayerResources() {
        playerRelease(0);
        playerRelease(1);
        resetPlayerStateControlVariables();

        safeRelease(mBassBoost);
        mBassBoost = null;

        safeRelease(mVirtualizer);
        mVirtualizer = null;

        safeRelease(mEqualizer);
        mEqualizer = null;

        safeRelease(mLoudnessEnhancer);
        mLoudnessEnhancer = null;

        safeRelease(mPresetReverb);
        mPresetReverb = null;

        safeRelease(mEnvironmentalReverb);
        mEnvironmentalReverb = null;

        safeRelease(mVisualizer);
        mVisualizer = null;

        safeRelease(mHQEqualizer);
        mHQEqualizer = null;

        safeRelease(mPreAmp);
        mPreAmp = null;

        safeRelease(mHQVisualizer);
        mHQVisualizer = null;
    }

    private void releaseFactory() {
        safeRelease(mFactory);
        mFactory = null;
    }

    private static void safeRelease(IReleasable obj) {
        try {
            if (obj != null) {
                obj.release();
            }
        } catch (Exception e) {
        }
    }

    private void setBassBoostEnabled(boolean enabled) {
        IAudioEffect effect = getBassBoost();
        BaseAudioEffectStateStore state = getBassBoostStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_BASSBOOST,
                BassBoostNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void setVirtualizerEnabled(boolean enabled) {
        IAudioEffect effect = getVirtualizer();
        BaseAudioEffectStateStore state = getVirtualizerStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_VIRTUALIZER,
                VirtualizerNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void setEqualizerEnabled(boolean enabled) {
        IAudioEffect effect = getEqualizer();
        BaseAudioEffectStateStore state = getEqualizerStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_EQUALIZER,
                EqualizerNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void setLoudnessEnhancerEnabled(boolean enabled) {
        ILoudnessEnhancer effect = getLoudnessEnhancer();
        BaseAudioEffectStateStore state = getLoudnessEnhancerStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_LOUDNESS_ENHANCER,
                LoudnessEnhancerNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void setEnvironmentalReverbEnabled(boolean enabled) {
        IAudioEffect effect = getEnvironmentalReverb();
        BaseAudioEffectStateStore state = getEnvironmentalReverbStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_ENVIRONMENTAL_REVERB,
                EnvironmentalReverbNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void setPresetReverbEnabled(boolean enabled) {
        IAudioEffect effect = getPresetReverb();
        BaseAudioEffectStateStore state = getPresetReverbStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_PRESET_REVERB,
                PresetReverbNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void setHQEqualizerEnabled(boolean enabled) {
        IAudioEffect effect = getHQEqualizer();
        BaseAudioEffectStateStore state = getHQEqualizerStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_HQ_EQUALIZER,
                HQEqualizerNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void setPreAmpEnabled(boolean enabled) {
        IAudioEffect effect = getPreAmp();
        BaseAudioEffectStateStore state = getPreAmpStateStore();

        handleAudioEffectEnabled(effect, state, enabled);

        // notify enabled state updated
        postAppEvent(
                EventDefs.Category.NOTIFY_PRE_AMP,
                PreAmpNotifyEvents.ENABLED_STATE_UPDATED,
                state.isEnabled() ? 1 : 0,
                0);
    }

    private void handleAudioEffectEnabled(
            IAudioEffect effect, BaseAudioEffectStateStore state, boolean enabled) {
        state.setEnabled(enabled);

        // apply
        if (effect != null) {
            effect.setEnabled(state.isEnabled());
        }
    }

    private int getMediaPlayerIndex(IBasicMediaPlayer player) {
        if (player == mMediaPlayer[0])
            return 0;
        if (player == mMediaPlayer[1])
            return 1;
        return -1; // default: ZERO
    }

    private void notifyPlayerExceptionOccurred(int index, String opName, Exception exception) {
        AppEvent eventObj = new AppEvent(
                EventDefs.Category.NOTIFY_PLAYER_CONTROL,
                PlayerControlNotifyEvents.NOTIFY_EXCEPTION_OCCURRED,
                index, 0);

        eventObj.extras = new Bundle();
        eventObj.extras.putString(
                PlayerControlNotifyEvents.EXTRA_EXEC_OPERATION_NAME,
                opName);
        eventObj.extras.putString(
                PlayerControlNotifyEvents.EXTRA_EXCEPTION_NAME,
                exception.getClass().getSimpleName());

        eventObj.extras.putString(
                PlayerControlNotifyEvents.EXTRA_STACK_TRACE,
                getStackTraceString(exception));

        eventBus().post(eventObj);
    }

    private static int nextPlayerIndex(int index) {
        return (index == 0) ? 1 : 0;
    }

    private static String getStackTraceString(Exception e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        pw.flush();
        return sw.toString();
    }

    public void bindHolderService(Service service) {
        mHolderService = service;

        checkAndUpdateForegroundState();
    }

    private void checkAndUpdateForegroundState() {
        if (mHolderService == null)
            return;

        final int index = getActivePlayerIndex();
        final boolean isPlaying = getActivePlayerState() == PLAYER_STATE_STARTED;
        final int reqState;

        if (isPlaying) {
            reqState = (index == 0) ? FG_STATE_PLAYER_0 : FG_STATE_PLAYER_1;
        } else {
            reqState = FG_STATE_INACTIVE;
        }

        if (mCurrentForegroundState == reqState)
            return;

        if (isPlaying) {
            final MediaMetadata metadata = getActiveMediaMetadata();

            if (metadata == null) {
                throw new IllegalStateException("Bug check (GitHub issue #5)");
            }

            final Notification notification = NotificationBuilder
                    .createServiceNotification(mHolderService, metadata);

            mHolderService.startForeground(
                    NotificationIds.ONGOING_NOTIFICATION, notification);
        } else {
            mHolderService.stopForeground(true);
        }

        mCurrentForegroundState = reqState;
    }
}

redpajama_set_name: RedPajamaGithub


text:
__version__ = '0.51'
__date__ = '2008-09-10'
__license__ = 'GPL'
__author__ = 'Peter Jurica, Jürgen Hermann, Mike Brown, Christopher Arndt'

import string, sys, cStringIO, cgi
import keyword, token, tokenize

_KEYWORD  = token.NT_OFFSET + 1
_TEXT     = token.NT_OFFSET + 2
_FUNCTION = token.NT_OFFSET + 3

_css_classes = {
    token.NUMBER:       'number',
    token.OP:           'operator',
    token.STRING:       'string',
    tokenize.COMMENT:   'comment',
    token.NAME:         'name',
    token.ERRORTOKEN:   'error',
    _KEYWORD:           'keyword',
    _TEXT:              'text',
    _FUNCTION:          'function',
}

STYLE_OBLIVION = """\
pre.code { font-style: Lucida,"Courier New"; background-color: #2e3436; }
    .line_number { color: #555753; }
    .number { color: #fce94f; }
    .operator { color: #ffffff; }
    .string { color: #fce94f; }
    .comment { color: #888a85; }
    .function { color: #729fcf; }
    .name { color: #eeeeec; }
    .error { color: red; border: solid 1.5pt #FF0000; }
    .keyword { color: #ffffff; font-weight: bold; }
    .text { color: #fce94f; }
"""

STYLE = STYLE_OBLIVION

template = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">

  <title>%(title)s</title>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  <meta name="Generator" content="colorpy.py">

%(body)s

"""

class Parser:
    def __init__(self, raw, out=sys.stdout):
        self.raw = string.strip(string.expandtabs(raw))
        self.out = out
        self._function = False
        self._format()

    def _print(self, x):
        self.out.write( x )

    def getstyle(self):
        return STYLE

    def getbody(self):
        return self.out.getvalue()

    def _format(self):
        """ Parse and send the colored source.
        """
        # store line offsets in self.lines
        self.lines = [0, 0]
        pos = 0
        while 1:
            pos = string.find(self.raw, '\n', pos) + 1
            if not pos: break
            self.lines.append(pos)
        self.lines.append(len(self.raw))

        # parse the source and write it
        self.pos = 0
        text = cStringIO.StringIO(self.raw)
        self._print('
\n')
        try:
            tokenize.tokenize(text.readline, self._token_cb)
        except tokenize.TokenError, ex:
            msg = ex[0]
            line = ex[1][0]
            self._print("
ERROR: %s
%s\n" % (
                msg, self.raw[self.lines[line]:]))
        self._print('\n
')

    def _token_cb(self, toktype, toktext, (srow,scol), (erow,ecol), line):
        # calculate new positions
        oldpos = self.pos
        newpos = self.lines[srow] + scol
        self.pos = newpos + len(toktext)

        # handle newlines
        if toktype in [token.NEWLINE, tokenize.NL]:
            self._print('\n')
            return

        # send the original whitespace, if needed
        if newpos > oldpos:
            self._print(self.raw[oldpos:newpos])

        # skip indenting tokens
        if toktype in [token.INDENT, token.DEDENT]:
            self.pos = newpos
            return

        # map token type to a color group
        if token.LPAR <= toktype and toktype <= token.OP:
            toktype = token.OP
        elif toktype == token.NAME:
            if keyword.iskeyword(toktext):
                toktype = _KEYWORD
                if toktext in ['class', 'def']:
                    self._function = True
            elif self._function:
                toktype = _FUNCTION
                self._function = False
        css_class = _css_classes.get(toktype, 'text')

        # send text
        self._print('' % (css_class,))
        self._print(cgi.escape(toktext))
        self._print('')

def colorize_file(file=None, outstream=sys.stdout):
    from os.path import basename
    source = open(file, 'U').read()
    filename = basename(file)
    from cStringIO import StringIO
    tout = StringIO()
    p = Parser(source, out=tout)
    outstream.write(template % \
        {'title': filename, 'style': STYLE_OBLIVION,
         'body' : tout.getvalue() })

if __name__ == "__main__":
    colorize_file(__file__)


text:
void
runtime·checkgoarm(void)
{
	// TODO(minux)
}

#pragma textflag NOSPLIT
int64
runtime·cputicks(void)
{
	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
	// TODO: need more entropy to better seed fastrand1.
	return runtime·nanotime();
}


text:
<hazelcast xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.7.xsd"
		   xmlns="http://www.hazelcast.com/schema/config"
		   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

	<map name="countries">
		<time-to-live-seconds>600</time-to-live-seconds>
		<max-size>200</max-size>
	</map>

	<cache name="countries">
		<eviction size="200"/>

		<statistics-enabled>true</statistics-enabled>
		<management-enabled>true</management-enabled>
	</cache>

	<network>
		<join>
			<tcp-ip enabled="false"/>
			<multicast enabled="false"/>
		</join>
	</network>

</hazelcast>


text:
<md-dialog aria-label="View Way">
    <form ng-cloak>
      <md-toolbar>

{{ viewWayCtrl.name }}

          <md-button class="md-icon-button" ng-click="viewWayCtrl.closeDialog()">
            <md-icon ng-md-icon icon="highlight_off"></md-icon>
          </md-button>

      </md-toolbar>

      <md-dialog-content layout-margin layout-padding layout="column">
        <md-content>
          <md-list layout="column">
            <md-subheader class="md-no-sticky">Way Details</md-subheader>
            <md-list-item layout="column" flex="none">
              {{viewWayCtrl.startLocation}}
              <md-icon ng-md-icon icon="arrow_downward"></md-icon>
              {{viewWayCtrl.endLocation}}

            </md-list-item>

            <md-list-item ng-show="viewWayCtrl.way.recurringDayOfWeek.length > 0" layout="column">

                <md-icon ng-md-icon icon="today"></md-icon>
                {{day}}

            </md-list-item>

            <md-list-item ng-show="viewWayCtrl.way.oneTimeDate" layout="column">

                <md-icon ng-md-icon icon="today"></md-icon>
                {{viewWayCtrl.oneTimeDate| date : "MMMM dd '''yy" }}

            </md-list-item>

            <md-list-item ng-show="viewWayCtrl.way.hour" layout="column">

                <md-icon ng-md-icon icon="access_time"></md-icon>
                {{viewWayCtrl.hour}}:{{viewWayCtrl.showMinutes}} {{viewWayCtrl.ampm}}

            </md-list-item>

            <md-divider></md-divider>

            <md-subheader class="md-no-sticky">Wayerz</md-subheader>
            <md-list-item layout="row"
              ng-repeat="wayer in viewWayCtrl.way.wayerz" md-colors="{{ viewWayCtrl.way.profileID === wayer._id ? {backgroundColor: 'swag-accent-300'} : '' }}" ng-click="viewWayCtrl.viewProfile($event, wayer)">

{{ viewWayCtrl.profile._id === wayer._id ? 'ME' : wayer.displayName}}

              <md-button
              ng-show="viewWayCtrl.isInWay && viewWayCtrl.profile._id !== wayer._id"
              ng-click="viewWayCtrl.leaveReview($event, wayer, viewWayCtrl.way)" class="md-icon-button md-warn">
                <md-icon ng-md-icon icon="grade"></md-icon>
              </md-button>
            </md-list-item>
          </md-list>
      </md-content>
    </md-dialog-content>

    <md-dialog-actions>
      <md-button ng-click="viewWayCtrl.joinSubmit()" class="md-primary md-raised" layout layout-align="center center" ng-show="viewWayCtrl.way.profileID !== viewWayCtrl.profile._id">
        Request to Join
        <md-progress-circular md-mode="indeterminate" ng-show="viewWayCtrl.isLoading" md-diameter="20" class="md-accent md-hue-1 float-left"></md-progress-circular>
      </md-button>

      <md-button ng-show="viewWayCtrl.way.profileID === viewWayCtrl.profile._id" ng-click="viewWayCtrl.editWay($event, true, viewWayCtrl.way)" class="md-primary">Edit</md-button>
    </md-dialog-actions>

  </form>
</md-dialog>


text:
import { Component } from "react";

import PropTypes from "prop-types";

import { FieldWithError, TextField, Select } from "components/Fields";

import { applyValidators, isRequired, isWholeNumber } from "utils/validation";

import { formLabel, bodySmall } from "uiTheme/radium/typography";

const DEFAULT_WIDTH = 200;

class ExtractMultipleList extends Component {
  static getFields() {
    return [
      "multiple.startIndex.value",
      "multiple.startIndex.direction",
      "multiple.endIndex.value",
      "multiple.endIndex.direction",
    ];
  }

  static validate(values) {
    return applyValidators(values, [
      isRequired("multiple.startIndex.value", "Start"),
      isWholeNumber("multiple.startIndex.value", "Start"),
      isRequired("multiple.endIndex.value", "End"),
      isWholeNumber("multiple.endIndex.value", "End"),
    ]);
  }

  static propTypes = {
    firstElementLabel: PropTypes.string,
    lastElementLabel: PropTypes.string,
    startIndex: PropTypes.object,
    endIndex: PropTypes.object,
  };

  constructor(props) {
    super(props);
    this.items = [
      {
        option: "Start",
        label: "Start",
      },
      {
        option: "End",
        label: "End",
      },
    ];
  }

  render() {
    const { startIndex, endIndex } = this.props;
    return (

              {this.props.firstElementLabel}

            <FieldWithError {...startIndex.value} errorPlacement="bottom">
              <TextField
                {...startIndex.value}
                type="number"
                style={styles.input}
                numberInputWrapperStyles={styles.numberInputWrapperStyles}
              />
            </FieldWithError>

            Relative To
            <Select
              items={this.items}
              {...startIndex.direction}
              style={styles.select}
            />

        {endIndex ? (

                {this.props.lastElementLabel}

              <FieldWithError {...endIndex.value} errorPlacement="bottom">
                <TextField
                  {...endIndex.value}
                  type="number"
                  style={styles.input}
                  numberInputWrapperStyles={styles.numberInputWrapperStyles}
                />
              </FieldWithError>

              Relative To
              <Select
                items={this.items}
                {...endIndex.direction}
                style={styles.select}
              />

        ) : null}

    );
  }
}

const styles = {
  item: {
    maxWidth: 200,
    marginLeft: 10,
    fontWeight: 400,
    marginTop: 5,
    display: "flex",
    flexDirection: "column",
    justifyContent: "flex-start",
  },
  largeItem: {
    marginTop: 0,
    display: "flex",
    backgroundColor: "rgb(255, 245, 220)",
  },
  input: {
    width: 180,
    height: 28,
    fontSize: 13,
    border: "1px solid #ccc",
    borderRadius: 3,
    outline: "none",
    marginRight: 0,
    backgroundColor: "none",
  },
  numberInputWrapperStyles: {
    background: "white",
    width: "inherit",
  },
  select: {
    width: DEFAULT_WIDTH,
    height: 24,
    background: "white",
    ...bodySmall,
  },
  font: {
    margin: 0,
  },
};
export default ExtractMultipleList;


text:
using System.Collections.Generic;
using System.Text.Json;
using Azure.Core;

namespace Azure.Maps.Rendering
{
    internal partial class MapAttribution
    {
        internal static MapAttribution DeserializeMapAttribution(JsonElement element)
        {
            Optional<IReadOnlyList<string>> copyrights = default;
            foreach (var property in element.EnumerateObject())
            {
                if (property.NameEquals("copyrights"))
                {
                    if (property.Value.ValueKind == JsonValueKind.Null)
                    {
                        property.ThrowNonNullablePropertyIsNull();
                        continue;
                    }
                    List<string> array = new List<string>();
                    foreach (var item in property.Value.EnumerateArray())
                    {
                        array.Add(item.GetString());
                    }
                    copyrights = array;
                    continue;
                }
            }
            return new MapAttribution(Optional.ToList(copyrights));
        }
    }
}


text:
module TVShowRenamer
  Version = VERSION = '0.5'
end


text:
@interface PodsDummy_Pods_HDImagePicker_HDImagePicker : NSObject
@end
@implementation PodsDummy_Pods_HDImagePicker_HDImagePicker
@end