1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.midi; 18 19 /** 20 * Uses multiple MidiEventSchedulers for waiting for events. 21 * 22 */ 23 public class MidiEventMultiScheduler { 24 private MultiLockMidiEventScheduler[] mMidiEventSchedulers; 25 private int mNumEventSchedulers; 26 private int mNumClosedSchedulers = 0; 27 private final Object mMultiLock = new Object(); 28 29 private class MultiLockMidiEventScheduler extends MidiEventScheduler { 30 @Override close()31 public void close() { 32 synchronized (mMultiLock) { 33 mNumClosedSchedulers++; 34 } 35 super.close(); 36 } 37 38 @Override getLock()39 protected Object getLock() { 40 return mMultiLock; 41 } 42 isEventBufferEmptyLocked()43 public boolean isEventBufferEmptyLocked() { 44 return mEventBuffer.isEmpty(); 45 } 46 getLowestTimeLocked()47 public long getLowestTimeLocked() { 48 return mEventBuffer.firstKey(); 49 } 50 } 51 52 /** 53 * MidiEventMultiScheduler constructor 54 * 55 * @param numSchedulers the number of schedulers to create 56 */ MidiEventMultiScheduler(int numSchedulers)57 public MidiEventMultiScheduler(int numSchedulers) { 58 mNumEventSchedulers = numSchedulers; 59 mMidiEventSchedulers = new MultiLockMidiEventScheduler[numSchedulers]; 60 for (int i = 0; i < numSchedulers; i++) { 61 mMidiEventSchedulers[i] = new MultiLockMidiEventScheduler(); 62 } 63 } 64 65 /** 66 * Waits for the next MIDI event. This will return true when it receives it. 67 * If all MidiEventSchedulers have been closed, this will return false. 68 * 69 * @return true if a MIDI event is received and false if all schedulers are closed. 70 */ waitNextEvent()71 public boolean waitNextEvent() throws InterruptedException { 72 synchronized (mMultiLock) { 73 while (true) { 74 if (mNumClosedSchedulers >= mNumEventSchedulers) { 75 return false; 76 } 77 long lowestTime = Long.MAX_VALUE; 78 long now = System.nanoTime(); 79 for (MultiLockMidiEventScheduler eventScheduler : mMidiEventSchedulers) { 80 if (!eventScheduler.isEventBufferEmptyLocked()) { 81 lowestTime = Math.min(lowestTime, 82 eventScheduler.getLowestTimeLocked()); 83 } 84 } 85 if (lowestTime <= now) { 86 return true; 87 } 88 long nanosToWait = lowestTime - now; 89 // Add 1 millisecond so we don't wake up before it is 90 // ready. 91 long millisToWait = 1 + (nanosToWait / EventScheduler.NANOS_PER_MILLI); 92 // Clip 64-bit value to 32-bit max. 93 if (millisToWait > Integer.MAX_VALUE) { 94 millisToWait = Integer.MAX_VALUE; 95 } 96 mMultiLock.wait(millisToWait); 97 } 98 } 99 } 100 101 /** 102 * Gets the number of MidiEventSchedulers. 103 * 104 * @return the number of MidiEventSchedulers. 105 */ getNumEventSchedulers()106 public int getNumEventSchedulers() { 107 return mNumEventSchedulers; 108 } 109 110 /** 111 * Gets a specific MidiEventScheduler based on the index. 112 * 113 * @param index the zero indexed index of a MIDI event scheduler 114 * @return a MidiEventScheduler 115 */ getEventScheduler(int index)116 public MidiEventScheduler getEventScheduler(int index) { 117 return mMidiEventSchedulers[index]; 118 } 119 120 /** 121 * Closes all event schedulers. 122 */ close()123 public void close() { 124 for (MidiEventScheduler eventScheduler : mMidiEventSchedulers) { 125 eventScheduler.close(); 126 } 127 } 128 } 129