| /* |
| * Copyright 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.mobileer.oboetester; |
| |
| import android.Manifest; |
| import android.content.pm.PackageManager; |
| import android.media.midi.MidiDevice; |
| import android.media.midi.MidiDeviceInfo; |
| import android.media.midi.MidiInputPort; |
| import android.media.midi.MidiManager; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.util.Log; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.widget.Button; |
| import android.widget.Toast; |
| |
| import com.mobileer.miditools.MidiOutputPortConnectionSelector; |
| import com.mobileer.miditools.MidiPortConnector; |
| import com.mobileer.miditools.MidiTools; |
| |
| import java.io.IOException; |
| import java.sql.Timestamp; |
| |
| import static com.mobileer.oboetester.MidiTapTester.NoteListener; |
| |
| public class TapToToneActivity extends TestOutputActivityBase { |
| // Names from obsolete version of Oboetester. |
| public static final String OLD_PRODUCT_NAME = "AudioLatencyTester"; |
| public static final String OLD_MANUFACTURER_NAME = "AndroidTest"; |
| |
| private MidiManager mMidiManager; |
| private MidiInputPort mInputPort; |
| |
| protected MidiTapTester mMidiTapTester; |
| protected TapToToneTester mTapToToneTester; |
| |
| private Button mStopButton; |
| private Button mStartButton; |
| |
| private MidiOutputPortConnectionSelector mPortSelector; |
| private final MyNoteListener mTestListener = new MyNoteListener(); |
| |
| @Override |
| protected void inflateActivity() { |
| setContentView(R.layout.activity_tap_to_tone); |
| } |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| mAudioOutTester = addAudioOutputTester(); |
| |
| mTapToToneTester = new TapToToneTester(this, |
| getResources().getString(R.string.tap_to_tone_instructions)); |
| |
| if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI)) { |
| setupMidi(); |
| } else { |
| Toast.makeText(TapToToneActivity.this, |
| "MIDI not supported!", Toast.LENGTH_LONG) |
| .show(); |
| } |
| |
| |
| // Start a blip test when the waveform view is tapped. |
| WaveformView mWaveformView = (WaveformView) findViewById(R.id.waveview_audio); |
| mWaveformView.setOnTouchListener((view, event) -> { |
| // Do not call view.performClick() because it may trigger a touch sound! |
| int action = event.getActionMasked(); |
| switch (action) { |
| case MotionEvent.ACTION_DOWN: |
| case MotionEvent.ACTION_POINTER_DOWN: |
| trigger(); |
| break; |
| case MotionEvent.ACTION_MOVE: |
| break; |
| case MotionEvent.ACTION_UP: |
| case MotionEvent.ACTION_POINTER_UP: |
| break; |
| } |
| // Must return true or we do not get the ACTION_MOVE and |
| // ACTION_UP events. |
| return true; |
| }); |
| |
| mStartButton = (Button) findViewById(R.id.button_start); |
| mStopButton = (Button) findViewById(R.id.button_stop); |
| updateButtons(false); |
| |
| updateEnabledWidgets(); |
| } |
| |
| private void updateButtons(boolean running) { |
| mStartButton.setEnabled(!running); |
| mStopButton.setEnabled(running); |
| } |
| |
| void trigger() { |
| if (mTapToToneTester.isArmed()) { |
| mAudioOutTester.trigger(); |
| mTapToToneTester.analyzeLater(getString(R.string.please_wait)); |
| Timestamp timestamp = new Timestamp(System.currentTimeMillis()); |
| Log.d(TAG, "Tap to Tone Triggered. Timestamp: " + timestamp); |
| } else { |
| showToast(getString(R.string.no_double_tap)); |
| } |
| } |
| |
| @Override |
| int getActivityType() { |
| return ACTIVITY_TAP_TO_TONE; |
| } |
| |
| @Override |
| protected void onDestroy() { |
| mMidiTapTester.removeTestListener(mTestListener); |
| closeMidiResources(); |
| super.onDestroy(); |
| } |
| |
| private void setupMidi() { |
| // Setup MIDI |
| mMidiManager = (MidiManager) getSystemService(MIDI_SERVICE); |
| MidiDeviceInfo[] infos = mMidiManager.getDevices(); |
| |
| // Warn if old version of OboeTester found. |
| for (MidiDeviceInfo info : infos) { |
| Log.i(TAG, "MIDI info = " + info); |
| Bundle properties = info.getProperties(); |
| String product = properties |
| .getString(MidiDeviceInfo.PROPERTY_PRODUCT); |
| String manufacturer = properties |
| .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER); |
| if (OLD_PRODUCT_NAME.equals(product) && OLD_MANUFACTURER_NAME.equals(manufacturer)) { |
| showErrorToast("Please uninstall old version of OboeTester."); |
| break; |
| } |
| } |
| |
| // Open the port now so that the MidiTapTester gets created. |
| for (MidiDeviceInfo info : infos) { |
| Bundle properties = info.getProperties(); |
| String product = properties |
| .getString(MidiDeviceInfo.PROPERTY_PRODUCT); |
| if (MidiTapTester.PRODUCT_NAME.equals(product)) { |
| String manufacturer = properties |
| .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER); |
| if (MidiTapTester.MANUFACTURER_NAME.equals(manufacturer)) { |
| openPortTemporarily(info); |
| break; |
| } |
| } |
| } |
| } |
| |
| // These should only be set after mAudioMidiTester is set. |
| private void setSpinnerListeners() { |
| MidiDeviceInfo synthInfo = MidiTools.findDevice(mMidiManager, MidiTapTester.MANUFACTURER_NAME, |
| MidiTapTester.PRODUCT_NAME); |
| Log.i(TAG, "found tester virtual device info: " + synthInfo); |
| int portIndex = 0; |
| mPortSelector = new MidiOutputPortConnectionSelector(mMidiManager, this, |
| R.id.spinner_synth_sender, synthInfo, portIndex); |
| mPortSelector.setConnectedListener(new MyPortsConnectedListener()); |
| |
| } |
| |
| private class MyNoteListener implements NoteListener { |
| @Override |
| public void onNoteOn(final int pitch) { |
| runOnUiThread(() -> { |
| trigger(); |
| mStreamContexts.get(0).configurationView.setStatusText("MIDI pitch = " + pitch); |
| }); |
| } |
| } |
| |
| private void openPortTemporarily(final MidiDeviceInfo info) { |
| Log.i(TAG, "MIDI openPort() info = " + info); |
| mMidiManager.openDevice(info, device -> { |
| if (device == null) { |
| Log.e(TAG, "could not open device " + info); |
| } else { |
| mInputPort = device.openInputPort(0); |
| Log.i(TAG, "opened MIDI port = " + mInputPort + " on " + info); |
| mMidiTapTester = MidiTapTester.getInstanceOrNull(); |
| if (mMidiTapTester == null) { |
| Log.e(TAG, "MidiTapTester Service was not created! info = " + info); |
| showErrorToast("MidiTapTester Service was not created!"); |
| } else { |
| Log.i(TAG, "openPort() mMidiTapTester = " + mMidiTapTester); |
| // Now that we have created the MidiTapTester, close the port so we can |
| // open it later. |
| try { |
| mInputPort.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| mMidiTapTester.addTestListener(mTestListener); |
| setSpinnerListeners(); |
| } |
| } |
| }, new Handler(Looper.getMainLooper()) |
| ); |
| } |
| |
| // TODO Listen to the synth server |
| // for open/close events and then disable/enable the spinner. |
| private class MyPortsConnectedListener |
| implements MidiPortConnector.OnPortsConnectedListener { |
| @Override |
| public void onPortsConnected(final MidiDevice.MidiConnection connection) { |
| Log.i(TAG, "onPortsConnected, connection = " + connection); |
| runOnUiThread(() -> { |
| if (connection == null) { |
| Toast.makeText(TapToToneActivity.this, |
| R.string.error_port_busy, Toast.LENGTH_LONG) |
| .show(); |
| mPortSelector.clearSelection(); |
| } else { |
| Toast.makeText(TapToToneActivity.this, |
| R.string.port_open_ok, Toast.LENGTH_LONG) |
| .show(); |
| } |
| }); |
| } |
| } |
| |
| private void closeMidiResources() { |
| if (mPortSelector != null) { |
| mPortSelector.close(); |
| } |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| // Inflate the menu; this adds items to the action bar if it is present. |
| getMenuInflater().inflate(R.menu.menu_main, menu); |
| return true; |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| // Handle action bar item clicks here. The action bar will |
| // automatically handle clicks on the Home/Up button, so long |
| // as you specify a parent activity in AndroidManifest.xml. |
| int id = item.getItemId(); |
| |
| //noinspection SimplifiableIfStatement |
| if (id == R.id.action_settings) { |
| return true; |
| } |
| |
| return super.onOptionsItemSelected(item); |
| } |
| |
| public void startTest(View view) { |
| try { |
| openAudio(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| showErrorToast("Open audio failed!"); |
| return; |
| } |
| try { |
| super.startAudio(); |
| mTapToToneTester.resetLatency(); |
| mTapToToneTester.start(); |
| updateButtons(true); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| showErrorToast("Start audio failed! " + e.getMessage()); |
| return; |
| } |
| } |
| |
| public void stopTest(View view) { |
| mTapToToneTester.stop(); |
| stopAudio(); |
| closeAudio(); |
| updateButtons(false); |
| } |
| } |