├── LyrePlayer ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── top │ │ │ └── weixiansen574 │ │ │ └── LyrePlayer │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ └── ResolutionCoordinateMapping.json │ │ ├── java │ │ │ ├── com │ │ │ │ └── sun │ │ │ │ │ └── media │ │ │ │ │ └── sound │ │ │ │ │ ├── FastShortMessage.java │ │ │ │ │ ├── MidiUtils.java │ │ │ │ │ ├── StandardMidiFileReader.java │ │ │ │ │ └── StandardMidiFileWriter.java │ │ │ ├── javax │ │ │ │ └── sound │ │ │ │ │ └── midi │ │ │ │ │ ├── ControllerEventListener.java │ │ │ │ │ ├── InvalidMidiDataException.java │ │ │ │ │ ├── MetaEventListener.java │ │ │ │ │ ├── MetaMessage.java │ │ │ │ │ ├── MidiChannel.java │ │ │ │ │ ├── MidiDevice.java │ │ │ │ │ ├── MidiDeviceReceiver.java │ │ │ │ │ ├── MidiDeviceTransmitter.java │ │ │ │ │ ├── MidiEvent.java │ │ │ │ │ ├── MidiFileFormat.java │ │ │ │ │ ├── MidiMessage.java │ │ │ │ │ ├── MidiSystem.java │ │ │ │ │ ├── MidiUnavailableException.java │ │ │ │ │ ├── Patch.java │ │ │ │ │ ├── Receiver.java │ │ │ │ │ ├── Sequence.java │ │ │ │ │ ├── Sequencer.java │ │ │ │ │ ├── ShortMessage.java │ │ │ │ │ ├── SysexMessage.java │ │ │ │ │ ├── Track.java │ │ │ │ │ ├── Transmitter.java │ │ │ │ │ ├── VoiceStatus.java │ │ │ │ │ ├── package-info.java │ │ │ │ │ └── spi │ │ │ │ │ ├── MidiDeviceProvider.java │ │ │ │ │ ├── MidiFileReader.java │ │ │ │ │ ├── MidiFileWriter.java │ │ │ │ │ └── package-info.java │ │ │ └── top │ │ │ │ └── weixiansen574 │ │ │ │ └── LyrePlayer │ │ │ │ ├── AboutActivity.java │ │ │ │ ├── AdjustAndStartActivity.java │ │ │ │ ├── ClickService.java │ │ │ │ ├── FileHelp.java │ │ │ │ ├── FloatListActivity.java │ │ │ │ ├── FloatListManager.java │ │ │ │ ├── FloatingButtonService.java │ │ │ │ ├── MD5.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MusicListAdapter.java │ │ │ │ ├── NetworkHelpActivity.java │ │ │ │ ├── SelecFromServerActivity.java │ │ │ │ ├── adapter │ │ │ │ ├── FLoatMusicListAdapter.java │ │ │ │ └── MusicListAdapterForOnline.java │ │ │ │ ├── enums │ │ │ │ ├── InvalidKeySetting.java │ │ │ │ └── MusicInstrumentType.java │ │ │ │ ├── midi │ │ │ │ ├── FloatMusicBean.java │ │ │ │ ├── MidiProcessor.java │ │ │ │ ├── Note.java │ │ │ │ └── SequenceOfNotes.java │ │ │ │ └── util │ │ │ │ ├── AccessibilityUtil.java │ │ │ │ ├── HttpUtil.java │ │ │ │ ├── NoteListStorage.java │ │ │ │ ├── RootUtils.java │ │ │ │ └── ShellUtils.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── test_ipv6.jpg │ │ │ ├── drawable │ │ │ ├── file_help_1.jpg │ │ │ ├── file_help_2.jpg │ │ │ ├── file_help_3.jpg │ │ │ ├── ic_delete.xml │ │ │ ├── ic_floder.xml │ │ │ ├── ic_list.xml │ │ │ ├── ic_menu.xml │ │ │ ├── ic_music.xml │ │ │ ├── ic_search.xml │ │ │ ├── ic_server.xml │ │ │ ├── ic_user.xml │ │ │ ├── key_map.jpg │ │ │ ├── logo.png │ │ │ ├── lrye_round_icon.png │ │ │ ├── lyre.png │ │ │ ├── old_lyre.png │ │ │ ├── old_lyre_round_icon.png │ │ │ └── upload.png │ │ │ ├── layout │ │ │ ├── activity_about.xml │ │ │ ├── activity_adjust_and_start.xml │ │ │ ├── activity_adjust_and_start_new.xml │ │ │ ├── activity_file_help.xml │ │ │ ├── activity_float_list.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_network_help.xml │ │ │ ├── activity_selec_from_server.xml │ │ │ ├── edit_text.xml │ │ │ ├── float_layout.xml │ │ │ ├── float_music_list.xml │ │ │ ├── foot_view.xml │ │ │ ├── foot_view_page.xml │ │ │ ├── item_float_window_music.xml │ │ │ ├── list_item.xml │ │ │ ├── list_item_online_music.xml │ │ │ ├── online_music_info_dialog.xml │ │ │ └── upload_dialog.xml │ │ │ ├── menu │ │ │ ├── menu.xml │ │ │ └── menu_search.xml │ │ │ ├── values-land │ │ │ └── dimens.xml │ │ │ ├── values-w1240dp │ │ │ └── dimens.xml │ │ │ ├── values-w600dp │ │ │ └── dimens.xml │ │ │ ├── values-zh-rTW │ │ │ └── strings.xml │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ ├── accessibility_service_config.xml │ │ │ └── network_security_config.xml │ │ └── test │ │ └── java │ │ └── top │ │ └── weixiansen574 │ │ └── LyrePlayer │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── README.md ├── output.json └── screenshot.jpg /LyrePlayer/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /LyrePlayer/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /LyrePlayer/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 30 5 | buildToolsVersion '32.0.0' 6 | defaultConfig { 7 | applicationId "top.weixiansen574.LyrePlayer" 8 | minSdkVersion 24 9 | targetSdkVersion 30 10 | versionCode 500 11 | versionName "5.0.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | implementation 'androidx.appcompat:appcompat:1.0.2' 29 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 30 | implementation 'androidx.navigation:navigation-fragment:2.3.5' 31 | implementation 'androidx.navigation:navigation-ui:2.3.5' 32 | implementation 'androidx.cardview:cardview:1.0.0' 33 | implementation 'com.squareup.okhttp3:okhttp:3.10.0' 34 | implementation 'com.alibaba:fastjson:1.2.79' 35 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' 36 | 37 | } 38 | repositories { 39 | mavenCentral() 40 | } 41 | -------------------------------------------------------------------------------- /LyrePlayer/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/androidTest/java/top/weixiansen574/LyrePlayer/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("top.weixiansen574.LyrePlayer", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 24 | 29 | 34 | 39 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | 69 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/assets/ResolutionCoordinateMapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "2400*1080": { 3 | "x1": 570, 4 | "x2": 775, 5 | "x3": 990, 6 | "x4": 1195, 7 | "x5": 1410, 8 | "x6": 1615, 9 | "x7": 1828, 10 | "y1": 575, 11 | "y2": 740, 12 | "y3": 910 13 | }, 14 | "2340*1080": { 15 | "x1": 540, 16 | "x2": 750, 17 | "x3": 964, 18 | "x4": 1170, 19 | "x5": 1380, 20 | "x6": 1590, 21 | "x7": 1800, 22 | "y1": 575, 23 | "y2": 740, 24 | "y3": 910 25 | }, 26 | "2248*1080": { 27 | "x1": 500, 28 | "x2": 700, 29 | "x3": 915, 30 | "x4": 1125, 31 | "x5": 1335, 32 | "x6": 1545, 33 | "x7": 1755, 34 | "y1": 620, 35 | "y2": 790, 36 | "y3": 960 37 | }, 38 | "1920*1080": { 39 | "x1": 335, 40 | "x2": 540, 41 | "x3": 750, 42 | "x4": 960, 43 | "x5": 1170, 44 | "x6": 1380, 45 | "x7": 1590, 46 | "y1": 573, 47 | "y2": 740, 48 | "y3": 910 49 | }, 50 | "3200*1440": { 51 | "x1": 765, 52 | "x2": 1045, 53 | "x3": 1325, 54 | "x4": 1600, 55 | "x5": 1880, 56 | "x6": 2160, 57 | "x7": 2440, 58 | "y1": 765, 59 | "y2": 990, 60 | "y3": 1215 61 | }, 62 | "2560*1600": { 63 | "x1": 443, 64 | "x2": 725, 65 | "x3": 1000, 66 | "x4": 1280, 67 | "x5": 1560, 68 | "x6": 1840, 69 | "x7": 2120, 70 | "y1": 925, 71 | "y2": 1145, 72 | "y3": 1375 73 | } 74 | } -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/com/sun/media/sound/FastShortMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package com.sun.media.sound; 27 | 28 | import javax.sound.midi.InvalidMidiDataException; 29 | import javax.sound.midi.ShortMessage; 30 | 31 | /** 32 | * an optimized ShortMessage that does not need an array. 33 | * 34 | * @author Florian Bomers 35 | */ 36 | final class FastShortMessage extends ShortMessage { 37 | private int packedMsg; 38 | 39 | FastShortMessage(int packedMsg) throws InvalidMidiDataException { 40 | this.packedMsg = packedMsg; 41 | getDataLength(packedMsg & 0xFF); // to check for validity 42 | } 43 | 44 | /** Creates a FastShortMessage from this ShortMessage */ 45 | FastShortMessage(ShortMessage msg) { 46 | this.packedMsg = msg.getStatus() 47 | | (msg.getData1() << 8) 48 | | (msg.getData2() << 16); 49 | } 50 | 51 | int getPackedMsg() { 52 | return packedMsg; 53 | } 54 | 55 | @Override 56 | public byte[] getMessage() { 57 | int length = 0; 58 | try { 59 | // fix for bug 4851018: MidiMessage.getLength and .getData return wrong values 60 | // fix for bug 4890405: Reading MidiMessage byte array fails in 1.4.2 61 | length = getDataLength(packedMsg & 0xFF) + 1; 62 | } catch (InvalidMidiDataException imde) { 63 | // should never happen 64 | } 65 | byte[] returnedArray = new byte[length]; 66 | if (length>0) { 67 | returnedArray[0] = (byte) (packedMsg & 0xFF); 68 | if (length>1) { 69 | returnedArray[1] = (byte) ((packedMsg & 0xFF00) >> 8); 70 | if (length>2) { 71 | returnedArray[2] = (byte) ((packedMsg & 0xFF0000) >> 16); 72 | } 73 | } 74 | } 75 | return returnedArray; 76 | } 77 | 78 | @Override 79 | public int getLength() { 80 | try { 81 | return getDataLength(packedMsg & 0xFF) + 1; 82 | } catch (InvalidMidiDataException imde) { 83 | // should never happen 84 | } 85 | return 0; 86 | } 87 | 88 | @Override 89 | public void setMessage(int status) throws InvalidMidiDataException { 90 | // check for valid values 91 | int dataLength = getDataLength(status); // can throw InvalidMidiDataException 92 | if (dataLength != 0) { 93 | super.setMessage(status); // throws Exception 94 | } 95 | packedMsg = (packedMsg & 0xFFFF00) | (status & 0xFF); 96 | } 97 | 98 | @Override 99 | public void setMessage(int status, int data1, int data2) throws InvalidMidiDataException { 100 | getDataLength(status); // can throw InvalidMidiDataException 101 | packedMsg = (status & 0xFF) | ((data1 & 0xFF) << 8) | ((data2 & 0xFF) << 16); 102 | } 103 | 104 | @Override 105 | public void setMessage(int command, int channel, int data1, int data2) throws InvalidMidiDataException { 106 | getDataLength(command); // can throw InvalidMidiDataException 107 | packedMsg = (command & 0xF0) | (channel & 0x0F) | ((data1 & 0xFF) << 8) | ((data2 & 0xFF) << 16); 108 | } 109 | 110 | @Override 111 | public int getChannel() { 112 | return packedMsg & 0x0F; 113 | } 114 | 115 | @Override 116 | public int getCommand() { 117 | return packedMsg & 0xF0; 118 | } 119 | 120 | @Override 121 | public int getData1() { 122 | return (packedMsg & 0xFF00) >> 8; 123 | } 124 | 125 | @Override 126 | public int getData2() { 127 | return (packedMsg & 0xFF0000) >> 16; 128 | } 129 | 130 | @Override 131 | public int getStatus() { 132 | return packedMsg & 0xFF; 133 | } 134 | 135 | /** 136 | * Creates a new object of the same class and with the same contents 137 | * as this object. 138 | * @return a clone of this instance. 139 | */ 140 | @Override 141 | public Object clone() { 142 | try { 143 | return new FastShortMessage(packedMsg); 144 | } catch (InvalidMidiDataException imde) { 145 | // should never happen 146 | } 147 | return null; 148 | } 149 | 150 | } // class FastShortMsg 151 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/ControllerEventListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | import java.util.EventListener; 29 | 30 | /** 31 | * The {@code ControllerEventListener} interface should be implemented by 32 | * classes whose instances need to be notified when a {@link Sequencer} has 33 | * processed a requested type of MIDI control-change event. To register a 34 | * {@code ControllerEventListener} object to receive such notifications, invoke 35 | * the 36 | * {@link Sequencer#addControllerEventListener(ControllerEventListener, int[]) 37 | * addControllerEventListener} method of {@code Sequencer}, specifying the types 38 | * of MIDI controllers about which you are interested in getting control-change 39 | * notifications. 40 | * 41 | * @author Kara Kytle 42 | * @see MidiChannel#controlChange(int, int) 43 | */ 44 | public interface ControllerEventListener extends EventListener { 45 | 46 | /** 47 | * Invoked when a {@link Sequencer} has encountered and processed a 48 | * control-change event of interest to this listener. The event passed in is 49 | * a {@code ShortMessage} whose first data byte indicates the controller 50 | * number and whose second data byte is the value to which the controller 51 | * was set. 52 | * 53 | * @param event the control-change event that the sequencer encountered in 54 | * the sequence it is processing 55 | * @see Sequencer#addControllerEventListener(ControllerEventListener, int[]) 56 | * @see MidiChannel#controlChange(int, int) 57 | * @see ShortMessage#getData1 58 | * @see ShortMessage#getData2 59 | */ 60 | void controlChange(ShortMessage event); 61 | } 62 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/InvalidMidiDataException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * An {@code InvalidMidiDataException} indicates that inappropriate MIDI data 30 | * was encountered. This often means that the data is invalid in and of itself, 31 | * from the perspective of the MIDI specification. An example would be an 32 | * undefined status byte. However, the exception might simply mean that the data 33 | * was invalid in the context it was used, or that the object to which the data 34 | * was given was unable to parse or use it. For example, a file reader might not 35 | * be able to parse a Type 2 MIDI file, even though that format is defined in 36 | * the MIDI specification. 37 | * 38 | * @author Kara Kytle 39 | */ 40 | public class InvalidMidiDataException extends Exception { 41 | 42 | /** 43 | * Use serialVersionUID from JDK 1.3 for interoperability. 44 | */ 45 | private static final long serialVersionUID = 2780771756789932067L; 46 | 47 | /** 48 | * Constructs an {@code InvalidMidiDataException} with {@code null} for its 49 | * error detail message. 50 | */ 51 | public InvalidMidiDataException() { 52 | super(); 53 | } 54 | 55 | /** 56 | * Constructs an {@code InvalidMidiDataException} with the specified detail 57 | * message. 58 | * 59 | * @param message the string to display as an error detail message 60 | */ 61 | public InvalidMidiDataException(final String message) { 62 | super(message); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MetaEventListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | import java.util.EventListener; 29 | 30 | /** 31 | * The {@code MetaEventListener} interface should be implemented by classes 32 | * whose instances need to be notified when a {@link Sequencer} has processed a 33 | * {@link MetaMessage}. To register a {@code MetaEventListener} object to 34 | * receive such notifications, pass it as the argument to the 35 | * {@link Sequencer#addMetaEventListener(MetaEventListener) 36 | * addMetaEventListener} method of {@code Sequencer}. 37 | * 38 | * @author Kara Kytle 39 | */ 40 | public interface MetaEventListener extends EventListener { 41 | 42 | /** 43 | * Invoked when a {@link Sequencer} has encountered and processed a 44 | * {@code MetaMessage} in the {@code Sequence} it is processing. 45 | * 46 | * @param meta the meta-message that the sequencer encountered 47 | */ 48 | void meta(MetaMessage meta); 49 | } 50 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MetaMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * A {@code MetaMessage} is a {@link MidiMessage} that is not meaningful to 30 | * synthesizers, but that can be stored in a MIDI file and interpreted by a 31 | * sequencer program. (See the discussion in the {@code MidiMessage} class 32 | * description.) The Standard MIDI Files specification defines various types of 33 | * meta-events, such as sequence number, lyric, cue point, and set tempo. There 34 | * are also meta-events for such information as lyrics, copyrights, tempo 35 | * indications, time and key signatures, markers, etc. For more information, see 36 | * the Standard MIDI Files 1.0 specification, which is part of the Complete MIDI 37 | * 1.0 Detailed Specification published by the MIDI Manufacturer's Association 38 | * (http://www.midi.org). 39 | *

40 | * When data is being transported using MIDI wire protocol, a 41 | * {@link ShortMessage} with the status value {@code 0xFF} represents a system 42 | * reset message. In MIDI files, this same status value denotes a 43 | * {@code MetaMessage}. The types of meta-message are distinguished from each 44 | * other by the first byte that follows the status byte {@code 0xFF}. The 45 | * subsequent bytes are data bytes. As with system exclusive messages, there are 46 | * an arbitrary number of data bytes, depending on the type of 47 | * {@code MetaMessage}. 48 | * 49 | * @author David Rivas 50 | * @author Kara Kytle 51 | * @see MetaEventListener 52 | */ 53 | public class MetaMessage extends MidiMessage { 54 | 55 | /** 56 | * Status byte for {@code MetaMessage} (0xFF, or 255), which is used in MIDI 57 | * files. It has the same value as {@link ShortMessage#SYSTEM_RESET}, which 58 | * is used in the real-time "MIDI wire" protocol. 59 | * 60 | * @see MidiMessage#getStatus 61 | */ 62 | public static final int META = 0xFF; // 255 63 | 64 | /** 65 | * The length of the actual message in the data array. This is used to 66 | * determine how many bytes of the data array is the message, and how many 67 | * are the status byte, the type byte, and the variable-length-int 68 | * describing the length of the message. 69 | */ 70 | private int dataLength = 0; 71 | 72 | /** 73 | * Constructs a new {@code MetaMessage}. The contents of the message are not 74 | * set here; use {@link #setMessage(int, byte[], int) setMessage} to set 75 | * them subsequently. 76 | */ 77 | public MetaMessage() { 78 | // Default meta message data: just the META status byte value 79 | this(new byte[]{(byte) META, 0}); 80 | } 81 | 82 | /** 83 | * Constructs a new {@code MetaMessage} and sets the message parameters. The 84 | * contents of the message can be changed by using the {@code setMessage} 85 | * method. 86 | * 87 | * @param type meta-message type (must be less than 128) 88 | * @param data the data bytes in the MIDI message 89 | * @param length an amount of bytes in the {@code data} byte array; it 90 | * should be non-negative and less than or equal to 91 | * {@code data.length} 92 | * @throws InvalidMidiDataException if the parameter values do not specify a 93 | * valid MIDI meta message 94 | * @see #setMessage(int, byte[], int) 95 | * @see #getType() 96 | * @see #getData() 97 | * @since 1.7 98 | */ 99 | public MetaMessage(int type, byte[] data, int length) 100 | throws InvalidMidiDataException { 101 | super(null); 102 | setMessage(type, data, length); // can throw InvalidMidiDataException 103 | } 104 | 105 | /** 106 | * Constructs a new {@code MetaMessage}. 107 | * 108 | * @param data an array of bytes containing the complete message. The 109 | * message data may be changed using the {@code setMessage} method. 110 | * @see #setMessage 111 | */ 112 | protected MetaMessage(byte[] data) { 113 | super(data); 114 | //$$fb 2001-10-06: need to calculate dataLength. Fix for bug #4511796 115 | if (data.length>=3) { 116 | dataLength=data.length-3; 117 | int pos=2; 118 | while (pos 131 | * The {@code type} argument should be a valid value for the byte that 132 | * follows the status byte in the {@code MetaMessage}. The {@code data} 133 | * argument should contain all the subsequent bytes of the 134 | * {@code MetaMessage}. In other words, the byte that specifies the type of 135 | * {@code MetaMessage} is not considered a data byte. 136 | * 137 | * @param type meta-message type (must be less than 128) 138 | * @param data the data bytes in the MIDI message 139 | * @param length the number of bytes in the {@code data} byte array 140 | * @throws InvalidMidiDataException if the parameter values do not specify a 141 | * valid MIDI meta message 142 | */ 143 | public void setMessage(int type, byte[] data, int length) throws InvalidMidiDataException { 144 | 145 | if (type >= 128 || type < 0) { 146 | throw new InvalidMidiDataException("Invalid meta event with type " + type); 147 | } 148 | if ((length > 0 && length > data.length) || length < 0) { 149 | throw new InvalidMidiDataException("length out of bounds: "+length); 150 | } 151 | 152 | this.length = 2 + getVarIntLength(length) + length; 153 | this.dataLength = length; 154 | this.data = new byte[this.length]; 155 | this.data[0] = (byte) META; // status value for MetaMessages (meta events) 156 | this.data[1] = (byte) type; // MetaMessage type 157 | writeVarInt(this.data, 2, length); // write the length as a variable int 158 | if (length > 0) { 159 | System.arraycopy(data, 0, this.data, this.length - this.dataLength, this.dataLength); 160 | } 161 | } 162 | 163 | /** 164 | * Obtains the type of the {@code MetaMessage}. 165 | * 166 | * @return an integer representing the {@code MetaMessage} type 167 | */ 168 | public int getType() { 169 | if (length>=2) { 170 | return data[1] & 0xFF; 171 | } 172 | return 0; 173 | } 174 | 175 | /** 176 | * Obtains a copy of the data for the meta message. The returned array of 177 | * bytes does not include the status byte or the message length data. The 178 | * length of the data for the meta message is the length of the array. Note 179 | * that the length of the entire message includes the status byte and the 180 | * meta message type byte, and therefore may be longer than the returned 181 | * array. 182 | * 183 | * @return array containing the meta message data 184 | * @see MidiMessage#getLength 185 | */ 186 | public byte[] getData() { 187 | byte[] returnedArray = new byte[dataLength]; 188 | System.arraycopy(data, (length - dataLength), returnedArray, 0, dataLength); 189 | return returnedArray; 190 | } 191 | 192 | /** 193 | * Creates a new object of the same class and with the same contents as this 194 | * object. 195 | * 196 | * @return a clone of this instance 197 | */ 198 | @Override 199 | public Object clone() { 200 | byte[] newData = new byte[length]; 201 | System.arraycopy(data, 0, newData, 0, newData.length); 202 | return new MetaMessage(newData); 203 | } 204 | 205 | // HELPER METHODS 206 | 207 | private int getVarIntLength(long value) { 208 | int length = 0; 209 | do { 210 | value = value >> 7; 211 | length++; 212 | } while (value > 0); 213 | return length; 214 | } 215 | 216 | private static final long mask = 0x7F; 217 | 218 | private void writeVarInt(byte[] data, int off, long value) { 219 | int shift=63; // number of bitwise left-shifts of mask 220 | // first screen out leading zeros 221 | while ((shift > 0) && ((value & (mask << shift)) == 0)) shift-=7; 222 | // then write actual values 223 | while (shift > 0) { 224 | data[off++]=(byte) (((value & (mask << shift)) >> shift) | 0x80); 225 | shift-=7; 226 | } 227 | data[off] = (byte) (value & mask); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MidiDeviceReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * {@code MidiDeviceReceiver} is a {@code Receiver} which represents a MIDI 30 | * input connector of a {@code MidiDevice} (see 31 | * {@link MidiDevice#getReceiver()}). 32 | * 33 | * @since 1.7 34 | */ 35 | public interface MidiDeviceReceiver extends Receiver { 36 | 37 | /** 38 | * Obtains a {@code MidiDevice} object which is an owner of this 39 | * {@code Receiver}. 40 | * 41 | * @return a {@code MidiDevice} object which is an owner of this 42 | * {@code Receiver} 43 | */ 44 | MidiDevice getMidiDevice(); 45 | } 46 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MidiDeviceTransmitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * {@code MidiDeviceTransmitter} is a {@code Transmitter} which represents a 30 | * MIDI input connector of a {@code MidiDevice} (see 31 | * {@link MidiDevice#getTransmitter()}). 32 | * 33 | * @since 1.7 34 | */ 35 | public interface MidiDeviceTransmitter extends Transmitter { 36 | 37 | /** 38 | * Obtains a {@code MidiDevice} object which is an owner of this 39 | * {@code Transmitter}. 40 | * 41 | * @return a {@code MidiDevice} object which is an owner of this 42 | * {@code Transmitter} 43 | */ 44 | MidiDevice getMidiDevice(); 45 | } 46 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MidiEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * MIDI events contain a MIDI message and a corresponding time-stamp expressed 30 | * in ticks, and can represent the MIDI event information stored in a MIDI file 31 | * or a {@link Sequence} object. The duration of a tick is specified by the 32 | * timing information contained in the MIDI file or {@code Sequence} object. 33 | *

34 | * In Java Sound, {@code MidiEvent} objects are typically contained in a 35 | * {@link Track}, and {@code Tracks} are likewise contained in a 36 | * {@code Sequence}. 37 | * 38 | * @author David Rivas 39 | * @author Kara Kytle 40 | */ 41 | public class MidiEvent { 42 | 43 | /** 44 | * The MIDI message for this event. 45 | */ 46 | private final MidiMessage message; 47 | 48 | /** 49 | * The tick value for this event. 50 | */ 51 | private long tick; 52 | 53 | /** 54 | * Constructs a new {@code MidiEvent}. 55 | * 56 | * @param message the MIDI message contained in the event 57 | * @param tick the time-stamp for the event, in MIDI ticks 58 | */ 59 | public MidiEvent(MidiMessage message, long tick) { 60 | 61 | this.message = message; 62 | this.tick = tick; 63 | } 64 | 65 | /** 66 | * Obtains the MIDI message contained in the event. 67 | * 68 | * @return the MIDI message 69 | */ 70 | public MidiMessage getMessage() { 71 | return message; 72 | } 73 | 74 | /** 75 | * Sets the time-stamp for the event, in MIDI ticks. 76 | * 77 | * @param tick the new time-stamp, in MIDI ticks 78 | */ 79 | public void setTick(long tick) { 80 | this.tick = tick; 81 | } 82 | 83 | /** 84 | * Obtains the time-stamp for the event, in MIDI ticks. 85 | * 86 | * @return the time-stamp for the event, in MIDI ticks 87 | */ 88 | public long getTick() { 89 | return tick; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MidiFileFormat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | import java.util.Collections; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | /** 33 | * A {@code MidiFileFormat} object encapsulates a MIDI file's type, as well as 34 | * its length and timing information. 35 | *

36 | * A {@code MidiFileFormat} object can include a set of properties. A property 37 | * is a pair of key and value: the key is of type {@code String}, the associated 38 | * property value is an arbitrary object. Properties specify additional 39 | * informational meta data (like a author, or copyright). Properties are 40 | * optional information, and file reader and file writer implementations are not 41 | * required to provide or recognize properties. 42 | *

43 | * The following table lists some common properties that should be used in 44 | * implementations: 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 54 | * 55 | * 56 | * 60 | * 64 | * 68 | * 72 | * 76 | *
MIDI File Format Properties
Property key 51 | * Value type 52 | * Description 53 | *
"author" 57 | * {@link String String} 58 | * name of the author of this file 59 | *
"title" 61 | * {@link String String} 62 | * title of this file 63 | *
"copyright" 65 | * {@link String String} 66 | * copyright message 67 | *
"date" 69 | * {@link java.util.Date Date} 70 | * date of the recording or release 71 | *
"comment" 73 | * {@link String String} 74 | * an arbitrary text 75 | *
77 | * 78 | * @author Kara Kytle 79 | * @author Florian Bomers 80 | * @see MidiSystem#getMidiFileFormat(java.io.File) 81 | * @see Sequencer#setSequence(java.io.InputStream stream) 82 | */ 83 | public class MidiFileFormat { 84 | 85 | /** 86 | * Represents unknown length. 87 | * 88 | * @see #getByteLength 89 | * @see #getMicrosecondLength 90 | */ 91 | public static final int UNKNOWN_LENGTH = -1; 92 | 93 | /** 94 | * The type of MIDI file. 95 | */ 96 | protected int type; 97 | 98 | /** 99 | * The division type of the MIDI file. 100 | * 101 | * @see Sequence#PPQ 102 | * @see Sequence#SMPTE_24 103 | * @see Sequence#SMPTE_25 104 | * @see Sequence#SMPTE_30DROP 105 | * @see Sequence#SMPTE_30 106 | */ 107 | protected float divisionType; 108 | 109 | /** 110 | * The timing resolution of the MIDI file. 111 | */ 112 | protected int resolution; 113 | 114 | /** 115 | * The length of the MIDI file in bytes. 116 | */ 117 | protected int byteLength; 118 | 119 | /** 120 | * The duration of the MIDI file in microseconds. 121 | */ 122 | protected long microsecondLength; 123 | 124 | /** 125 | * The set of properties. 126 | */ 127 | private HashMap properties; 128 | 129 | /** 130 | * Constructs a {@code MidiFileFormat}. 131 | * 132 | * @param type the MIDI file type (0, 1, or 2) 133 | * @param divisionType the timing division type (PPQ or one of the SMPTE 134 | * types) 135 | * @param resolution the timing resolution 136 | * @param bytes the length of the MIDI file in bytes, or 137 | * {@link #UNKNOWN_LENGTH} if not known 138 | * @param microseconds the duration of the file in microseconds, or 139 | * {@link #UNKNOWN_LENGTH} if not known 140 | * @see #UNKNOWN_LENGTH 141 | * @see Sequence#PPQ 142 | * @see Sequence#SMPTE_24 143 | * @see Sequence#SMPTE_25 144 | * @see Sequence#SMPTE_30DROP 145 | * @see Sequence#SMPTE_30 146 | */ 147 | public MidiFileFormat(int type, float divisionType, int resolution, int bytes, long microseconds) { 148 | 149 | this.type = type; 150 | this.divisionType = divisionType; 151 | this.resolution = resolution; 152 | this.byteLength = bytes; 153 | this.microsecondLength = microseconds; 154 | this.properties = null; 155 | } 156 | 157 | /** 158 | * Construct a {@code MidiFileFormat} with a set of properties. 159 | * 160 | * @param type the MIDI file type (0, 1, or 2) 161 | * @param divisionType the timing division type (PPQ or one of the SMPTE 162 | * types) 163 | * @param resolution the timing resolution 164 | * @param bytes the length of the MIDI file in bytes, or 165 | * {@code UNKNOWN_LENGTH} if not known 166 | * @param microseconds the duration of the file in microseconds, or 167 | * {@code UNKNOWN_LENGTH} if not known 168 | * @param properties a {@code Map} object with properties 169 | * @see #UNKNOWN_LENGTH 170 | * @see Sequence#PPQ 171 | * @see Sequence#SMPTE_24 172 | * @see Sequence#SMPTE_25 173 | * @see Sequence#SMPTE_30DROP 174 | * @see Sequence#SMPTE_30 175 | * @since 1.5 176 | */ 177 | public MidiFileFormat(int type, float divisionType, 178 | int resolution, int bytes, 179 | long microseconds, Map properties) { 180 | this(type, divisionType, resolution, bytes, microseconds); 181 | this.properties = new HashMap<>(properties); 182 | } 183 | 184 | /** 185 | * Obtains the MIDI file type. 186 | * 187 | * @return the file's type (0, 1, or 2) 188 | */ 189 | public int getType() { 190 | return type; 191 | } 192 | 193 | /** 194 | * Obtains the timing division type for the MIDI file. 195 | * 196 | * @return the division type (PPQ or one of the SMPTE types) 197 | * @see Sequence#Sequence(float, int) 198 | * @see Sequence#PPQ 199 | * @see Sequence#SMPTE_24 200 | * @see Sequence#SMPTE_25 201 | * @see Sequence#SMPTE_30DROP 202 | * @see Sequence#SMPTE_30 203 | * @see Sequence#getDivisionType() 204 | */ 205 | public float getDivisionType() { 206 | return divisionType; 207 | } 208 | 209 | /** 210 | * Obtains the timing resolution for the MIDI file. If the division type is 211 | * PPQ, the resolution is specified in ticks per beat. For SMTPE timing, the 212 | * resolution is specified in ticks per frame. 213 | * 214 | * @return the number of ticks per beat (PPQ) or per frame (SMPTE) 215 | * @see #getDivisionType 216 | * @see Sequence#getResolution() 217 | */ 218 | public int getResolution() { 219 | return resolution; 220 | } 221 | 222 | /** 223 | * Obtains the length of the MIDI file, expressed in 8-bit bytes. 224 | * 225 | * @return the number of bytes in the file, or {@code UNKNOWN_LENGTH} if not 226 | * known 227 | * @see #UNKNOWN_LENGTH 228 | */ 229 | public int getByteLength() { 230 | return byteLength; 231 | } 232 | 233 | /** 234 | * Obtains the length of the MIDI file, expressed in microseconds. 235 | * 236 | * @return the file's duration in microseconds, or {@code UNKNOWN_LENGTH} if 237 | * not known 238 | * @see Sequence#getMicrosecondLength() 239 | * @see #getByteLength 240 | * @see #UNKNOWN_LENGTH 241 | */ 242 | public long getMicrosecondLength() { 243 | return microsecondLength; 244 | } 245 | 246 | /** 247 | * Obtain an unmodifiable map of properties. The concept of properties is 248 | * further explained in the {@link MidiFileFormat class description}. 249 | * 250 | * @return a {@code Map} object containing all properties. If 251 | * no properties are recognized, an empty map is returned. 252 | * @see #getProperty(String) 253 | * @since 1.5 254 | */ 255 | @SuppressWarnings("unchecked") // Cast of result of clone 256 | public Map properties() { 257 | Map ret; 258 | if (properties == null) { 259 | ret = new HashMap<>(0); 260 | } else { 261 | ret = (Map) (properties.clone()); 262 | } 263 | return Collections.unmodifiableMap(ret); 264 | } 265 | 266 | /** 267 | * Obtain the property value specified by the key. The concept of properties 268 | * is further explained in the {@link MidiFileFormat class description}. 269 | *

270 | * If the specified property is not defined for a particular file format, 271 | * this method returns {@code null}. 272 | * 273 | * @param key the key of the desired property 274 | * @return the value of the property with the specified key, or {@code null} 275 | * if the property does not exist 276 | * @see #properties() 277 | * @since 1.5 278 | */ 279 | public Object getProperty(String key) { 280 | if (properties == null) { 281 | return null; 282 | } 283 | return properties.get(key); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MidiMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * {@code MidiMessage} is the base class for MIDI messages. They include not 30 | * only the standard MIDI messages that a synthesizer can respond to, but also 31 | * "meta-events" that can be used by sequencer programs. There are meta-events 32 | * for such information as lyrics, copyrights, tempo indications, time and key 33 | * signatures, markers, etc. For more information, see the Standard MIDI Files 34 | * 1.0 specification, which is part of the Complete MIDI 1.0 Detailed 35 | * Specification published by the MIDI Manufacturer's Association 36 | * (http://www.midi.org). 37 | *

38 | * The base {@code MidiMessage} class provides access to three types of 39 | * information about a MIDI message: 40 | *

46 | * 47 | * {@code MidiMessage} includes methods to get, but not set, these values. 48 | * Setting them is a subclass responsibility. 49 | *

50 | * The MIDI standard expresses MIDI data in bytes. 51 | * However, because Java uses signed bytes, the Java Sound API uses 52 | * integers instead of bytes when expressing MIDI data. For example, the 53 | * {@link #getStatus()} method of {@code MidiMessage} returns MIDI status bytes 54 | * as integers. If you are processing MIDI data that originated outside Java 55 | * Sound and now is encoded as signed bytes, the bytes can be converted to 56 | * integers using this conversion: 57 | *

58 | * {@code int i = (int)(byte & 0xFF)} 59 | *

60 | * If you simply need to pass a known MIDI byte value as a method parameter, it 61 | * can be expressed directly as an integer, using (for example) decimal or 62 | * hexadecimal notation. For instance, to pass the "active sensing" status byte 63 | * as the first argument to {@code ShortMessage}'s 64 | * {@link ShortMessage#setMessage(int) setMessage(int)} method, you can express 65 | * it as 254 or 0xFE. 66 | * 67 | * @author David Rivas 68 | * @author Kara Kytle 69 | * @see Track 70 | * @see Sequence 71 | * @see Receiver 72 | */ 73 | public abstract class MidiMessage implements Cloneable { 74 | 75 | /** 76 | * The MIDI message data. The first byte is the status byte for the message; 77 | * subsequent bytes up to the length of the message are data bytes for this 78 | * message. 79 | * 80 | * @see #getLength 81 | */ 82 | protected byte[] data; 83 | 84 | /** 85 | * The number of bytes in the MIDI message, including the status byte and 86 | * any data bytes. 87 | * 88 | * @see #getLength 89 | */ 90 | protected int length = 0; 91 | 92 | /** 93 | * Constructs a new {@code MidiMessage}. This protected constructor is 94 | * called by concrete subclasses, which should ensure that the data array 95 | * specifies a complete, valid MIDI message. 96 | * 97 | * @param data an array of bytes containing the complete message. The 98 | * message data may be changed using the {@code setMessage} method. 99 | * @see #setMessage 100 | */ 101 | protected MidiMessage(byte[] data) { 102 | this.data = data; 103 | if (data != null) { 104 | this.length = data.length; 105 | } 106 | } 107 | 108 | /** 109 | * Sets the data for the MIDI message. This protected method is called by 110 | * concrete subclasses, which should ensure that the data array specifies a 111 | * complete, valid MIDI message. 112 | * 113 | * @param data the data bytes in the MIDI message 114 | * @param length the number of bytes in the data byte array 115 | * @throws InvalidMidiDataException if the parameter values do not specify a 116 | * valid MIDI meta message 117 | */ 118 | protected void setMessage(byte[] data, int length) 119 | throws InvalidMidiDataException { 120 | if (length < 0 || (length > 0 && length > data.length)) { 121 | throw new IndexOutOfBoundsException( 122 | "length out of bounds: " + length); 123 | } 124 | this.length = length; 125 | 126 | if (this.data == null || this.data.length < this.length) { 127 | this.data = new byte[this.length]; 128 | } 129 | System.arraycopy(data, 0, this.data, 0, length); 130 | } 131 | 132 | /** 133 | * Obtains the MIDI message data. The first byte of the returned byte array 134 | * is the status byte of the message. Any subsequent bytes up to the length 135 | * of the message are data bytes. The byte array may have a length which is 136 | * greater than that of the actual message; the total length of the message 137 | * in bytes is reported by the {@link #getLength} method. 138 | * 139 | * @return the byte array containing the complete {@code MidiMessage} data 140 | */ 141 | public byte[] getMessage() { 142 | byte[] returnedArray = new byte[length]; 143 | System.arraycopy(data, 0, returnedArray, 0, length); 144 | return returnedArray; 145 | } 146 | 147 | /** 148 | * Obtains the status byte for the MIDI message. The status "byte" is 149 | * represented as an integer; see the 150 | * discussion in the {@code MidiMessage} 151 | * class description. 152 | * 153 | * @return the integer representation of this event's status byte 154 | */ 155 | public int getStatus() { 156 | if (length > 0) { 157 | return (data[0] & 0xFF); 158 | } 159 | return 0; 160 | } 161 | 162 | /** 163 | * Obtains the total length of the MIDI message in bytes. A MIDI message 164 | * consists of one status byte and zero or more data bytes. The return value 165 | * ranges from 1 for system real-time messages, to 2 or 3 for channel 166 | * messages, to any value for meta and system exclusive messages. 167 | * 168 | * @return the length of the message in bytes 169 | */ 170 | public int getLength() { 171 | return length; 172 | } 173 | 174 | /** 175 | * Creates a new object of the same class and with the same contents as this 176 | * object. 177 | * 178 | * @return a clone of this instance 179 | */ 180 | @Override 181 | public abstract Object clone(); 182 | } 183 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/MidiUnavailableException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * A {@code MidiUnavailableException} is thrown when a requested MIDI component 30 | * cannot be opened or created because it is unavailable. This often occurs when 31 | * a device is in use by another application. More generally, it can occur when 32 | * there is a finite number of a certain kind of resource that can be used for 33 | * some purpose, and all of them are already in use (perhaps all by this 34 | * application). For an example of the latter case, see the 35 | * {@link Transmitter#setReceiver(Receiver) setReceiver} method of 36 | * {@code Transmitter}. 37 | * 38 | * @author Kara Kytle 39 | */ 40 | public class MidiUnavailableException extends Exception { 41 | 42 | /** 43 | * Use serialVersionUID from JDK 1.3 for interoperability. 44 | */ 45 | private static final long serialVersionUID = 6093809578628944323L; 46 | 47 | /** 48 | * Constructs a {@code MidiUnavailableException} that has {@code null} as 49 | * its error detail message. 50 | */ 51 | public MidiUnavailableException() { 52 | super(); 53 | } 54 | 55 | /** 56 | * Constructs a {@code MidiUnavailableException} with the specified detail 57 | * message. 58 | * 59 | * @param message the string to display as an error detail message 60 | */ 61 | public MidiUnavailableException(final String message) { 62 | super(message); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/Patch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * A {@code Patch} object represents a location, on a MIDI synthesizer, into 30 | * which a single instrument is stored (loaded). Every {@code Instrument} object 31 | * has its own {@code Patch} object that specifies the memory location into 32 | * which that instrument should be loaded. The location is specified abstractly 33 | * by a bank index and a program number (not by any scheme that directly refers 34 | * to a specific address or offset in RAM). This is a hierarchical indexing 35 | * scheme: MIDI provides for up to 16384 banks, each of which contains up to 128 36 | * program locations. For example, a minimal sort of synthesizer might have only 37 | * one bank of instruments, and only 32 instruments (programs) in that bank. 38 | *

39 | * To select what instrument should play the notes on a particular MIDI channel, 40 | * two kinds of MIDI message are used that specify a patch location: a 41 | * bank-select command, and a program-change channel command. The Java Sound 42 | * equivalent is the 43 | * {@link MidiChannel#programChange(int, int) programChange(int, int)} method of 44 | * {@code MidiChannel}. 45 | * 46 | * @author Kara Kytle 47 | * @see Instrument 48 | * @see Instrument#getPatch() 49 | * @see MidiChannel#programChange(int, int) 50 | * @see Synthesizer#loadInstruments(Soundbank, Patch[]) 51 | * @see Soundbank 52 | * @see Sequence#getPatchList() 53 | */ 54 | public class Patch { 55 | 56 | /** 57 | * Bank index. 58 | */ 59 | private final int bank; 60 | 61 | /** 62 | * Program change number. 63 | */ 64 | private final int program; 65 | 66 | /** 67 | * Constructs a new patch object from the specified bank and program 68 | * numbers. 69 | * 70 | * @param bank the bank index (in the range from 0 to 16383) 71 | * @param program the program index (in the range from 0 to 127) 72 | */ 73 | public Patch(int bank, int program) { 74 | this.bank = bank; 75 | this.program = program; 76 | } 77 | 78 | /** 79 | * Returns the number of the bank that contains the instrument whose 80 | * location this {@code Patch} specifies. 81 | * 82 | * @return the bank number, whose range is from 0 to 16383 83 | * @see MidiChannel#programChange(int, int) 84 | */ 85 | public int getBank() { 86 | return bank; 87 | } 88 | 89 | /** 90 | * Returns the index, within a bank, of the instrument whose location this 91 | * {@code Patch} specifies. 92 | * 93 | * @return the instrument's program number, whose range is from 0 to 127 94 | * @see MidiChannel#getProgram 95 | * @see MidiChannel#programChange(int) 96 | * @see MidiChannel#programChange(int, int) 97 | */ 98 | public int getProgram() { 99 | return program; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/Receiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * A {@code Receiver} receives {@link MidiEvent} objects and typically does 30 | * something useful in response, such as interpreting them to generate sound or 31 | * raw MIDI output. Common MIDI receivers include synthesizers and MIDI Out 32 | * ports. 33 | * 34 | * @author Kara Kytle 35 | * @see MidiDevice 36 | * @see Synthesizer 37 | * @see Transmitter 38 | */ 39 | public interface Receiver extends AutoCloseable { 40 | 41 | //$$fb 2002-04-12: fix for 4662090: Contradiction in Receiver specification 42 | 43 | /** 44 | * Sends a MIDI message and time-stamp to this receiver. If time-stamping is 45 | * not supported by this receiver, the time-stamp value should be -1. 46 | * 47 | * @param message the MIDI message to send 48 | * @param timeStamp the time-stamp for the message, in microseconds 49 | * @throws IllegalStateException if the receiver is closed 50 | */ 51 | void send(MidiMessage message, long timeStamp); 52 | 53 | /** 54 | * Indicates that the application has finished using the receiver, and that 55 | * limited resources it requires may be released or made available. 56 | *

57 | * If the creation of this {@code Receiver} resulted in implicitly opening 58 | * the underlying device, the device is implicitly closed by this method. 59 | * This is true unless the device is kept open by other {@code Receiver} or 60 | * {@code Transmitter} instances that opened the device implicitly, and 61 | * unless the device has been opened explicitly. If the device this 62 | * {@code Receiver} is retrieved from is closed explicitly by calling 63 | * {@link MidiDevice#close MidiDevice.close}, the {@code Receiver} is 64 | * closed, too. For a detailed description of open/close behaviour see the 65 | * class description of {@link MidiDevice MidiDevice}. 66 | * 67 | * @see MidiSystem#getReceiver 68 | */ 69 | @Override 70 | void close(); 71 | } 72 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/SysexMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | import com.sun.media.sound.MidiUtils; 29 | 30 | /** 31 | * A {@code SysexMessage} object represents a MIDI system exclusive message. 32 | *

33 | * When a system exclusive message is read from a MIDI file, it always has a 34 | * defined length. Data from a system exclusive message from a MIDI file should 35 | * be stored in the data array of a {@code SysexMessage} as follows: the system 36 | * exclusive message status byte (0xF0 or 0xF7), all message data bytes, and 37 | * finally the end-of-exclusive flag (0xF7). The length reported by the 38 | * {@code SysexMessage} object is therefore the length of the system exclusive 39 | * data plus two: one byte for the status byte and one for the end-of-exclusive 40 | * flag. 41 | *

42 | * As dictated by the Standard MIDI Files specification, two status byte values 43 | * are legal for a {@code SysexMessage} read from a MIDI file: 44 | *

48 | * When Java Sound is used to handle system exclusive data that is being 49 | * received using MIDI wire protocol, it should place the data in one or more 50 | * {@code SysexMessages}. In this case, the length of the system exclusive data 51 | * is not known in advance; the end of the system exclusive data is marked by an 52 | * end-of-exclusive flag (0xF7) in the MIDI wire byte stream. 53 | * 57 | * The first {@code SysexMessage} object containing data for a particular system 58 | * exclusive message should have the status value 0xF0. If this message contains 59 | * all the system exclusive data for the message, it should end with the status 60 | * byte 0xF7 (EOX). Otherwise, additional system exclusive data should be sent 61 | * in one or more {@code SysexMessages} with a status value of 0xF7. The 62 | * {@code SysexMessage} containing the last of the data for the system exclusive 63 | * message should end with the value 0xF7 (EOX) to mark the end of the system 64 | * exclusive message. 65 | *

66 | * If system exclusive data from {@code SysexMessages} objects is being 67 | * transmitted using MIDI wire protocol, only the initial 0xF0 status byte, the 68 | * system exclusive data itself, and the final 0xF7 (EOX) byte should be 69 | * propagated; any 0xF7 status bytes used to indicate that a 70 | * {@code SysexMessage} contains continuing system exclusive data should not be 71 | * propagated via MIDI wire protocol. 72 | * 73 | * @author David Rivas 74 | * @author Kara Kytle 75 | * @author Florian Bomers 76 | */ 77 | public class SysexMessage extends MidiMessage { 78 | 79 | // Status byte defines 80 | 81 | /** 82 | * Status byte for System Exclusive message (0xF0, or 240). 83 | * 84 | * @see MidiMessage#getStatus 85 | */ 86 | public static final int SYSTEM_EXCLUSIVE = 0xF0; // 240 87 | 88 | /** 89 | * Status byte for Special System Exclusive message (0xF7, or 247), which is 90 | * used in MIDI files. It has the same value as END_OF_EXCLUSIVE, which is 91 | * used in the real-time "MIDI wire" protocol. 92 | * 93 | * @see MidiMessage#getStatus 94 | */ 95 | public static final int SPECIAL_SYSTEM_EXCLUSIVE = 0xF7; // 247 96 | 97 | /** 98 | * The data bytes for this system exclusive message. These are initialized 99 | * to {@code null} and are set explicitly by 100 | * {@link #setMessage(int, byte[], int, long) setMessage}. 101 | */ 102 | //protected byte[] data = null; 103 | 104 | /** 105 | * Constructs a new {@code SysexMessage}. The contents of the new message 106 | * are guaranteed to specify a valid MIDI message. Subsequently, you may set 107 | * the contents of the message using one of the {@code setMessage} methods. 108 | * 109 | * @see #setMessage 110 | */ 111 | public SysexMessage() { 112 | this(new byte[2]); 113 | // Default sysex message data: SOX followed by EOX 114 | data[0] = (byte) (SYSTEM_EXCLUSIVE & 0xFF); 115 | data[1] = (byte) (ShortMessage.END_OF_EXCLUSIVE & 0xFF); 116 | } 117 | 118 | /** 119 | * Constructs a new {@code SysexMessage} and sets the data for the message. 120 | * The first byte of the data array must be a valid system exclusive status 121 | * byte (0xF0 or 0xF7). The contents of the message can be changed by using 122 | * one of the {@code setMessage} methods. 123 | * 124 | * @param data the system exclusive message data including the status byte 125 | * @param length the length of the valid message data in the array, 126 | * including the status byte; it should be non-negative and less 127 | * than or equal to {@code data.length} 128 | * @throws InvalidMidiDataException if the parameter values do not specify a 129 | * valid MIDI meta message 130 | * @see #setMessage(byte[], int) 131 | * @see #setMessage(int, byte[], int) 132 | * @see #getData() 133 | * @since 1.7 134 | */ 135 | public SysexMessage(byte[] data, int length) 136 | throws InvalidMidiDataException { 137 | super(null); 138 | setMessage(data, length); 139 | } 140 | 141 | /** 142 | * Constructs a new {@code SysexMessage} and sets the data for the message. 143 | * The contents of the message can be changed by using one of the 144 | * {@code setMessage} methods. 145 | * 146 | * @param status the status byte for the message; it must be a valid system 147 | * exclusive status byte (0xF0 or 0xF7) 148 | * @param data the system exclusive message data (without the status byte) 149 | * @param length the length of the valid message data in the array; it 150 | * should be non-negative and less than or equal to 151 | * {@code data.length} 152 | * @throws InvalidMidiDataException if the parameter values do not specify a 153 | * valid MIDI system exclusive message 154 | * @see #setMessage(byte[], int) 155 | * @see #setMessage(int, byte[], int) 156 | * @see #getData() 157 | * @since 1.7 158 | */ 159 | public SysexMessage(int status, byte[] data, int length) 160 | throws InvalidMidiDataException { 161 | super(null); 162 | setMessage(status, data, length); 163 | } 164 | 165 | /** 166 | * Constructs a new {@code SysexMessage}. 167 | * 168 | * @param data an array of bytes containing the complete message. The 169 | * message data may be changed using the {@code setMessage} method. 170 | * @see #setMessage 171 | */ 172 | protected SysexMessage(byte[] data) { 173 | super(data); 174 | } 175 | 176 | /** 177 | * Sets the data for the system exclusive message. The first byte of the 178 | * data array must be a valid system exclusive status byte (0xF0 or 0xF7). 179 | * 180 | * @param data the system exclusive message data 181 | * @param length the length of the valid message data in the array, 182 | * including the status byte 183 | * @throws InvalidMidiDataException if the parameter values do not specify a 184 | * valid MIDI system exclusive message 185 | */ 186 | @Override 187 | public void setMessage(byte[] data, int length) throws InvalidMidiDataException { 188 | MidiUtils.checkSysexStatus(data, length); 189 | super.setMessage(data, length); 190 | } 191 | 192 | /** 193 | * Sets the data for the system exclusive message. 194 | * 195 | * @param status the status byte for the message (0xF0 or 0xF7) 196 | * @param data the system exclusive message data 197 | * @param length the length of the valid message data in the array 198 | * @throws InvalidMidiDataException if the status byte is invalid for a 199 | * system exclusive message 200 | */ 201 | public void setMessage(int status, byte[] data, int length) throws InvalidMidiDataException { 202 | MidiUtils.checkSysexStatus(status); 203 | if (length < 0 || length > data.length) { 204 | throw new IndexOutOfBoundsException("length out of bounds: "+length); 205 | } 206 | this.length = length + 1; 207 | 208 | if (this.data==null || this.data.length < this.length) { 209 | this.data = new byte[this.length]; 210 | } 211 | 212 | this.data[0] = (byte) (status & 0xFF); 213 | if (length > 0) { 214 | System.arraycopy(data, 0, this.data, 1, length); 215 | } 216 | } 217 | 218 | /** 219 | * Obtains a copy of the data for the system exclusive message. The returned 220 | * array of bytes does not include the status byte. 221 | * 222 | * @return array containing the system exclusive message data 223 | */ 224 | public byte[] getData() { 225 | byte[] returnedArray = new byte[length - 1]; 226 | System.arraycopy(data, 1, returnedArray, 0, (length - 1)); 227 | return returnedArray; 228 | } 229 | 230 | /** 231 | * Creates a new object of the same class and with the same contents as this 232 | * object. 233 | * 234 | * @return a clone of this instance 235 | */ 236 | @Override 237 | public Object clone() { 238 | byte[] newData = new byte[length]; 239 | System.arraycopy(data, 0, newData, 0, newData.length); 240 | return new SysexMessage(newData); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/Transmitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * A {@code Transmitter} sends {@link MidiEvent} objects to one or more 30 | * {@link Receiver Receivers}. Common MIDI transmitters include sequencers and 31 | * MIDI input ports. 32 | * 33 | * @author Kara Kytle 34 | * @see Receiver 35 | */ 36 | public interface Transmitter extends AutoCloseable { 37 | 38 | /** 39 | * Sets the receiver to which this transmitter will deliver MIDI messages. 40 | * If a receiver is currently set, it is replaced with this one. 41 | * 42 | * @param receiver the desired receiver 43 | */ 44 | void setReceiver(Receiver receiver); 45 | 46 | /** 47 | * Obtains the current receiver to which this transmitter will deliver MIDI 48 | * messages. 49 | * 50 | * @return the current receiver. If no receiver is currently set, returns 51 | * {@code null}. 52 | */ 53 | Receiver getReceiver(); 54 | 55 | /** 56 | * Indicates that the application has finished using the transmitter, and 57 | * that limited resources it requires may be released or made available. 58 | *

59 | * If the creation of this {@code Transmitter} resulted in implicitly 60 | * opening the underlying device, the device is implicitly closed by this 61 | * method. This is true unless the device is kept open by other 62 | * {@code Receiver} or {@code Transmitter} instances that opened the device 63 | * implicitly, and unless the device has been opened explicitly. If the 64 | * device this {@code Transmitter} is retrieved from is closed explicitly by 65 | * calling {@link MidiDevice#close MidiDevice.close}, the 66 | * {@code Transmitter} is closed, too. For a detailed description of 67 | * open/close behaviour see the class description of 68 | * {@link MidiDevice MidiDevice}. 69 | * 70 | * @see MidiSystem#getTransmitter 71 | */ 72 | @Override 73 | void close(); 74 | } 75 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/VoiceStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi; 27 | 28 | /** 29 | * A {@code VoiceStatus} object contains information about the current status of 30 | * one of the voices produced by a {@link Synthesizer}. 31 | *

32 | * MIDI synthesizers are generally capable of producing some maximum number of 33 | * simultaneous notes, also referred to as voices. A voice is a stream of 34 | * successive single notes, and the process of assigning incoming MIDI notes to 35 | * specific voices is known as voice allocation. However, the voice-allocation 36 | * algorithm and the contents of each voice are normally internal to a MIDI 37 | * synthesizer and hidden from outside view. One can, of course, learn from MIDI 38 | * messages which notes the synthesizer is playing, and one might be able deduce 39 | * something about the assignment of notes to voices. But MIDI itself does not 40 | * provide a means to report which notes a synthesizer has assigned to which 41 | * voice, nor even to report how many voices the synthesizer is capable of 42 | * synthesizing. 43 | *

44 | * In Java Sound, however, a {@code Synthesizer} class can expose the contents 45 | * of its voices through its 46 | * {@link Synthesizer#getVoiceStatus() getVoiceStatus()} method. This behavior 47 | * is recommended but optional; synthesizers that don't expose their voice 48 | * allocation simply return a zero-length array. A {@code Synthesizer} that does 49 | * report its voice status should maintain this information at all times for all 50 | * of its voices, whether they are currently sounding or not. In other words, a 51 | * given type of {@code Synthesizer} always has a fixed number of voices, equal 52 | * to the maximum number of simultaneous notes it is capable of sounding. 53 | *

54 | * If the voice is not currently processing a 55 | * MIDI note, it is considered inactive. A voice is inactive when it has been 56 | * given no note-on commands, or when every note-on command received has been 57 | * terminated by a corresponding note-off (or by an "all notes off" message). 58 | * For example, this happens when a synthesizer capable of playing 16 59 | * simultaneous notes is told to play a four-note chord; only four voices are 60 | * active in this case (assuming no earlier notes are still playing). Usually, a 61 | * voice whose status is reported as active is producing audible sound, but this 62 | * is not always true; it depends on the details of the instrument (that is, the 63 | * synthesis algorithm) and how long the note has been going on. For example, a 64 | * voice may be synthesizing the sound of a single hand-clap. Because this sound 65 | * dies away so quickly, it may become inaudible before a note-off message is 66 | * received. In such a situation, the voice is still considered active even 67 | * though no sound is currently being produced. 68 | *

69 | * Besides its active or inactive status, the {@code VoiceStatus} class provides 70 | * fields that reveal the voice's current MIDI channel, bank and program number, 71 | * MIDI note number, and MIDI volume. All of these can change during the course 72 | * of a voice. While the voice is inactive, each of these fields has an 73 | * unspecified value, so you should check the active field first. 74 | * 75 | * @author David Rivas 76 | * @author Kara Kytle 77 | * @see Synthesizer#getMaxPolyphony 78 | * @see Synthesizer#getVoiceStatus 79 | */ 80 | public class VoiceStatus { 81 | 82 | /** 83 | * Indicates whether the voice is currently processing a MIDI note. See the 84 | * explanation of 85 | * active and inactive voices. 86 | */ 87 | public boolean active = false; 88 | 89 | /** 90 | * The MIDI channel on which this voice is playing. The value is a 91 | * zero-based channel number if the voice is active, or unspecified if the 92 | * voice is inactive. 93 | * 94 | * @see MidiChannel 95 | * @see #active 96 | */ 97 | public int channel = 0; 98 | 99 | /** 100 | * The bank number of the instrument that this voice is currently using. 101 | * This is a number dictated by the MIDI bank-select message; it does not 102 | * refer to a {@code SoundBank} object. The value ranges from 0 to 16383 if 103 | * the voice is active, and is unspecified if the voice is inactive. 104 | * 105 | * @see Patch 106 | * @see Soundbank 107 | * @see #active 108 | * @see MidiChannel#programChange(int, int) 109 | */ 110 | public int bank = 0; 111 | 112 | /** 113 | * The program number of the instrument that this voice is currently using. 114 | * The value ranges from 0 to 127 if the voice is active, and is unspecified 115 | * if the voice is inactive. 116 | * 117 | * @see MidiChannel#getProgram 118 | * @see Patch 119 | * @see #active 120 | */ 121 | public int program = 0; 122 | 123 | /** 124 | * The MIDI note that this voice is playing. The range for an active voice 125 | * is from 0 to 127 in semitones, with 60 referring to Middle C. The value 126 | * is unspecified if the voice is inactive. 127 | * 128 | * @see MidiChannel#noteOn 129 | * @see #active 130 | */ 131 | public int note = 0; 132 | 133 | /** 134 | * The current MIDI volume level for the voice. The value ranges from 0 to 135 | * 127 if the voice is active, and is unspecified if the voice is inactive. 136 | *

137 | * Note that this value does not necessarily reflect the instantaneous level 138 | * of the sound produced by this voice; that level is the result of many 139 | * contributing factors, including the current instrument and the shape of 140 | * the amplitude envelope it produces. 141 | * 142 | * @see #active 143 | */ 144 | public int volume = 0; 145 | 146 | /** 147 | * Constructs a {@code VoiceStatus}. 148 | */ 149 | public VoiceStatus() {} 150 | } 151 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | /** 27 | * Provides interfaces and classes for I/O, sequencing, and synthesis of MIDI 28 | * (Musical Instrument Digital Interface) data. 29 | * 30 | *

Related Documentation

31 | * For more information on using Java Sound see: 32 | * 36 | * Please note: In the {@code javax.sound.midi} APIs, a {@code null} reference 37 | * parameter to methods is incorrect unless explicitly documented on the method 38 | * as having a meaningful interpretation. Usage to the contrary is incorrect 39 | * coding and may result in a run time exception either immediately or at some 40 | * later time. {@code NullPointerException} is an example of typical and 41 | * acceptable run time exception for such cases. 42 | * 43 | * @since 1.3 44 | */ 45 | package javax.sound.midi; 46 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/spi/MidiDeviceProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi.spi; 27 | 28 | import java.util.Arrays; 29 | 30 | import javax.sound.midi.MidiDevice; 31 | 32 | /** 33 | * A {@code MidiDeviceProvider} is a factory or provider for a particular type 34 | * of MIDI device. This mechanism allows the implementation to determine how 35 | * resources are managed in the creation and management of a device. 36 | * 37 | * @author Kara Kytle 38 | */ 39 | public abstract class MidiDeviceProvider { 40 | 41 | /** 42 | * Constructor for subclasses to call. 43 | */ 44 | protected MidiDeviceProvider() {} 45 | 46 | /** 47 | * Indicates whether the device provider supports the device represented by 48 | * the specified device info object. 49 | * 50 | * @param info an info object that describes the device for which support 51 | * is queried 52 | * @return {@code true} if the specified device is supported, otherwise 53 | * {@code false} 54 | * @throws NullPointerException if {@code info} is {@code null} 55 | */ 56 | public boolean isDeviceSupported(final MidiDevice.Info info) { 57 | return Arrays.stream(getDeviceInfo()).anyMatch(info::equals); 58 | } 59 | 60 | /** 61 | * Obtains the set of info objects representing the device or devices 62 | * provided by this {@code MidiDeviceProvider}. 63 | * 64 | * @return set of device info objects 65 | */ 66 | public abstract MidiDevice.Info[] getDeviceInfo(); 67 | 68 | /** 69 | * Obtains an instance of the device represented by the info object. 70 | * 71 | * @param info an info object that describes the desired device 72 | * @return device instance 73 | * @throws IllegalArgumentException if the info object specified does not 74 | * match the info object for a device supported by this 75 | * {@code MidiDeviceProvider} 76 | * @throws NullPointerException if {@code info} is {@code null} 77 | */ 78 | public abstract MidiDevice getDevice(MidiDevice.Info info); 79 | } 80 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/spi/MidiFileReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi.spi; 27 | 28 | import java.io.File; 29 | import java.io.IOException; 30 | import java.io.InputStream; 31 | import java.net.URL; 32 | 33 | import javax.sound.midi.InvalidMidiDataException; 34 | import javax.sound.midi.MidiFileFormat; 35 | import javax.sound.midi.Sequence; 36 | 37 | /** 38 | * A {@code MidiFileReader} supplies MIDI file-reading services. Classes 39 | * implementing this interface can parse the format information from one or more 40 | * types of MIDI file, and can produce a {@link Sequence} object from files of 41 | * these types. 42 | * 43 | * @author Kara Kytle 44 | * @since 1.3 45 | */ 46 | public abstract class MidiFileReader { 47 | 48 | /** 49 | * Constructor for subclasses to call. 50 | */ 51 | protected MidiFileReader() {} 52 | 53 | /** 54 | * Obtains the MIDI file format of the input stream provided. The stream 55 | * must point to valid MIDI file data. In general, MIDI file readers may 56 | * need to read some data from the stream before determining whether they 57 | * support it. These parsers must be able to mark the stream, read enough 58 | * data to determine whether they support the stream, and, if not, reset the 59 | * stream's read pointer to its original position. If the input stream does 60 | * not support this, this method may fail with an {@code IOException}. 61 | * 62 | * @param stream the input stream from which file format information should 63 | * be extracted 64 | * @return a {@code MidiFileFormat} object describing the MIDI file format 65 | * @throws InvalidMidiDataException if the stream does not point to valid 66 | * MIDI file data recognized by the system 67 | * @throws IOException if an I/O exception occurs 68 | * @throws NullPointerException if {@code stream} is {@code null} 69 | * @see InputStream#markSupported 70 | * @see InputStream#mark 71 | */ 72 | public abstract MidiFileFormat getMidiFileFormat(InputStream stream) 73 | throws InvalidMidiDataException, IOException; 74 | 75 | /** 76 | * Obtains the MIDI file format of the {@code URL} provided. The {@code URL} 77 | * must point to valid MIDI file data. 78 | * 79 | * @param url the {@code URL} from which file format information should be 80 | * extracted 81 | * @return a {@code MidiFileFormat} object describing the MIDI file format 82 | * @throws InvalidMidiDataException if the {@code URL} does not point to 83 | * valid MIDI file data recognized by the system 84 | * @throws IOException if an I/O exception occurs 85 | * @throws NullPointerException if {@code url} is {@code null} 86 | */ 87 | public abstract MidiFileFormat getMidiFileFormat(URL url) 88 | throws InvalidMidiDataException, IOException; 89 | 90 | /** 91 | * Obtains the MIDI file format of the {@code File} provided. The 92 | * {@code File} must point to valid MIDI file data. 93 | * 94 | * @param file the {@code File} from which file format information should 95 | * be extracted 96 | * @return a {@code MidiFileFormat} object describing the MIDI file format 97 | * @throws InvalidMidiDataException if the {@code File} does not point to 98 | * valid MIDI file data recognized by the system 99 | * @throws IOException if an I/O exception occurs 100 | * @throws NullPointerException if {@code file} is {@code null} 101 | */ 102 | public abstract MidiFileFormat getMidiFileFormat(File file) 103 | throws InvalidMidiDataException, IOException; 104 | 105 | /** 106 | * Obtains a MIDI sequence from the input stream provided. The stream must 107 | * point to valid MIDI file data. In general, MIDI file readers may need to 108 | * read some data from the stream before determining whether they support 109 | * it. These parsers must be able to mark the stream, read enough data to 110 | * determine whether they support the stream, and, if not, reset the 111 | * stream's read pointer to its original position. If the input stream does 112 | * not support this, this method may fail with an {@code IOException}. 113 | * 114 | * @param stream the input stream from which the {@code Sequence} should be 115 | * constructed 116 | * @return a {@code Sequence} object based on the MIDI file data contained 117 | * in the input stream 118 | * @throws InvalidMidiDataException if the stream does not point to valid 119 | * MIDI file data recognized by the system 120 | * @throws IOException if an I/O exception occurs 121 | * @throws NullPointerException if {@code stream} is {@code null} 122 | * @see InputStream#markSupported 123 | * @see InputStream#mark 124 | */ 125 | public abstract Sequence getSequence(InputStream stream) 126 | throws InvalidMidiDataException, IOException; 127 | 128 | /** 129 | * Obtains a MIDI sequence from the {@code URL} provided. The {@code URL} 130 | * must point to valid MIDI file data. 131 | * 132 | * @param url the {@code URL} for which the {@code Sequence} should be 133 | * constructed 134 | * @return a {@code Sequence} object based on the MIDI file data pointed to 135 | * by the {@code URL} 136 | * @throws InvalidMidiDataException if the {@code URL} does not point to 137 | * valid MIDI file data recognized by the system 138 | * @throws IOException if an I/O exception occurs 139 | * @throws NullPointerException if {@code url} is {@code null} 140 | */ 141 | public abstract Sequence getSequence(URL url) 142 | throws InvalidMidiDataException, IOException; 143 | 144 | /** 145 | * Obtains a MIDI sequence from the {@code File} provided. The {@code File} 146 | * must point to valid MIDI file data. 147 | * 148 | * @param file the {@code File} from which the {@code Sequence} should be 149 | * constructed 150 | * @return a {@code Sequence} object based on the MIDI file data pointed to 151 | * by the {@code File} 152 | * @throws InvalidMidiDataException if the {@code File} does not point to 153 | * valid MIDI file data recognized by the system 154 | * @throws IOException if an I/O exception occurs 155 | * @throws NullPointerException if {@code file} is {@code null} 156 | */ 157 | public abstract Sequence getSequence(File file) 158 | throws InvalidMidiDataException, IOException; 159 | } 160 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/spi/MidiFileWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | package javax.sound.midi.spi; 27 | 28 | import java.io.File; 29 | import java.io.IOException; 30 | import java.io.OutputStream; 31 | import java.util.Arrays; 32 | 33 | import javax.sound.midi.Sequence; 34 | 35 | /** 36 | * A {@code MidiFileWriter} supplies MIDI file-writing services. Classes that 37 | * implement this interface can write one or more types of MIDI file from a 38 | * {@link Sequence} object. 39 | * 40 | * @author Kara Kytle 41 | * @since 1.3 42 | */ 43 | public abstract class MidiFileWriter { 44 | 45 | /** 46 | * Constructor for subclasses to call. 47 | */ 48 | protected MidiFileWriter() {} 49 | 50 | /** 51 | * Obtains the set of MIDI file types for which file writing support is 52 | * provided by this file writer. 53 | * 54 | * @return array of file types. If no file types are supported, an array of 55 | * length 0 is returned. 56 | */ 57 | public abstract int[] getMidiFileTypes(); 58 | 59 | /** 60 | * Obtains the file types that this file writer can write from the sequence 61 | * specified. 62 | * 63 | * @param sequence the sequence for which MIDI file type support is queried 64 | * @return array of file types. If no file types are supported, returns an 65 | * array of length 0. 66 | * @throws NullPointerException if {@code sequence} is {@code null} 67 | */ 68 | public abstract int[] getMidiFileTypes(Sequence sequence); 69 | 70 | /** 71 | * Indicates whether file writing support for the specified MIDI file type 72 | * is provided by this file writer. 73 | * 74 | * @param fileType the file type for which write capabilities are queried 75 | * @return {@code true} if the file type is supported, otherwise 76 | * {@code false} 77 | */ 78 | public boolean isFileTypeSupported(final int fileType) { 79 | return Arrays.stream(getMidiFileTypes()) 80 | .anyMatch(type -> fileType == type); 81 | } 82 | 83 | /** 84 | * Indicates whether a MIDI file of the file type specified can be written 85 | * from the sequence indicated. 86 | * 87 | * @param fileType the file type for which write capabilities are queried 88 | * @param sequence the sequence for which file writing support is queried 89 | * @return {@code true} if the file type is supported for this sequence, 90 | * otherwise {@code false} 91 | * @throws NullPointerException if {@code sequence} is {@code null} 92 | */ 93 | public boolean isFileTypeSupported(final int fileType, 94 | final Sequence sequence) { 95 | return Arrays.stream(getMidiFileTypes(sequence)) 96 | .anyMatch(type -> fileType == type); 97 | } 98 | 99 | /** 100 | * Writes a stream of bytes representing a MIDI file of the file type 101 | * indicated to the output stream provided. 102 | * 103 | * @param in sequence containing MIDI data to be written to the file 104 | * @param fileType type of the file to be written to the output stream 105 | * @param out stream to which the file data should be written 106 | * @return the number of bytes written to the output stream 107 | * @throws IOException if an I/O exception occurs 108 | * @throws IllegalArgumentException if the file type is not supported by 109 | * this file writer 110 | * @throws NullPointerException if {@code in} or {@code out} are 111 | * {@code null} 112 | * @see #isFileTypeSupported(int, Sequence) 113 | * @see #getMidiFileTypes(Sequence) 114 | */ 115 | public abstract int write(Sequence in, int fileType, OutputStream out) 116 | throws IOException; 117 | 118 | /** 119 | * Writes a stream of bytes representing a MIDI file of the file type 120 | * indicated to the external file provided. 121 | * 122 | * @param in sequence containing MIDI data to be written to the external 123 | * file 124 | * @param fileType type of the file to be written to the external file 125 | * @param out external file to which the file data should be written 126 | * @return the number of bytes written to the file 127 | * @throws IOException if an I/O exception occurs 128 | * @throws IllegalArgumentException if the file type is not supported by 129 | * this file writer 130 | * @throws NullPointerException if {@code in} or {@code out} are 131 | * {@code null} 132 | * @see #isFileTypeSupported(int, Sequence) 133 | * @see #getMidiFileTypes(Sequence) 134 | */ 135 | public abstract int write(Sequence in, int fileType, File out) 136 | throws IOException; 137 | } 138 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/javax/sound/midi/spi/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | /** 27 | * Supplies interfaces for service providers to implement when offering new MIDI 28 | * devices, MIDI file readers and writers, or sound bank readers. 29 | * 30 | *

Related Documentation

31 | * For more information on using Java Sound see: 32 | * 36 | * Please note: In the {@code javax.sound.midi.spi} APIs, a {@code null} 37 | * reference parameter to methods is incorrect unless explicitly documented on 38 | * the method as having a meaningful interpretation. Usage to the contrary is 39 | * incorrect coding and may result in a run time exception either immediately or 40 | * at some later time. {@code NullPointerException} is an example of typical and 41 | * acceptable run time exception for such cases. 42 | * 43 | * @since 1.3 44 | */ 45 | package javax.sound.midi.spi; 46 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.content.pm.PackageInfo; 7 | import android.content.pm.PackageManager; 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.text.SpannableString; 11 | import android.text.Spanned; 12 | import android.text.method.LinkMovementMethod; 13 | import android.text.style.ClickableSpan; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.widget.TextView; 17 | 18 | public class AboutActivity extends AppCompatActivity { 19 | TextView versionName,github,bilibili,youtube; 20 | @Override 21 | public boolean onOptionsItemSelected(MenuItem item) { 22 | //工具栏返回上一级按钮 23 | if (item.getItemId() == 16908332){ 24 | finish(); 25 | } 26 | return true; 27 | } 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_about); 32 | 33 | versionName = findViewById(R.id.version_name); 34 | PackageManager packageManager = getPackageManager(); 35 | try { 36 | PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(),0); 37 | versionName.setText(packageInfo.versionName); 38 | } catch (PackageManager.NameNotFoundException e) { 39 | e.printStackTrace(); 40 | } 41 | github = findViewById(R.id.github); 42 | bilibili = findViewById(R.id.bilibili); 43 | youtube = findViewById(R.id.youtube); 44 | 45 | SpannableString spannableString1 = new SpannableString("github"); 46 | spannableString1.setSpan(new ClickableSpan() { 47 | 48 | @Override 49 | public void onClick(View widget) { 50 | Intent intent = new Intent(); 51 | intent.setData(Uri.parse("https://github.com/weixiansen574/Genshin-Lyre-midi-player")); 52 | startActivity(intent); 53 | } 54 | }, 0, github.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 55 | github.setText(spannableString1); 56 | github.setMovementMethod(LinkMovementMethod.getInstance()); 57 | 58 | SpannableString spannableString2 = new SpannableString("bilibili"); 59 | spannableString2.setSpan(new ClickableSpan() { 60 | 61 | @Override 62 | public void onClick(View widget) { 63 | Intent intent = new Intent(); 64 | intent.setData(Uri.parse("https://space.bilibili.com/503113802")); 65 | startActivity(intent); 66 | } 67 | }, 0, bilibili.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 68 | bilibili.setText(spannableString2); 69 | bilibili.setMovementMethod(LinkMovementMethod.getInstance()); 70 | 71 | SpannableString spannableString3 = new SpannableString("YouTube"); 72 | spannableString3.setSpan(new ClickableSpan() { 73 | 74 | @Override 75 | public void onClick(View widget) { 76 | Intent intent = new Intent(); 77 | intent.setData(Uri.parse("https://www.youtube.com/channel/UCbCv9JlpYxGMj-fv5xiX4vA")); 78 | startActivity(intent); 79 | } 80 | }, 0, youtube.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 81 | youtube.setText(spannableString3); 82 | youtube.setMovementMethod(LinkMovementMethod.getInstance()); 83 | 84 | 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/ClickService.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import android.accessibilityservice.AccessibilityService; 4 | import android.os.Build; 5 | import android.view.accessibility.AccessibilityEvent; 6 | 7 | import androidx.annotation.RequiresApi; 8 | 9 | public class ClickService extends AccessibilityService { 10 | public static ClickService mService; 11 | 12 | @RequiresApi(api = Build.VERSION_CODES.O) 13 | 14 | @Override 15 | protected void onServiceConnected() { 16 | super.onServiceConnected(); 17 | mService = this; 18 | } 19 | public static boolean isStart() { 20 | return mService != null; 21 | } 22 | 23 | @Override 24 | public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { 25 | 26 | } 27 | 28 | @Override 29 | public void onInterrupt() { 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/FileHelp.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.view.MenuItem; 7 | 8 | public class FileHelp extends AppCompatActivity { 9 | @Override 10 | public boolean onOptionsItemSelected(MenuItem item) { 11 | //工具栏返回上一级按钮 12 | if (item.getItemId() == 16908332){ 13 | finish(); 14 | } 15 | return true; 16 | } 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_file_help); 21 | } 22 | } -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/FloatListActivity.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import android.os.Bundle; 4 | import android.view.MenuItem; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.recyclerview.widget.LinearLayoutManager; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import java.util.ArrayList; 11 | 12 | public class FloatListActivity extends AppCompatActivity { 13 | RecyclerView music_list; 14 | FloatListManager floatListManager; 15 | @Override 16 | public boolean onOptionsItemSelected(MenuItem item) { 17 | //工具栏返回上一级按钮 18 | if (item.getItemId() == 16908332){ 19 | finish(); 20 | } 21 | return true; 22 | } 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | floatListManager = new FloatListManager(FloatListActivity.this); 28 | setContentView(R.layout.activity_float_list); 29 | music_list = findViewById(R.id.music_list_main); 30 | ArrayList musicNamesList = floatListManager.getMusicNames(); 31 | MusicListAdapter adapter = new MusicListAdapter(FloatListActivity.this,musicNamesList); 32 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); 33 | linearLayoutManager.setOrientation(RecyclerView.VERTICAL); 34 | music_list.setLayoutManager(linearLayoutManager); 35 | music_list.setAdapter(adapter); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/FloatListManager.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.database.sqlite.SQLiteOpenHelper; 8 | 9 | import java.io.ByteArrayInputStream; 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | import java.io.ObjectInputStream; 13 | import java.io.ObjectOutputStream; 14 | import java.util.ArrayList; 15 | 16 | import top.weixiansen574.LyrePlayer.midi.FloatMusicBean; 17 | import top.weixiansen574.LyrePlayer.midi.Note; 18 | 19 | public class FloatListManager extends SQLiteOpenHelper { 20 | public static final int VERSION = 2; 21 | public static final String DBNAME = "float_music_list"; 22 | 23 | public static final int MI_TYPE_LYRE = 1; 24 | public static final int MI_TYPE_OLD_LYRE = 2; 25 | 26 | Context context; 27 | public FloatListManager(Context context){ 28 | super(context,DBNAME,null,VERSION); 29 | SQLiteDatabase db = context.openOrCreateDatabase(DBNAME,Context.MODE_PRIVATE,null); 30 | 31 | } 32 | 33 | @Override 34 | public void onCreate(SQLiteDatabase db) { 35 | //version0: db.execSQL("CREATE TABLE IF NOT EXISTS musics (name TEXT Not null Primary key,note_list BLOB Not null) "); 36 | db.execSQL("CREATE TABLE IF NOT EXISTS musics (name TEXT Not null Primary key,type INTEGER Not null,note_list BLOB Not null) "); 37 | //收拾烂摊子,之前没用SQLiteOpenHelper,会直接调用onCreate,而不是调用onUpgrade 38 | if (db.getVersion() == 0){ 39 | onUpgrade(db,0,VERSION); 40 | } 41 | } 42 | 43 | @Override 44 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 45 | System.out.println("oldVersion:" + oldVersion + " newVersion:" + newVersion); 46 | switch (oldVersion){ 47 | case 0 : 48 | Cursor cursor = db.rawQuery("select * from musics", null); 49 | System.out.println(cursor.getCount()); 50 | db.execSQL("drop table musics"); 51 | db.execSQL("CREATE TABLE IF NOT EXISTS musics (name TEXT Not null Primary key,type INTEGER Not null,note_list BLOB Not null) "); 52 | while (cursor.moveToNext()){ 53 | ContentValues values = new ContentValues(); 54 | values.put("name",cursor.getString(cursor.getColumnIndex("name"))); 55 | values.put("type",FloatListManager.MI_TYPE_LYRE); 56 | values.put("note_list",cursor.getBlob(cursor.getColumnIndex("note_list"))); 57 | db.insert("musics",null,values); 58 | } 59 | break; 60 | } 61 | } 62 | 63 | public boolean insertMusic (String name,int type, ArrayList lyreNotes){ 64 | SQLiteDatabase database = getWritableDatabase(); 65 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 66 | ObjectOutputStream oos; 67 | try { 68 | oos = new ObjectOutputStream(baos); 69 | oos.writeObject(lyreNotes); 70 | ContentValues cv = new ContentValues(3); 71 | cv.put("name",name); 72 | cv.put("type",type); 73 | cv.put("note_list",baos.toByteArray()); 74 | database.insert("musics",null,cv); 75 | oos.flush(); 76 | oos.close(); 77 | baos.flush(); 78 | baos.close(); 79 | return true; 80 | } catch (IOException e) { 81 | e.printStackTrace(); 82 | return false; 83 | } 84 | } 85 | 86 | public ArrayList getMusicNames(){ 87 | SQLiteDatabase database = getReadableDatabase(); 88 | Cursor cursor = database.rawQuery("select * from musics",null); 89 | ArrayList musicNames = new ArrayList<>(cursor.getCount()); 90 | while(cursor.moveToNext()){ 91 | musicNames.add(cursor.getString(cursor.getColumnIndex("name"))); 92 | } 93 | return musicNames; 94 | } 95 | 96 | public FloatMusicBean getFloatMusicBean(String musicName){ 97 | SQLiteDatabase database = getReadableDatabase(); 98 | Cursor cursor = database.rawQuery("select type,note_list from musics where name=?",new String[]{musicName}); 99 | if (cursor.moveToNext()){ 100 | byte[] data = cursor.getBlob(cursor.getColumnIndex("note_list")); 101 | int type = cursor.getInt(cursor.getColumnIndex("type")); 102 | try { 103 | ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); 104 | ArrayList lyreNotes = (ArrayList) ois.readObject(); 105 | return new FloatMusicBean(musicName,type,lyreNotes); 106 | } catch (IOException | ClassNotFoundException e) { 107 | e.printStackTrace(); 108 | return null; 109 | } 110 | }else { 111 | return null; 112 | } 113 | } 114 | 115 | public int deleteMusic (String musicName){ 116 | SQLiteDatabase database = getWritableDatabase(); 117 | return database.delete("musics","name=?",new String[]{musicName}); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/MD5.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import java.math.BigInteger; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | public class MD5 { 8 | public static String getMD5Three(byte[] data) { 9 | BigInteger bi = null; 10 | try { 11 | MessageDigest md = MessageDigest.getInstance("MD5"); 12 | md.update(data, 0, data.length); 13 | byte[] b = md.digest(); 14 | bi = new BigInteger(1, b); 15 | } catch (NoSuchAlgorithmException e) { 16 | e.printStackTrace(); 17 | } 18 | String md5 = bi.toString(16); 19 | if (md5.length() < 32){ 20 | for (int i = 32 - md5.length(); i > 0; i--) { 21 | md5 = "0" + md5; 22 | } 23 | } 24 | return md5; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import android.app.ActivityManager; 4 | import android.app.AlertDialog; 5 | import android.content.ComponentName; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.net.Uri; 11 | import android.os.Bundle; 12 | import android.provider.Settings; 13 | import android.view.Menu; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.widget.Button; 17 | import android.widget.EditText; 18 | import android.widget.Toast; 19 | 20 | import androidx.annotation.NonNull; 21 | import androidx.appcompat.app.AppCompatActivity; 22 | import androidx.core.app.ActivityCompat; 23 | import androidx.core.content.ContextCompat; 24 | 25 | import java.io.File; 26 | import java.util.List; 27 | 28 | import top.weixiansen574.LyrePlayer.util.ShellUtils; 29 | 30 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 31 | Button openMidiFile; 32 | Button openFloatList; 33 | Button selectFromServer; 34 | SharedPreferences server; 35 | 36 | 37 | 38 | 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | this.setContentView(R.layout.activity_main); 42 | this.openMidiFile = this.findViewById(R.id.openMidiFile); 43 | this.openMidiFile.setOnClickListener(this); 44 | test(); 45 | server = getSharedPreferences("server", Context.MODE_PRIVATE); 46 | openFloatList = findViewById(R.id.open_float_list); 47 | openFloatList.setOnClickListener(new View.OnClickListener() { 48 | @Override 49 | public void onClick(View v) { 50 | startActivity(new Intent(MainActivity.this, FloatListActivity.class)); 51 | } 52 | }); 53 | selectFromServer = findViewById(R.id.select_from_server); 54 | selectFromServer.setOnLongClickListener(new View.OnLongClickListener() { 55 | @Override 56 | public boolean onLongClick(View v) { 57 | View view = View.inflate(MainActivity.this, R.layout.edit_text, null); 58 | final EditText editText = view.findViewById(R.id.edit_text); 59 | editText.setText(server.getString("address", "lyre-player.weixiansen574.top:1180")); 60 | AlertDialog setServerDialog = new AlertDialog.Builder(MainActivity.this) 61 | .setTitle("服务器地址") 62 | .setMessage("仅开发者调试用,请不要乱改!") 63 | .setView(view) 64 | .setPositiveButton("确定", new DialogInterface.OnClickListener() { 65 | 66 | @Override 67 | public void onClick(DialogInterface dialog, int which) { 68 | server.edit().putString("address", editText.getText().toString()).commit(); 69 | } 70 | }) 71 | .setNegativeButton("取消", new DialogInterface.OnClickListener() { 72 | 73 | @Override 74 | public void onClick(DialogInterface dialog, int which) { 75 | dialog.dismiss(); 76 | } 77 | }) 78 | .setNeutralButton("默认",null).show(); 79 | setServerDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener(new View.OnClickListener() { 80 | @Override 81 | public void onClick(View v) { 82 | editText.setText("lyre-player.weixiansen574.top:1180"); 83 | } 84 | }); 85 | return false; 86 | } 87 | }); 88 | selectFromServer.setOnClickListener(new View.OnClickListener() { 89 | @Override 90 | public void onClick(View v) { 91 | startActivity(new Intent(MainActivity.this, SelecFromServerActivity.class)); 92 | } 93 | }); 94 | Intent intent0 = getIntent(); 95 | //当使用文件管理器、QQ等第三方应用打开文件时,直接将URI传给下一activity 96 | if (intent0.getAction().equals("android.intent.action.VIEW")) { 97 | Uri uri = intent0.getData(); 98 | Intent intent = new Intent(this, AdjustAndStartActivity.class); 99 | intent.putExtra("open_file", true); 100 | intent.setData(uri); 101 | startActivity(intent); 102 | } 103 | //如果没有music_list文件夹就创建一个,免得没有创建文件夹导致打开列表时崩溃 104 | File lyreNotesFile = getFilesDir(); 105 | lyreNotesFile = new File(lyreNotesFile, "music_list"); 106 | if (!lyreNotesFile.exists()) { 107 | lyreNotesFile.mkdir(); 108 | } 109 | } 110 | 111 | private void test() { 112 | 113 | 114 | } 115 | 116 | @Override 117 | public boolean onCreateOptionsMenu(Menu menu) { 118 | getMenuInflater().inflate(R.menu.menu, menu); 119 | return true; 120 | } 121 | 122 | @Override 123 | public boolean onOptionsItemSelected(MenuItem item) { 124 | 125 | switch (item.getItemId()) { 126 | case R.id.about: 127 | startActivity(new Intent(this, AboutActivity.class)); 128 | break; 129 | default: 130 | break; 131 | } 132 | 133 | return true; 134 | } 135 | 136 | 137 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 138 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 139 | switch (requestCode) { 140 | case 1: { 141 | if (grantResults.length > 0 && grantResults[0] == 0) return; 142 | Toast.makeText(this, getString(R.string.qxsqsb), Toast.LENGTH_SHORT).show(); 143 | } 144 | } 145 | } 146 | 147 | public void onClick(View view) { 148 | //判断是拥有存储权限,否则申请 149 | if (ContextCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") != 0) { 150 | AlertDialog alertDialog = new AlertDialog.Builder(this).setTitle(R.string.qsyccqx).setMessage(getString(R.string.qsyccqx_msg_1) + getString(R.string.qsyccqx_msg_2)).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 151 | 152 | public void onClick(DialogInterface dialogInterface, int i) { 153 | ActivityCompat.requestPermissions(MainActivity.this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}, 1); 154 | } 155 | }).show(); 156 | } else { 157 | startActivity(new Intent(this, AdjustAndStartActivity.class)); 158 | } 159 | } 160 | 161 | @Override 162 | public void onBackPressed() { 163 | if (isAccessibilitySettingsOn(this,"top.weixiansen574.LyrePlayer.ClickService")){ 164 | new AlertDialog.Builder(this).setTitle("温馨提示:").setMessage("请先关闭无障碍再退出程序,不然下次打开会导致无障碍功能出故障,导致启用了但是等于没有的问题(安卓系统背锅,万年不修此bug!)") 165 | .setPositiveButton("去关闭", new DialogInterface.OnClickListener() { 166 | @Override 167 | public void onClick(DialogInterface dialog, int which) { 168 | try { 169 | startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); 170 | } catch (Exception e) { 171 | startActivity(new Intent(Settings.ACTION_SETTINGS)); 172 | e.printStackTrace(); 173 | } 174 | } 175 | }) 176 | .setNegativeButton("取消",null).setNeutralButton("使用root快速关闭", new DialogInterface.OnClickListener() { 177 | @Override 178 | public void onClick(DialogInterface dialog, int which) { 179 | if(ShellUtils.execCommand("settings put secure enabled_accessibility_services \"\"",true).result == 0){ 180 | Toast.makeText(MainActivity.this,"无障碍已关闭,正在安全退出程序",Toast.LENGTH_LONG).show(); 181 | exit(); 182 | }else { 183 | Toast.makeText(MainActivity.this,"你的手机没有root权限或拒绝了授权",Toast.LENGTH_LONG).show(); 184 | } 185 | } 186 | }).show(); 187 | }else { 188 | exit(); 189 | } 190 | } 191 | 192 | public static boolean isAccessibilitySettingsOn(Context context,String className){ 193 | if (context == null){ 194 | return false; 195 | } 196 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 197 | List runningServices = 198 | activityManager.getRunningServices(100);// 获取正在运行的服务列表 199 | if (runningServices.size()<0){ 200 | return false; 201 | } 202 | for (int i=0;i { 27 | Context context; 28 | FloatListManager floatListManager; 29 | List musicNames; 30 | 31 | public MusicListAdapter(Context context, List musicNames) { 32 | this.context = context; 33 | this.musicNames = musicNames; 34 | floatListManager = new FloatListManager(context); 35 | } 36 | 37 | 38 | @NonNull 39 | @Override 40 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 41 | View itemView = View.inflate(context, R.layout.list_item, null); 42 | return new MyViewHolder(itemView); 43 | } 44 | 45 | @Override 46 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 47 | FloatMusicBean musicBean = floatListManager.getFloatMusicBean(musicNames.get(position)); 48 | if (musicBean.type == FloatListManager.MI_TYPE_LYRE) { 49 | holder.img_icon.setImageDrawable(context.getDrawable(R.drawable.lrye_round_icon)); 50 | } else if (musicBean.type == FloatListManager.MI_TYPE_OLD_LYRE) { 51 | holder.img_icon.setImageDrawable(context.getDrawable(R.drawable.old_lyre_round_icon)); 52 | } 53 | holder.txv_name.setText(musicBean.name); 54 | Activity activity = (Activity) context; 55 | holder.itemView.setOnClickListener(v -> { 56 | final AlertDialog dialog_start = new AlertDialog.Builder(context).setTitle(R.string.querengequ).setMessage(musicBean.name).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 57 | @Override 58 | public void onClick(DialogInterface dialog, int which) { 59 | 60 | } 61 | }).setPositiveButton(R.string.ok, null).show(); 62 | dialog_start.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() { 63 | @Override 64 | public void onClick(View v) { 65 | //判断是否有悬浮窗权限 66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) { 67 | new AlertDialog.Builder(context).setTitle(R.string.floating_window_permission_is_required).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 68 | @Override 69 | public void onClick(DialogInterface dialogInterface, int i) { 70 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); 71 | intent.setData(Uri.parse("package:" + context.getPackageName())); 72 | activity.startActivityForResult(intent, 100); 73 | } 74 | }).show(); 75 | } else { 76 | //已经有权限,可以直接显示悬浮窗 77 | //判断是否有无障碍权限 78 | if (AccessibilityUtil.checkPermission(context)) { 79 | Intent intent = new Intent(context, FloatingButtonService.class); 80 | intent.putExtra("name", musicBean.name); 81 | intent.putExtra("noteListKey", NoteListStorage.putNoteList(floatListManager.getFloatMusicBean(musicBean.name).noteList)); 82 | activity.startService(intent); 83 | dialog_start.dismiss(); 84 | } 85 | } 86 | } 87 | }); 88 | }); 89 | holder.btn_delete.setOnClickListener((View.OnClickListener) v -> { 90 | new AlertDialog.Builder(context).setTitle(R.string.qdyscm).setMessage(musicBean.name).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 91 | @Override 92 | public void onClick(DialogInterface dialog, int which) { 93 | 94 | } 95 | }).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 96 | public void onClick(DialogInterface dialogInterface, int i) { 97 | //删除文件并清除音乐相关速度信息 98 | floatListManager.deleteMusic(musicBean.name); 99 | musicNames.remove(holder.getAdapterPosition()); 100 | notifyItemRemoved(holder.getAdapterPosition()); 101 | } 102 | }).show(); 103 | }); 104 | } 105 | 106 | @Override 107 | public int getItemCount() { 108 | return musicNames.size(); 109 | } 110 | 111 | public class MyViewHolder extends RecyclerView.ViewHolder { 112 | View itemView; 113 | ImageView img_icon; 114 | TextView txv_name; 115 | Button btn_delete; 116 | 117 | public MyViewHolder(@NonNull View itemView) { 118 | super(itemView); 119 | this.itemView = itemView; 120 | img_icon = itemView.findViewById(R.id.img_music_icon); 121 | txv_name = itemView.findViewById(R.id.music_name); 122 | btn_delete = itemView.findViewById(R.id.delate); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/NetworkHelpActivity.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.view.MenuItem; 7 | 8 | public class NetworkHelpActivity extends AppCompatActivity { 9 | 10 | @Override 11 | public boolean onOptionsItemSelected(MenuItem item) { 12 | switch (item.getItemId()) { 13 | case 16908332: 14 | finish(); 15 | break; 16 | default: 17 | break; 18 | } 19 | 20 | return true; 21 | } 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_network_help); 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/adapter/FLoatMusicListAdapter.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import java.util.List; 13 | 14 | import top.weixiansen574.LyrePlayer.FloatListManager; 15 | import top.weixiansen574.LyrePlayer.R; 16 | import top.weixiansen574.LyrePlayer.midi.FloatMusicBean; 17 | 18 | public class FLoatMusicListAdapter extends RecyclerView.Adapter { 19 | Context context; 20 | List musicNames; 21 | OnItemClickListener listener; 22 | FloatListManager floatListManager; 23 | 24 | public FLoatMusicListAdapter(Context context, List musicNames) { 25 | this.context = context; 26 | this.musicNames = musicNames; 27 | floatListManager = new FloatListManager(context); 28 | } 29 | 30 | @NonNull 31 | @Override 32 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 33 | View view = View.inflate(context.getApplicationContext(),R.layout.item_float_window_music,null); 34 | return new MyViewHolder(view); 35 | } 36 | 37 | @Override 38 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 39 | final int finalPosition = position; 40 | FloatMusicBean musicBean = floatListManager.getFloatMusicBean(musicNames.get(position)); 41 | if (musicBean.type == FloatListManager.MI_TYPE_LYRE){ 42 | holder.imageView.setImageDrawable(context.getDrawable(R.drawable.lrye_round_icon)); 43 | } else if (musicBean.type == FloatListManager.MI_TYPE_OLD_LYRE){ 44 | holder.imageView.setImageDrawable(context.getDrawable(R.drawable.old_lyre_round_icon)); 45 | } 46 | holder.textView.setText(musicBean.name); 47 | holder.itemView.setOnClickListener(new View.OnClickListener() { 48 | @Override 49 | public void onClick(View v) { 50 | listener.onItemClick(v,finalPosition); 51 | } 52 | }); 53 | } 54 | 55 | public void setOnItemClickListener(OnItemClickListener listener){ 56 | this.listener = listener; 57 | } 58 | @Override 59 | public int getItemCount() { 60 | return musicNames.size(); 61 | } 62 | 63 | public interface OnItemClickListener { 64 | public void onItemClick(View v, int position); 65 | } 66 | 67 | 68 | public static class MyViewHolder extends RecyclerView.ViewHolder{ 69 | View itemView; 70 | ImageView imageView; 71 | TextView textView; 72 | public MyViewHolder(@NonNull View itemView) { 73 | super(itemView); 74 | this.itemView = itemView; 75 | this.imageView = itemView.findViewById(R.id.img_music_icon); 76 | this.textView = itemView.findViewById(R.id.music_name); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/enums/InvalidKeySetting.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.enums; 2 | public enum InvalidKeySetting { 3 | leftAndRight,left,right,no 4 | } 5 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/enums/MusicInstrumentType.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.enums; 2 | 3 | public enum MusicInstrumentType { 4 | lyre,oldLyre 5 | } 6 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/midi/FloatMusicBean.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.midi; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class FloatMusicBean { 6 | public String name; 7 | public int type; 8 | public ArrayList noteList; 9 | 10 | public FloatMusicBean(String name, int type, ArrayList noteList) { 11 | this.name = name; 12 | this.type = type; 13 | this.noteList = noteList; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "FloatMusicBean{" + 19 | "name='" + name + '\'' + 20 | ", type=" + type + 21 | ", noteList=" + noteList + 22 | '}'; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/midi/Note.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.midi; 2 | 3 | import java.io.Serializable; 4 | 5 | public class Note implements Comparable, Serializable { 6 | private final long tick; 7 | private final byte value; 8 | private final boolean isNoteOn; 9 | 10 | public Note(long tick, byte value, boolean isNoteOn) { 11 | this.tick = tick; 12 | this.value = value; 13 | this.isNoteOn = isNoteOn; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "Note{" + 19 | "tick=" + tick + 20 | ", value=" + value + 21 | ", isNoteOn=" + isNoteOn + 22 | '}'; 23 | } 24 | 25 | public long getTick() { 26 | return tick; 27 | } 28 | 29 | public byte getValue() { 30 | return value; 31 | } 32 | 33 | public boolean isNoteOn() { 34 | return isNoteOn; 35 | } 36 | 37 | @Override 38 | public int compareTo(Note note) { 39 | return (int) (this.tick - note.getTick()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/midi/SequenceOfNotes.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.midi; 2 | 3 | import java.util.List; 4 | 5 | public class SequenceOfNotes { 6 | public List noteList; 7 | public float speed; 8 | 9 | public SequenceOfNotes(List noteList, float speed) { 10 | this.noteList = noteList; 11 | this.speed = speed; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/util/AccessibilityUtil.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.util; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Context; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.provider.Settings; 8 | import android.widget.Toast; 9 | 10 | import top.weixiansen574.LyrePlayer.ClickService; 11 | import top.weixiansen574.LyrePlayer.R; 12 | 13 | public class AccessibilityUtil { 14 | public static boolean checkPermission(final Context context) { 15 | if (!ClickService.isStart()) { 16 | new AlertDialog.Builder(context).setTitle(R.string.Accessibility_required).setMessage(R.string.Accessibility_required_msg).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 17 | public void onClick(DialogInterface dialogInterface, int i) { 18 | try { 19 | context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); 20 | } catch (Exception e) { 21 | context.startActivity(new Intent(Settings.ACTION_SETTINGS)); 22 | e.printStackTrace(); 23 | } 24 | } 25 | }).setNeutralButton("使用root自动开启无障碍", new DialogInterface.OnClickListener() { 26 | @Override 27 | public void onClick(DialogInterface dialog, int which) { 28 | 29 | if (ShellUtils.execCommand(new String[]{ 30 | "settings put secure enabled_accessibility_services " + context.getPackageName() + "/" + context.getPackageName() + ".ClickService", 31 | "settings put secure accessibility_enabled 1", 32 | "settings put secure enabled_accessibility_services \"\"",//关闭再开启,保证能成功 33 | "settings put secure enabled_accessibility_services " + context.getPackageName() + "/" + context.getPackageName() + ".ClickService", 34 | "settings put secure accessibility_enabled 1", 35 | }, true).result == 0) { 36 | Toast.makeText(context, "已使用root开启无障碍,请再次点击“启动悬浮窗”以打开悬浮窗", Toast.LENGTH_LONG).show(); 37 | } else { 38 | Toast.makeText(context, "你的手机没有root权限或拒绝了授权", Toast.LENGTH_LONG).show(); 39 | } 40 | } 41 | }).show(); 42 | return false; 43 | } else { 44 | return true; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/util/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.util; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Context; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.os.Handler; 8 | 9 | import com.alibaba.fastjson.JSON; 10 | import com.alibaba.fastjson.JSONObject; 11 | 12 | import java.io.IOException; 13 | 14 | import okhttp3.Call; 15 | import okhttp3.Callback; 16 | import okhttp3.OkHttpClient; 17 | import okhttp3.Request; 18 | import okhttp3.Response; 19 | import top.weixiansen574.LyrePlayer.NetworkHelpActivity; 20 | import top.weixiansen574.LyrePlayer.R; 21 | 22 | public class HttpUtil { 23 | static OkHttpClient okHttpClient = new OkHttpClient(); 24 | 25 | public static OkHttpClient getClient() { 26 | return okHttpClient; 27 | } 28 | 29 | public static void GETJson(String url, final Context context, final Handler handler, final Receive receive) { 30 | final Request request = new Request.Builder().url(url).build(); 31 | Call call = okHttpClient.newCall(request); 32 | call.enqueue(new Callback() { 33 | @Override 34 | public void onFailure(Call call, final IOException e) { 35 | if(receive instanceof Receive_WithDismiss){ 36 | Receive_WithDismiss receive_withDismiss = (Receive_WithDismiss)receive; 37 | receive_withDismiss.doDismiss(e); 38 | } 39 | handler.post(new Runnable() { 40 | @Override 41 | public void run() { 42 | new AlertDialog.Builder(context) 43 | .setTitle(R.string.wfljzfwq) 44 | .setMessage(e.getMessage()) 45 | .setPositiveButton(R.string.ckbz, new DialogInterface.OnClickListener() { 46 | @Override 47 | public void onClick(DialogInterface dialog, int which) { 48 | context.startActivity(new Intent(context, NetworkHelpActivity.class)); 49 | } 50 | }) 51 | .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 52 | @Override 53 | public void onClick(DialogInterface dialog, int which) { 54 | 55 | } 56 | }) 57 | .show(); 58 | } 59 | }); 60 | } 61 | 62 | @Override 63 | public void onResponse(Call call, Response response) throws IOException { 64 | final JSONObject jsonObject = JSON.parseObject(response.body().string()); 65 | handler.post(new Runnable() { 66 | @Override 67 | public void run() { 68 | receive.success(jsonObject); 69 | } 70 | }); 71 | } 72 | }); 73 | } 74 | public interface Receive { 75 | void success(JSONObject jsonObject); 76 | } 77 | public interface Receive_WithDismiss extends Receive { 78 | void doDismiss(Exception e); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/util/NoteListStorage.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | 6 | import top.weixiansen574.LyrePlayer.midi.Note; 7 | 8 | public class NoteListStorage { 9 | private static HashMap> noteListMap = new HashMap<>(); 10 | 11 | public static long putNoteList(ArrayList noteList) { 12 | long key = System.currentTimeMillis(); 13 | noteListMap.put(key, noteList); 14 | return key; 15 | } 16 | 17 | public static ArrayList getNoteList(long key){ 18 | return noteListMap.get(key); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/util/RootUtils.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.util; 2 | 3 | import java.io.DataInputStream; 4 | import java.io.DataOutputStream; 5 | import java.io.IOException; 6 | import java.nio.charset.StandardCharsets; 7 | 8 | public class RootUtils { 9 | private Process process; 10 | private DataOutputStream os; 11 | private DataInputStream is; 12 | public RootUtils() throws IOException { 13 | process = Runtime.getRuntime().exec("su"); 14 | os = new DataOutputStream(process.getOutputStream()); 15 | 16 | }; 17 | public void exec(String command){ 18 | try { 19 | os.write(command.getBytes(StandardCharsets.UTF_8)); 20 | System.out.println(); 21 | } catch (IOException e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/java/top/weixiansen574/LyrePlayer/util/ShellUtils.java: -------------------------------------------------------------------------------- 1 | package top.weixiansen574.LyrePlayer.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.DataOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.util.List; 8 | 9 | public class ShellUtils { 10 | private static final String TAG = "ShellUtils"; 11 | 12 | public static final String COMMAND_SU = "su"; 13 | public static final String COMMAND_SH = "sh"; 14 | public static final String COMMAND_EXIT = "exit\n"; 15 | public static final String COMMAND_LINE_END = "\n"; 16 | 17 | private ShellUtils() { 18 | throw new AssertionError(); 19 | } 20 | 21 | /** 22 | * 查看是否有了root权限 23 | * 24 | * @return 25 | */ 26 | public static boolean checkRootPermission() { 27 | return execCommand("echo root", true, false).result == 0; 28 | } 29 | 30 | /** 31 | * 执行shell命令,默认返回结果 32 | * 33 | * @param command 34 | * command 35 | * @param isRoot 36 | * need root permission 37 | * @return 38 | * @see ShellUtils#execCommand(String[], boolean, boolean) 39 | */ 40 | public static CommandResult execCommand(String command, boolean isRoot) { 41 | return execCommand(new String[] { command }, isRoot, true); 42 | } 43 | 44 | /** 45 | * 执行shell命令,默认返回结果 46 | * 47 | * @param commands 48 | * command list 49 | * @param isRoot 50 | * need root permission 51 | * @return 52 | * @see ShellUtils#execCommand(String[], boolean, boolean) 53 | */ 54 | public static CommandResult execCommand(List commands, 55 | boolean isRoot) { 56 | return execCommand( 57 | commands == null ? null : commands.toArray(new String[] {}), 58 | isRoot, true); 59 | } 60 | 61 | /** 62 | * 执行shell命令,默认返回结果 63 | * 64 | * @param commands 65 | * command array 66 | * @param isRoot 67 | * need root permission 68 | * @return 69 | * @see ShellUtils#execCommand(String[], boolean, boolean) 70 | */ 71 | public static CommandResult execCommand(String[] commands, boolean isRoot) { 72 | return execCommand(commands, isRoot, true); 73 | } 74 | 75 | /** 76 | * execute shell command 77 | * 78 | * @param command 79 | * command 80 | * @param isRoot 81 | * need root permission 82 | * @param isNeedResultMsg 83 | * whether need result msg 84 | * @return 85 | * @see ShellUtils#execCommand(String[], boolean, boolean) 86 | */ 87 | public static CommandResult execCommand(String command, boolean isRoot, 88 | boolean isNeedResultMsg) { 89 | return execCommand(new String[] { command }, isRoot, isNeedResultMsg); 90 | } 91 | 92 | /** 93 | * execute shell commands 94 | * 95 | * @param commands 96 | * command list 97 | * @param isRoot 98 | * need root permission 99 | * @param isNeedResultMsg 100 | * result 101 | * @return 102 | * @see ShellUtils#execCommand(String[], boolean, boolean) 103 | */ 104 | public static CommandResult execCommand(List commands, 105 | boolean isRoot, boolean isNeedResultMsg) { 106 | return execCommand( 107 | commands == null ? null : commands.toArray(new String[] {}), 108 | isRoot, isNeedResultMsg); 109 | } 110 | 111 | /** 112 | * execute shell commands 113 | * 114 | * @param commands 115 | * command array 116 | * @param isRoot 117 | * need root permission 118 | * @param isNeedResultMsg 119 | * result 120 | * @return
    121 | *
  • if isNeedResultMsg is false, {@link CommandResult#successMsg} 122 | * is null and {@link CommandResult#errorMsg} is null.
  • 123 | *
  • if {@link CommandResult#result} is -1, there maybe some 124 | * excepiton.
  • 125 | *
126 | */ 127 | public static CommandResult execCommand(String[] commands, boolean isRoot, 128 | boolean isNeedResultMsg) { 129 | int result = -1; 130 | if (commands == null || commands.length == 0) { 131 | return new CommandResult(result, null, null); 132 | } 133 | 134 | Process process = null; 135 | BufferedReader successResult = null; 136 | BufferedReader errorResult = null; 137 | StringBuilder successMsg = null; 138 | StringBuilder errorMsg = null; 139 | 140 | DataOutputStream os = null; 141 | try { 142 | process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH); 143 | os = new DataOutputStream(process.getOutputStream()); 144 | for (String command : commands) { 145 | if (command == null) { 146 | continue; 147 | } 148 | // donnot use os.writeBytes(commmand), avoid chinese charset 149 | // error 150 | os.write(command.getBytes()); 151 | os.writeBytes(COMMAND_LINE_END); 152 | os.flush(); 153 | } 154 | os.writeBytes(COMMAND_EXIT); 155 | os.flush(); 156 | result = process.waitFor(); 157 | // get command result 158 | if (isNeedResultMsg) { 159 | successMsg = new StringBuilder(); 160 | errorMsg = new StringBuilder(); 161 | successResult = new BufferedReader(new InputStreamReader( 162 | process.getInputStream())); 163 | errorResult = new BufferedReader(new InputStreamReader( 164 | process.getErrorStream())); 165 | String s; 166 | while ((s = successResult.readLine()) != null) { 167 | successMsg.append(s); 168 | } 169 | while ((s = errorResult.readLine()) != null) { 170 | errorMsg.append(s); 171 | } 172 | } 173 | } catch (IOException e) { 174 | e.printStackTrace(); 175 | } catch (Exception e) { 176 | e.printStackTrace(); 177 | } finally { 178 | try { 179 | if (os != null) { 180 | os.close(); 181 | } 182 | if (successResult != null) { 183 | successResult.close(); 184 | } 185 | if (errorResult != null) { 186 | errorResult.close(); 187 | } 188 | } catch (IOException e) { 189 | e.printStackTrace(); 190 | } 191 | if (process != null) { 192 | process.destroy(); 193 | } 194 | } 195 | return new CommandResult(result, successMsg == null ? null 196 | : successMsg.toString(), errorMsg == null ? null 197 | : errorMsg.toString()); 198 | } 199 | 200 | /** 201 | * 运行结果 202 | *
    203 | *
  • {@link CommandResult#result} means result of command, 0 means normal, 204 | * else means error, same to excute in linux shell
  • 205 | *
  • {@link CommandResult#successMsg} means success message of command 206 | * result
  • 207 | *
  • {@link CommandResult#errorMsg} means error message of command result
  • 208 | *
209 | * 210 | * @author Trinea 211 | * 2013-5-16 212 | */ 213 | public static class CommandResult { 214 | /** 运行结果 **/ 215 | public int result; 216 | /** 运行成功结果 **/ 217 | public String successMsg; 218 | /** 运行失败结果 **/ 219 | public String errorMsg; 220 | 221 | public CommandResult(int result) { 222 | this.result = result; 223 | } 224 | 225 | public CommandResult(int result, String successMsg, String errorMsg) { 226 | this.result = result; 227 | this.successMsg = successMsg; 228 | this.errorMsg = errorMsg; 229 | } 230 | } 231 | } 232 | //Copy自:https://blog.csdn.net/Sunxiaolin2016/article/details/100974661 -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable-v24/test_ipv6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable-v24/test_ipv6.jpg -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/file_help_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/file_help_1.jpg -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/file_help_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/file_help_2.jpg -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/file_help_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/file_help_3.jpg -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_floder.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_list.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_music.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_server.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/ic_user.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/key_map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/key_map.jpg -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/logo.png -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/lrye_round_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/lrye_round_icon.png -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/lyre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/lyre.png -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/old_lyre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/old_lyre.png -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/old_lyre_round_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/old_lyre_round_icon.png -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/drawable/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weixiansen574/Genshin-Lyre-midi-player/d738b552652f1e4d8d5f4e83b9f675b9593d0e30/LyrePlayer/app/src/main/res/drawable/upload.png -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 14 | 15 | 20 | 21 | 28 | 33 | 40 | 47 | 54 | 61 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/layout/activity_file_help.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 17 | 18 | 24 | 25 | 30 | 31 | 37 | 38 | 45 | 46 | 51 | 52 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/layout/activity_float_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /LyrePlayer/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 |