publicstatic UsbMidiDevice create(Context context, Bundle properties, int card, int device) { // FIXME - support devices with different number of input and output ports intsubDeviceCount= nativeGetSubdeviceCount(card, device); 1.1// 构造UsbMidiDevice UsbMidiDevicemidiDevice=newUsbMidiDevice(card, device, subDeviceCount); 1.2// 注册midi服务 if (!midiDevice.register(context, properties)) { } return midiDevice; }
publicvoidonDeviceStatusChanged(final MidiDeviceStatus status) { // If an input port becomes busy then remove it from the menu. // If it becomes free then add it back to the menu. if (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) { MidiDeviceInfoinfo= status.getDeviceInfo(); Log.i(MidiConstants.TAG, "MidiPortSelector.onDeviceStatusChanged status = " + status + ", mType = " + mType + ", activity = " + mActivity.getPackageName() + ", info = " + info); // Look for transitions from free to busy. intportCount= info.getInputPortCount(); for (inti=0; i < portCount; ++i) { MidiPortWrapperwrapper=newMidiPortWrapper(info, mType, i); if (!wrapper.equals(mCurrentWrapper)) { if (status.isInputPortOpen(i)) { // busy? if (!mBusyPorts.contains(wrapper)) { // UI相关的变化 // was free, now busy mBusyPorts.add(wrapper); mAdapter.remove(wrapper); mAdapter.notifyDataSetChanged(); } } else { if (mBusyPorts.remove(wrapper)) { // was busy, now free mAdapter.add(wrapper); mAdapter.notifyDataSetChanged(); } } } } } }
@Override public FileDescriptor openInputPort(IBinder token, int portNumber) { if (mDeviceInfo.isPrivate()) { if (Binder.getCallingUid() != Process.myUid()) { thrownewSecurityException("Can't access private device from different UID"); } }
if (portNumber < 0 || portNumber >= mInputPortCount) { Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber); returnnull; }
3.1-> // 更新所有server绑定的device状态 privatevoidupdateDeviceStatus() { // clear calling identity, since we may be in a Binder call from one of our clients longidentityToken= Binder.clearCallingIdentity();
privatebooleanopenLocked() { // FIXME - support devices with different number of input and output ports FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount); if (fileDescriptors == null) { Log.e(TAG, "nativeOpen failed"); returnfalse; }
mFileDescriptors = fileDescriptors; intinputStreamCount= fileDescriptors.length; // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll() // in our input thread intoutputStreamCount= fileDescriptors.length - 1;
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
// Create input thread which will read from all output ports of the physical device newThread("UsbMidiDevice input thread") { @Override publicvoidrun() { byte[] buffer = newbyte[BUFFER_SIZE]; try { while (true) { // Record time of event immediately after waking. longtimestamp= System.nanoTime(); synchronized (mLock) { if (!mIsOpen) break;
// look for a readable FileDescriptor for (intindex=0; index < mPollFDs.length; index++) { StructPollfdpfd= mPollFDs[index]; if ((pfd.revents & (OsConstants.POLLERR | OsConstants.POLLHUP)) != 0) { break; } elseif ((pfd.revents & OsConstants.POLLIN) != 0) { // clear readable flag pfd.revents = 0;
if (index == mInputStreams.length - 1) { // last file descriptor is used only for unblocking Os.poll() break; }
// wait until we have a readable port or we are signalled to close Os.poll(mPollFDs, -1/* infinite timeout */); } } catch (IOException e) { Log.d(TAG, "reader thread exiting"); } catch (ErrnoException e) { Log.d(TAG, "reader thread exiting"); } Log.d(TAG, "input thread exit"); } }.start();
// Create output thread for each input port of the physical device for (intport=0; port < outputStreamCount; port++) { finalMidiEventSchedulereventSchedulerF= mEventSchedulers[port]; finalFileOutputStreamoutputStreamF= mOutputStreams[port]; finalintportF= port;
@startuml abstract class MidiReceiver { + void send(byte[] msg, int offset, int count) + void onSend(byte[] msg, int offset, int count, long timestamp) } abstract class MidiSender{ + void onConnect(MidiReceiver receiver) + void onDisconnect(MidiReceiver receiver) }
MidiSender --o MidiDispatcher class MidiDispatcher extends MidiReceiver { MidiSender mSender MidiReceiverFailureHandler mFailureHandler List<MidiReceiver> <b>mReceivers + MidiSender getSender() + void <b>onSend(byte[] msg, int offset, int count, long timestamp) +int getReceiverCount() } MidiReceiver --* MidiDispatcher MidiDispatcher --o MidiOutputPort
class MidiOutputPort <<<i><u>receiving data from a port on a MIDI device>> { MidiDispatcher mDispatcher + void <b>onConnect(MidiReceiver receiver) + void onDisconnect(MidiReceiver receiver) close() } MidiOutputPort -u|> MidiSender MidiReceiverFailureHandler -d-o MidiDispatcher interface MidiReceiverFailureHandler{ void onReceiverFailure(MidiReceiver receiver, IOException failure) }
class MidiInputPort <<<i><u>for sending data to a port on a MIDI device>> { FileDescriptor mFileDescriptor +void onSend(byte[] msg, int offset, int count, long timestamp) +void close() }
MidiInputPort -|> MidiReceiver abstract class PortClient { void close() }
class InputPortClient extends PortClient class OutputPortClient extends PortClient { MidiInputPort mInputPort void close() MidiInputPort getInputPort() }