├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── nekocode │ │ └── musicviz │ │ ├── FFTFrame.java │ │ ├── MainActivity.java │ │ ├── WaveFormFrame.java │ │ ├── render │ │ ├── GLScene.java │ │ ├── MyGLUtils.java │ │ ├── RenderBuffer.java │ │ ├── SceneController.java │ │ └── VisualizerRenderer.java │ │ └── scene │ │ ├── BasicSpectrumScene.java │ │ ├── ChlastScene.java │ │ ├── CircSpectrumScene.java │ │ ├── CommonScene.java │ │ ├── EnhancedSpectrumScene.java │ │ ├── InputSoundScene.java │ │ ├── OriginScene.java │ │ ├── RainbowSpectrumScene.java │ │ ├── Sa2WaveScene.java │ │ └── WavesRemixScene.java │ └── res │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── raw │ ├── basic_spectrum.fsh │ ├── chlast.fsh │ ├── circ_spectrum.fsh │ ├── enhanced_spectrum.fsh │ ├── input_sound.fsh │ ├── music.mp3 │ ├── origin.fsh │ ├── rainbow_spectrum.fsh │ ├── sa2_wave.fsh │ ├── vertext.vsh │ └── waves_remix.fsh │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── image ├── p1.png ├── p2.png ├── p3.png ├── p4.png └── p5.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | *.iml 10 | *.apk 11 | *.jobf 12 | keystore.properties -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project ports some music visulization shaders from WebGL([Shadertoy.com](https://www.shadertoy.com/)) to Android OpenGL ES. It captures the audio's fft & waveform data by system's [`Visualizer`](https://developer.android.com/reference/android/media/audiofx/Visualizer.html), and then pass them to a 2-lines height texture that can be used in a shader program ([more detail](https://forum.openframeworks.cc/t/passing-fft-audio-data-into-a-shader-as-a-texture2d-object-shadertoy/13756)). 2 | 3 | ![Preview](image/p1.png) ![Preview](image/p2.png) ![Preview](image/p3.png) ![Preview](image/p4.png) ![Preview](image/p5.png) etc 4 | 5 | Download apk to try: [Releases page](https://github.com/nekocode/MusicVisualization/releases) 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "cn.nekocode.musicviz" 7 | minSdkVersion 14 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | implementation fileTree(dir: 'libs', include: ['*.jar']) 22 | implementation "com.android.support:appcompat-v7:28.0.0" 23 | implementation "com.android.support:support-annotations:28.0.0" 24 | } 25 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/opt/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/FFTFrame.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz; 18 | 19 | /** 20 | * Some code was copied from: 21 | * https://github.com/Cleveroad/WaveInApp/blob/master/library/src/main/java/com/cleveroad/audiovisualization/VisualizerDbmHandler.java 22 | * 23 | * @author nekocode (nekocode.cn@gmail.com) 24 | */ 25 | public class FFTFrame { 26 | /** 27 | * Maximum value of dB. Used for controlling wave height percentage. 28 | */ 29 | private static final float MAX_DB_VALUE = 76; 30 | 31 | private byte[] mRawFFT; 32 | private float[] mDbs; 33 | 34 | 35 | public FFTFrame(byte[] fft, int offset, int len) { 36 | if (offset + len > fft.length) throw new RuntimeException("Illegal offset and len"); 37 | 38 | mRawFFT = new byte[len]; 39 | System.arraycopy(fft, offset, mRawFFT, 0, len); 40 | } 41 | 42 | /** 43 | * Calculate dBs and amplitudes 44 | */ 45 | public void calculate(int arraySize, float[] dBmArray) { 46 | int dataSize = mRawFFT.length / 2 - 1; 47 | 48 | if (mDbs == null || mDbs.length != dataSize) { 49 | mDbs = new float[dataSize]; 50 | } 51 | for (int i = 0; i < dataSize; i++) { 52 | float re = mRawFFT[2 * i]; 53 | float im = mRawFFT[2 * i + 1]; 54 | float sqMag = re * re + im * im; 55 | mDbs[i] = magnitude2Db(sqMag); 56 | } 57 | 58 | for (int i = 0; i < arraySize; i++) { 59 | int index = (int) (i * 1f * dataSize / arraySize); 60 | dBmArray[i] = mDbs[index] / MAX_DB_VALUE; 61 | } 62 | } 63 | 64 | private static float magnitude2Db(float squareMag) { 65 | if (squareMag == 0) return 0; 66 | return (float) (20 * Math.log10(squareMag)); 67 | } 68 | 69 | // http://forum.processing.org/topic/super-fast-square-root 70 | private static float fastSqrt(float x) { 71 | return Float.intBitsToFloat(532483686 + (Float.floatToRawIntBits(x) >> 1)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz; 18 | 19 | import android.Manifest; 20 | import android.content.Context; 21 | import android.content.pm.PackageManager; 22 | import android.media.MediaPlayer; 23 | import android.media.audiofx.Visualizer; 24 | import android.os.Bundle; 25 | import android.support.annotation.NonNull; 26 | import android.support.v4.app.ActivityCompat; 27 | import android.support.v4.content.ContextCompat; 28 | import android.support.v7.app.AppCompatActivity; 29 | import android.util.Pair; 30 | import android.view.Menu; 31 | import android.view.MenuItem; 32 | import android.view.TextureView; 33 | import android.view.View; 34 | import android.widget.FrameLayout; 35 | import android.widget.Toast; 36 | 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | 40 | import cn.nekocode.musicviz.render.GLScene; 41 | import cn.nekocode.musicviz.render.SceneController; 42 | import cn.nekocode.musicviz.render.VisualizerRenderer; 43 | import cn.nekocode.musicviz.scene.BasicSpectrumScene; 44 | import cn.nekocode.musicviz.scene.CircSpectrumScene; 45 | import cn.nekocode.musicviz.scene.ChlastScene; 46 | import cn.nekocode.musicviz.scene.EnhancedSpectrumScene; 47 | import cn.nekocode.musicviz.scene.InputSoundScene; 48 | import cn.nekocode.musicviz.scene.OriginScene; 49 | import cn.nekocode.musicviz.scene.Sa2WaveScene; 50 | import cn.nekocode.musicviz.scene.WavesRemixScene; 51 | import cn.nekocode.musicviz.scene.RainbowSpectrumScene; 52 | 53 | /** 54 | * @author nekocode (nekocode.cn@gmail.com) 55 | */ 56 | public class MainActivity extends AppCompatActivity implements Visualizer.OnDataCaptureListener { 57 | private static final int REQUEST_PERMISSION = 101; 58 | private FrameLayout mContainerView; 59 | private VisualizerRenderer mRender; 60 | private SceneController mSceneController; 61 | private List> mSceneList; 62 | private MediaPlayer mPlayer; 63 | private Visualizer mVisualizer; 64 | 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | setContentView(mContainerView = new FrameLayout(this)); 70 | 71 | /* 72 | Check premission 73 | */ 74 | if (ContextCompat.checkSelfPermission( 75 | this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { 76 | 77 | // Show an explanation 78 | if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) { 79 | Toast.makeText(this, "RECORD_AUDIO permission is required.", Toast.LENGTH_SHORT).show(); 80 | 81 | } else { 82 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 83 | REQUEST_PERMISSION); 84 | } 85 | 86 | } else { 87 | start(); 88 | } 89 | } 90 | 91 | @Override 92 | public void onRequestPermissionsResult( 93 | int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { 94 | 95 | switch (requestCode) { 96 | case REQUEST_PERMISSION: { 97 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 98 | start(); 99 | } 100 | } 101 | } 102 | } 103 | 104 | private void start() { 105 | int captureSize = Visualizer.getCaptureSizeRange()[1]; 106 | captureSize = captureSize > 512 ? 512 : captureSize; 107 | 108 | /* 109 | Setup texture view 110 | */ 111 | final TextureView textureView = new TextureView(this); 112 | mContainerView.addView(textureView); 113 | textureView.setSurfaceTextureListener(mRender = new VisualizerRenderer(this, captureSize / 2)); 114 | textureView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 115 | @Override 116 | public void onLayoutChange( 117 | View v, int left, int top, int right, int bottom, 118 | int oldLeft, int oldTop, int oldRight, int oldBottom) { 119 | 120 | mRender.onSurfaceTextureSizeChanged(null, v.getWidth(), v.getHeight()); 121 | } 122 | }); 123 | textureView.requestLayout(); 124 | 125 | mRender.setSceneController(mSceneController = new SceneController() { 126 | @Override 127 | public void onSetup(Context context, int audioTextureId, int textureWidth) { 128 | mSceneList = new ArrayList<>(); 129 | GLScene defaultScene = new BasicSpectrumScene(context, audioTextureId, textureWidth); 130 | mSceneList.add(Pair.create("Basic Spectrum", defaultScene)); 131 | mSceneList.add(Pair.create("Circle Spectrum", new CircSpectrumScene(context, audioTextureId, textureWidth))); 132 | mSceneList.add(Pair.create("Enhanced Spectrum", new EnhancedSpectrumScene(context, audioTextureId, textureWidth))); 133 | mSceneList.add(Pair.create("Input Sound", new InputSoundScene(context, audioTextureId, textureWidth))); 134 | mSceneList.add(Pair.create("Sa2Wave", new Sa2WaveScene(context, audioTextureId, textureWidth))); 135 | mSceneList.add(Pair.create("Waves Remix", new WavesRemixScene(context, audioTextureId, textureWidth))); 136 | mSceneList.add(Pair.create("Rainbow Spectrum", new RainbowSpectrumScene(context, audioTextureId, textureWidth))); 137 | mSceneList.add(Pair.create("Chlast", new ChlastScene(context, audioTextureId, textureWidth))); 138 | mSceneList.add(Pair.create("Origin Texture", new OriginScene(context, audioTextureId, textureWidth))); 139 | 140 | changeScene(defaultScene); 141 | 142 | invalidateOptionsMenu(); 143 | } 144 | }); 145 | 146 | /* 147 | Setup media player and play music 148 | */ 149 | mPlayer = MediaPlayer.create(this, R.raw.music); 150 | mPlayer.setLooping(true); 151 | mPlayer.start(); 152 | 153 | mVisualizer = new Visualizer(mPlayer.getAudioSessionId()); 154 | mVisualizer.setCaptureSize(captureSize); 155 | mVisualizer.setDataCaptureListener(this, Visualizer.getMaxCaptureRate(), true, true); 156 | // Start capturing 157 | mVisualizer.setEnabled(true); 158 | } 159 | 160 | @Override 161 | public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) { 162 | mRender.updateWaveFormFrame(new WaveFormFrame(waveform, 0, waveform.length / 2)); 163 | } 164 | 165 | @Override 166 | public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) { 167 | mRender.updateFFTFrame(new FFTFrame(fft, 0, fft.length / 2)); 168 | } 169 | 170 | @Override 171 | public boolean onCreateOptionsMenu(Menu menu) { 172 | menu.clear(); 173 | if (mSceneList != null) { 174 | int id = 0; 175 | for (Pair pair : mSceneList) { 176 | menu.add(0, id, id, pair.first); 177 | id ++; 178 | } 179 | } 180 | return super.onCreateOptionsMenu(menu); 181 | } 182 | 183 | @Override 184 | public boolean onOptionsItemSelected(MenuItem item) { 185 | if (mSceneList != null && mSceneController != null) { 186 | final GLScene scene = mSceneList.get(item.getItemId()).second; 187 | mSceneController.changeScene(scene); 188 | } 189 | return super.onOptionsItemSelected(item); 190 | } 191 | 192 | @Override 193 | protected void onDestroy() { 194 | super.onDestroy(); 195 | if (mPlayer != null) { 196 | mVisualizer.setEnabled(false); 197 | mPlayer.release(); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/WaveFormFrame.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz; 18 | 19 | /** 20 | * @author nekocode (nekocode.cn@gmail.com) 21 | */ 22 | public class WaveFormFrame { 23 | private byte[] mRawWaveForm; 24 | 25 | 26 | public WaveFormFrame(byte[] waveform, int offset, int len) { 27 | if (offset + len > waveform.length) throw new RuntimeException("Illegal offset and len"); 28 | 29 | mRawWaveForm = new byte[len]; 30 | System.arraycopy(waveform, offset, mRawWaveForm, 0, len); 31 | } 32 | 33 | public byte[] getRawWaveForm() { 34 | return mRawWaveForm; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/render/GLScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.render; 18 | 19 | import android.opengl.GLES20; 20 | import android.support.annotation.CallSuper; 21 | 22 | import java.nio.ByteBuffer; 23 | import java.nio.ByteOrder; 24 | import java.nio.FloatBuffer; 25 | 26 | /** 27 | * @author nekocode (nekocode.cn@gmail.com) 28 | */ 29 | public abstract class GLScene { 30 | private static final float SQUARE_COORDS[] = { 31 | 1.0f, -1.0f, 32 | -1.0f, -1.0f, 33 | 1.0f, 1.0f, 34 | -1.0f, 1.0f, 35 | }; 36 | private static final float TEXTURE_COORDS[] = { 37 | 1.0f, 0.0f, 38 | 0.0f, 0.0f, 39 | 1.0f, 1.0f, 40 | 0.0f, 1.0f, 41 | }; 42 | private static FloatBuffer VERTEX_BUF, TEXTURE_COORD_BUF; 43 | static { 44 | // Setup default Buffers 45 | VERTEX_BUF = ByteBuffer.allocateDirect(SQUARE_COORDS.length * 4) 46 | .order(ByteOrder.nativeOrder()).asFloatBuffer(); 47 | VERTEX_BUF.put(SQUARE_COORDS); 48 | VERTEX_BUF.position(0); 49 | 50 | TEXTURE_COORD_BUF = ByteBuffer.allocateDirect(TEXTURE_COORDS.length * 4) 51 | .order(ByteOrder.nativeOrder()).asFloatBuffer(); 52 | TEXTURE_COORD_BUF.put(TEXTURE_COORDS); 53 | TEXTURE_COORD_BUF.position(0); 54 | } 55 | 56 | private long mStartTime = System.currentTimeMillis(); 57 | private int iFrame = 0; 58 | 59 | 60 | @CallSuper 61 | public void reset() { 62 | iFrame = 0; 63 | mStartTime = System.currentTimeMillis(); 64 | } 65 | 66 | public final void draw(int canvasWidth, int canvasHeight) { 67 | onDraw(canvasWidth, canvasHeight); 68 | iFrame++; 69 | } 70 | 71 | public abstract void onDraw(int canvasWidth, int canvasHeight); 72 | 73 | protected void runShandertoyProgram( 74 | int program, int[] iResolution, int[] iChannels, int[][] iChannelResolutions) { 75 | 76 | runShandertoyProgram(program, VERTEX_BUF, TEXTURE_COORD_BUF, iResolution, iChannels, iChannelResolutions); 77 | } 78 | 79 | protected void runShandertoyProgram( 80 | int program, FloatBuffer vertex, FloatBuffer textureCoord, 81 | int[] iResolution, int[] iChannels, int[][] iChannelResolutions) { 82 | 83 | GLES20.glUseProgram(program); 84 | 85 | int iResolutionLocation = GLES20.glGetUniformLocation(program, "iResolution"); 86 | GLES20.glUniform3fv(iResolutionLocation, 1, 87 | FloatBuffer.wrap(new float[]{(float) iResolution[0], (float) iResolution[1], 1.0f})); 88 | 89 | float time = ((float) (System.currentTimeMillis() - mStartTime)) / 1000.0f; 90 | int iGlobalTimeLocation = GLES20.glGetUniformLocation(program, "iTime"); 91 | GLES20.glUniform1f(iGlobalTimeLocation, time); 92 | 93 | int iFrameLocation = GLES20.glGetUniformLocation(program, "iFrame"); 94 | GLES20.glUniform1i(iFrameLocation, iFrame); 95 | 96 | int vPositionLocation = GLES20.glGetAttribLocation(program, "vPosition"); 97 | GLES20.glEnableVertexAttribArray(vPositionLocation); 98 | GLES20.glVertexAttribPointer(vPositionLocation, 2, GLES20.GL_FLOAT, false, 4 * 2, vertex); 99 | 100 | int vTexCoordLocation = GLES20.glGetAttribLocation(program, "vTexCoord"); 101 | GLES20.glEnableVertexAttribArray(vTexCoordLocation); 102 | GLES20.glVertexAttribPointer(vTexCoordLocation, 2, GLES20.GL_FLOAT, false, 4 * 2, textureCoord); 103 | 104 | for (int i = 0; i < iChannels.length; i++) { 105 | int sTextureLocation = GLES20.glGetUniformLocation(program, "iChannel" + i); 106 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); 107 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iChannels[i]); 108 | GLES20.glUniform1i(sTextureLocation, i); 109 | } 110 | 111 | float _iChannelResolutions[] = new float[iChannelResolutions.length * 3]; 112 | for (int i = 0; i < iChannelResolutions.length; i++) { 113 | _iChannelResolutions[i * 3] = iChannelResolutions[i][0]; 114 | _iChannelResolutions[i * 3 + 1] = iChannelResolutions[i][1]; 115 | _iChannelResolutions[i * 3 + 2] = 1.0f; 116 | } 117 | 118 | int iChannelResolutionLocation = GLES20.glGetUniformLocation(program, "iChannelResolution"); 119 | GLES20.glUniform3fv(iChannelResolutionLocation, 120 | _iChannelResolutions.length, FloatBuffer.wrap(_iChannelResolutions)); 121 | 122 | // Draw 123 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/render/MyGLUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.render; 18 | 19 | import android.content.Context; 20 | import android.content.res.Resources; 21 | import android.graphics.Bitmap; 22 | import android.graphics.BitmapFactory; 23 | import android.opengl.GLES20; 24 | import android.opengl.GLUtils; 25 | import android.support.annotation.RawRes; 26 | import android.util.Log; 27 | 28 | import java.io.ByteArrayOutputStream; 29 | import java.io.IOException; 30 | import java.io.InputStream; 31 | 32 | import javax.microedition.khronos.opengles.GL10; 33 | 34 | /** 35 | * @author nekocode (nekocode.cn@gmail.com) 36 | */ 37 | public class MyGLUtils { 38 | private static final String TAG = "MyGLUtils"; 39 | 40 | 41 | public static int genTexture() { 42 | int[] genBuf = new int[1]; 43 | GLES20.glGenTextures(1, genBuf, 0); 44 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, genBuf[0]); 45 | 46 | // Set texture default draw parameters 47 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); 48 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 49 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); 50 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT); 51 | 52 | return genBuf[0]; 53 | } 54 | 55 | public static int loadTexture(final Context context, final int resourceId, int[] size) { 56 | final int texId = genTexture(); 57 | 58 | if (texId != 0) { 59 | final BitmapFactory.Options options = new BitmapFactory.Options(); 60 | options.inScaled = false; // No pre-scaling 61 | options.inJustDecodeBounds = true; 62 | 63 | // Just decode bounds 64 | BitmapFactory.decodeResource(context.getResources(), resourceId, options); 65 | 66 | // Set return size 67 | size[0] = options.outWidth; 68 | size[1] = options.outHeight; 69 | 70 | // Decode 71 | options.inJustDecodeBounds = false; 72 | Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options); 73 | 74 | // Load the bitmap into the bound texture. 75 | GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 76 | 77 | // Recycle the bitmap, since its data has been loaded into OpenGL. 78 | bitmap.recycle(); 79 | } 80 | 81 | return texId; 82 | } 83 | 84 | public static int buildProgram(Context context, @RawRes int vertexSourceRawId, @RawRes int fragmentSourceRawId) { 85 | return buildProgram(getStringFromRaw(context, vertexSourceRawId), 86 | getStringFromRaw(context, fragmentSourceRawId)); 87 | } 88 | 89 | public static int buildProgram(String vertexSource, String fragmentSource) { 90 | final int vertexShader = buildShader(GLES20.GL_VERTEX_SHADER, vertexSource); 91 | if (vertexShader == 0) { 92 | return 0; 93 | } 94 | 95 | final int fragmentShader = buildShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 96 | if (fragmentShader == 0) { 97 | return 0; 98 | } 99 | 100 | final int program = GLES20.glCreateProgram(); 101 | if (program == 0) { 102 | return 0; 103 | } 104 | 105 | GLES20.glAttachShader(program, vertexShader); 106 | GLES20.glAttachShader(program, fragmentShader); 107 | GLES20.glLinkProgram(program); 108 | 109 | return program; 110 | } 111 | 112 | public static int buildShader(int type, String shaderSource) { 113 | final int shader = GLES20.glCreateShader(type); 114 | if (shader == 0) { 115 | return 0; 116 | } 117 | 118 | GLES20.glShaderSource(shader, shaderSource); 119 | GLES20.glCompileShader(shader); 120 | 121 | int[] status = new int[1]; 122 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0); 123 | if (status[0] == 0) { 124 | Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); 125 | GLES20.glDeleteShader(shader); 126 | return 0; 127 | } 128 | 129 | return shader; 130 | } 131 | 132 | private static String getStringFromRaw(Context context, @RawRes int id) { 133 | String str; 134 | try { 135 | Resources r = context.getResources(); 136 | InputStream is = r.openRawResource(id); 137 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 138 | int i = is.read(); 139 | while (i != -1) { 140 | baos.write(i); 141 | i = is.read(); 142 | } 143 | 144 | str = baos.toString(); 145 | is.close(); 146 | } catch (IOException e) { 147 | str = ""; 148 | } 149 | 150 | return str; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/render/RenderBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.render; 18 | 19 | import android.opengl.GLES20; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.nio.ByteOrder; 23 | import java.nio.IntBuffer; 24 | 25 | import javax.microedition.khronos.opengles.GL10; 26 | 27 | /** 28 | * @author nekocode (nekocode.cn@gmail.com) 29 | */ 30 | public class RenderBuffer { 31 | private int texId = 0; 32 | private int activeTexUnit = 0; 33 | private int renderBufferId = 0; 34 | private int frameBufferId = 0; 35 | 36 | private int width, height; 37 | 38 | public RenderBuffer(int width, int height, int activeTexUnit) { 39 | this.width = width; 40 | this.height = height; 41 | this.activeTexUnit = activeTexUnit; 42 | int[] genbuf = new int[1]; 43 | 44 | // Generate and bind 2d texture 45 | GLES20.glActiveTexture(activeTexUnit); 46 | texId = MyGLUtils.genTexture(); 47 | IntBuffer texBuffer = 48 | ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()).asIntBuffer(); 49 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, texBuffer); 50 | 51 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); 52 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 53 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); 54 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); 55 | 56 | // Generate frame buffer 57 | GLES20.glGenFramebuffers(1, genbuf, 0); 58 | frameBufferId = genbuf[0]; 59 | // Bind frame buffer 60 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId); 61 | 62 | // Generate render buffer 63 | GLES20.glGenRenderbuffers(1, genbuf, 0); 64 | renderBufferId = genbuf[0]; 65 | // Bind render buffer 66 | GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBufferId); 67 | GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width, height); 68 | 69 | unbind(); 70 | } 71 | 72 | public int getTexId() { 73 | return texId; 74 | } 75 | 76 | public int getWidth() { 77 | return width; 78 | } 79 | 80 | public int getHeight() { 81 | return height; 82 | } 83 | 84 | public int getActiveTexUnit() { 85 | return activeTexUnit; 86 | } 87 | 88 | public void bind() { 89 | GLES20.glViewport(0, 0, width, height); 90 | 91 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId); 92 | GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, 93 | GLES20.GL_TEXTURE_2D, texId, 0); 94 | GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, 95 | GLES20.GL_RENDERBUFFER, renderBufferId); 96 | } 97 | 98 | public void unbind() { 99 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/render/SceneController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.render; 18 | 19 | import android.content.Context; 20 | 21 | /** 22 | * @author nekocode (nekocode.cn@gmail.com) 23 | */ 24 | public abstract class SceneController { 25 | private GLScene mActivedScene; 26 | 27 | 28 | abstract public void onSetup(Context context, int audioTextureId, int textureWidth); 29 | 30 | public void changeScene(GLScene scene) { 31 | scene.reset(); 32 | mActivedScene = scene; 33 | } 34 | 35 | GLScene getActivedScene() { 36 | return mActivedScene; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/render/VisualizerRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.render; 18 | 19 | import android.content.Context; 20 | import android.graphics.SurfaceTexture; 21 | import android.opengl.GLES20; 22 | import android.view.TextureView; 23 | 24 | import java.nio.ByteBuffer; 25 | 26 | import javax.microedition.khronos.egl.EGL10; 27 | import javax.microedition.khronos.egl.EGLConfig; 28 | import javax.microedition.khronos.egl.EGLContext; 29 | import javax.microedition.khronos.egl.EGLDisplay; 30 | import javax.microedition.khronos.egl.EGLSurface; 31 | import javax.microedition.khronos.opengles.GL10; 32 | 33 | import cn.nekocode.musicviz.FFTFrame; 34 | import cn.nekocode.musicviz.WaveFormFrame; 35 | 36 | /** 37 | * @author nekocode (nekocode.cn@gmail.com) 38 | */ 39 | public class VisualizerRenderer implements Runnable, TextureView.SurfaceTextureListener { 40 | private static final int EGL_OPENGL_ES2_BIT = 4; 41 | private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 42 | private static final int DRAW_INTERVAL = 1000 / 30; 43 | private static final float DBM_INTERP = 0.75f; 44 | 45 | private Thread mRenderThread; 46 | private Context mContext; 47 | private SurfaceTexture mSurfaceTexture; 48 | private int mSurfaceWidth, mSurfaceHeight; 49 | 50 | private EGLDisplay mEglDisplay; 51 | private EGLSurface mEglSurface; 52 | private EGLContext mEglContext; 53 | private EGL10 mEgl10; 54 | 55 | private SceneController mSceneController; 56 | private int mTextureWidth; 57 | private WaveFormFrame mWaveFormFrame; 58 | private FFTFrame mFFTFrame; 59 | private float[] mLastDbmArray; 60 | 61 | 62 | public VisualizerRenderer(Context context, int textureWidth) { 63 | this.mContext = context; 64 | this.mTextureWidth = textureWidth; 65 | } 66 | 67 | public void setSceneController(SceneController controller) { 68 | mSceneController = controller; 69 | } 70 | 71 | public void updateWaveFormFrame(WaveFormFrame frame) { 72 | this.mWaveFormFrame = frame; 73 | } 74 | 75 | public void updateFFTFrame(FFTFrame frame) { 76 | this.mFFTFrame = frame; 77 | } 78 | 79 | @Override 80 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { 81 | } 82 | 83 | @Override 84 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 85 | mSurfaceWidth = -width; 86 | mSurfaceHeight = -height; 87 | } 88 | 89 | @Override 90 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 91 | if (mRenderThread != null && mRenderThread.isAlive()) { 92 | mRenderThread.interrupt(); 93 | } 94 | 95 | return true; 96 | } 97 | 98 | @Override 99 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 100 | if (mRenderThread != null && mRenderThread.isAlive()) { 101 | mRenderThread.interrupt(); 102 | } 103 | mRenderThread = new Thread(this); 104 | 105 | mSurfaceTexture = surface; 106 | mSurfaceWidth = -width; 107 | mSurfaceHeight = -height; 108 | 109 | // Start rendering 110 | mRenderThread.start(); 111 | } 112 | 113 | @Override 114 | public void run() { 115 | if (!initGL(mSurfaceTexture)) { 116 | throw new RuntimeException("Initializing OpenGL failed"); 117 | } 118 | 119 | final byte[] buf = new byte[mTextureWidth * 2]; 120 | final int audioTexId = genAudioTexture(buf, mTextureWidth); 121 | if (mSceneController != null) { 122 | mSceneController.onSetup(mContext, audioTexId, mTextureWidth); 123 | } 124 | 125 | 126 | // Render loop 127 | while (!Thread.currentThread().isInterrupted()) { 128 | try { 129 | if (mSurfaceWidth < 0 && mSurfaceHeight < 0) 130 | GLES20.glViewport(0, 0, mSurfaceWidth = -mSurfaceWidth, mSurfaceHeight = -mSurfaceHeight); 131 | 132 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 133 | GLES20.glClearColor(1f, 0f, 0f, 0f); 134 | 135 | 136 | // Prepare the audio texture 137 | fillFFT(buf, 0, mTextureWidth); 138 | if (mWaveFormFrame != null) { 139 | System.arraycopy(mWaveFormFrame.getRawWaveForm(), 0, buf, mTextureWidth, mTextureWidth); 140 | } 141 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 142 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, audioTexId); 143 | GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mTextureWidth, 2, 144 | GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(buf)); 145 | 146 | 147 | // Draw 148 | GLScene scene; 149 | if (mSceneController != null && (scene = mSceneController.getActivedScene()) != null) { 150 | scene.draw(mSurfaceWidth, mSurfaceHeight); 151 | } 152 | 153 | // Flush 154 | GLES20.glFlush(); 155 | mEgl10.eglSwapBuffers(mEglDisplay, mEglSurface); 156 | 157 | Thread.sleep(DRAW_INTERVAL); 158 | 159 | } catch (InterruptedException e) { 160 | Thread.currentThread().interrupt(); 161 | } 162 | } 163 | 164 | // Release something 165 | GLES20.glDeleteTextures(1, new int[]{audioTexId}, 0); 166 | } 167 | 168 | private void fillFFT(byte[] buf, int offset, int len) { 169 | if (mFFTFrame == null) return; 170 | if (offset + len > buf.length) return; 171 | 172 | float[] dBmArray = new float[len]; 173 | mFFTFrame.calculate(len, dBmArray); 174 | 175 | float maxDbm = Float.MIN_VALUE; 176 | float minDbm = Float.MAX_VALUE; 177 | for (int i = 0; i < len; i++) { 178 | float dbm = dBmArray[i]; 179 | if (dbm < 0f) dbm = 0f; 180 | if (dbm > 1f) dbm = 1f; 181 | 182 | if (mLastDbmArray != null) { 183 | float oldDbm = mLastDbmArray[i]; 184 | if (oldDbm - dbm > 0.025f) { 185 | dbm = oldDbm - 0.025f; 186 | } else { 187 | dbm = mLastDbmArray[i] * DBM_INTERP + dbm * (1f - DBM_INTERP); 188 | } 189 | } 190 | 191 | if (dbm > maxDbm) maxDbm = dbm; 192 | if (dbm < minDbm) minDbm = dbm; 193 | 194 | dBmArray[i] = dbm; 195 | buf[offset + i] = (byte) (dbm * 255); 196 | } 197 | 198 | // float midDbm = (maxDbm + minDbm) / 2f; 199 | // 200 | // for (int i = 0; i < len; i++) { 201 | // float dbm = dBmArray[i]; 202 | // dbm = midDbm * 0.2f + dbm * 0.8f; 203 | // buf[offset + i] = (byte) (dbm * 255); 204 | // } 205 | mLastDbmArray = dBmArray; 206 | } 207 | 208 | private int genAudioTexture(byte[] buf, int width) { 209 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 210 | 211 | final int[] genBuf = new int[1]; 212 | GLES20.glGenTextures(1, genBuf, 0); 213 | final int texId = genBuf[0]; 214 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId); 215 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); 216 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); 217 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); 218 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); 219 | 220 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId); 221 | GLES20.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_LUMINANCE, 222 | width, 2, 0, GL10.GL_LUMINANCE, GL10.GL_UNSIGNED_BYTE, 223 | ByteBuffer.wrap(buf)); 224 | 225 | return texId; 226 | } 227 | 228 | private boolean initGL(SurfaceTexture texture) { 229 | mEgl10 = (EGL10) EGLContext.getEGL(); 230 | 231 | mEglDisplay = mEgl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 232 | if (mEglDisplay == EGL10.EGL_NO_DISPLAY) return false; 233 | 234 | int[] version = new int[2]; 235 | if (!mEgl10.eglInitialize(mEglDisplay, version)) return false; 236 | 237 | int[] configsCount = new int[1]; 238 | EGLConfig[] configs = new EGLConfig[1]; 239 | int[] configSpec = { 240 | EGL10.EGL_RENDERABLE_TYPE, 241 | EGL_OPENGL_ES2_BIT, 242 | EGL10.EGL_RED_SIZE, 8, 243 | EGL10.EGL_GREEN_SIZE, 8, 244 | EGL10.EGL_BLUE_SIZE, 8, 245 | EGL10.EGL_ALPHA_SIZE, 8, 246 | EGL10.EGL_DEPTH_SIZE, 0, 247 | EGL10.EGL_STENCIL_SIZE, 0, 248 | EGL10.EGL_NONE 249 | }; 250 | 251 | EGLConfig eglConfig = null; 252 | if (!mEgl10.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { 253 | return false; 254 | } else if (configsCount[0] > 0) { 255 | eglConfig = configs[0]; 256 | } 257 | if (eglConfig == null) return false; 258 | 259 | mEglContext = mEgl10.eglCreateContext( 260 | mEglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, 261 | new int[]{EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}); 262 | mEglSurface = mEgl10.eglCreateWindowSurface(mEglDisplay, eglConfig, texture, null); 263 | 264 | if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) return false; 265 | if (!mEgl10.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) return false; 266 | 267 | return true; 268 | } 269 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/BasicSpectrumScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class BasicSpectrumScene extends CommonScene { 27 | 28 | public BasicSpectrumScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.basic_spectrum); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/ChlastScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class ChlastScene extends CommonScene { 27 | 28 | public ChlastScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.chlast); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/CircSpectrumScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class CircSpectrumScene extends CommonScene { 27 | 28 | public CircSpectrumScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.circ_spectrum); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/CommonScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | import cn.nekocode.musicviz.render.GLScene; 23 | import cn.nekocode.musicviz.render.MyGLUtils; 24 | 25 | /** 26 | * @author nekocode (nekocode.cn@gmail.com) 27 | */ 28 | public class CommonScene extends GLScene { 29 | private int mProgram; 30 | private int mAudioTextureId; 31 | private int mTexttureWidth; 32 | 33 | 34 | public CommonScene(Context context, int audioTextureId, int textureWidth, int programResId) { 35 | mProgram = MyGLUtils.buildProgram(context, R.raw.vertext, programResId); 36 | mAudioTextureId = audioTextureId; 37 | mTexttureWidth = textureWidth; 38 | } 39 | 40 | @Override 41 | public void onDraw(int canvasWidth, int canvasHeight) { 42 | runShandertoyProgram( 43 | mProgram, 44 | new int[]{canvasWidth, canvasHeight}, 45 | new int[]{mAudioTextureId}, 46 | new int[][]{new int[]{mTexttureWidth, 2}} 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/EnhancedSpectrumScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class EnhancedSpectrumScene extends CommonScene { 27 | 28 | public EnhancedSpectrumScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.enhanced_spectrum); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/InputSoundScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class InputSoundScene extends CommonScene { 27 | 28 | public InputSoundScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.input_sound); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/OriginScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class OriginScene extends CommonScene { 27 | 28 | public OriginScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.origin); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/RainbowSpectrumScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class RainbowSpectrumScene extends CommonScene { 27 | 28 | public RainbowSpectrumScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.rainbow_spectrum); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/Sa2WaveScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class Sa2WaveScene extends CommonScene { 27 | 28 | public Sa2WaveScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.sa2_wave); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/cn/nekocode/musicviz/scene/WavesRemixScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 nekocode 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package cn.nekocode.musicviz.scene; 18 | 19 | import android.content.Context; 20 | 21 | import cn.nekocode.musicviz.R; 22 | 23 | /** 24 | * @author nekocode (nekocode.cn@gmail.com) 25 | */ 26 | public class WavesRemixScene extends CommonScene { 27 | 28 | public WavesRemixScene(Context context, int audioTextureId, int textureWidth) { 29 | super(context, audioTextureId, textureWidth, R.raw.waves_remix); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/raw/basic_spectrum.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/XdX3z2 2 | 3 | varying vec2 fragCoord; 4 | uniform sampler2D iChannel0; 5 | 6 | #define bars 16.0 // How many buckets to divide spectrum into 7 | #define barSize 1.0 / bars // Constant to avoid division in main loop 8 | #define barGap 0.1 * barSize // 0.1 represents gap on both sides, so a bar is 9 | // shown to be 80% as wide as the spectrum it samples 10 | #define sampleSize 0.02 // How accurately to sample spectrum, must be a factor of 1.0 11 | // Setting this too low may crash your browser! 12 | 13 | // Helper for intensityToColour 14 | float h2rgb(float h) { 15 | if(h < 0.0) h += 1.0; 16 | if(h < 0.166666) return 0.1 + 4.8 * h; 17 | if(h < 0.5) return 0.9; 18 | if(h < 0.666666) return 0.1 + 4.8 * (0.666666 - h); 19 | return 0.1; 20 | } 21 | 22 | // Map [0, 1] to rgb using hues from [240, 0] - ie blue to red 23 | vec3 intensityToColour(float i) { 24 | // Algorithm rearranged from http://www.w3.org/TR/css3-color/#hsl-color 25 | // with s = 0.8, l = 0.5 26 | float h = 0.666666 - (i * 0.666666); 27 | 28 | return vec3(h2rgb(h + 0.333333), h2rgb(h), h2rgb(h - 0.333333)); 29 | } 30 | 31 | void main() { 32 | // Map input coordinate to [0, 1) 33 | vec2 uv = fragCoord.xy; 34 | 35 | // Get the starting x for this bar by rounding down 36 | float barStart = floor(uv.x * bars) / bars; 37 | 38 | // Discard pixels in the 'gap' between bars 39 | if(uv.x - barStart < barGap || uv.x > barStart + barSize - barGap) { 40 | gl_FragColor = vec4(0.0); 41 | } 42 | else 43 | { 44 | // Sample spectrum in bar area, keep cumulative total 45 | float intensity = 0.0; 46 | for(float s = 0.0; s < barSize; s += barSize * sampleSize) { 47 | // Shader toy shows loudness at a given frequency at (f, 0) with the same value in all channels 48 | intensity += texture2D(iChannel0, vec2(barStart + s, 0.0)).r; 49 | } 50 | intensity *= sampleSize; // Divide total by number of samples taken (which is 1 / sampleSize) 51 | intensity = clamp(intensity, 0.005, 1.0); // Show silent spectrum to be just poking out of the bottom 52 | 53 | // Only want to keep this pixel if it is lower (screenwise) than this bar is loud 54 | float i = float(intensity > uv.y); // Casting a comparison to float sets i to 0.0 or 1.0 55 | 56 | //gl_FragColor = vec4(intensityToColour(uv.x), 1.0); // Demo of HSL function 57 | //gl_FragColor = vec4(i); // Black and white output 58 | gl_FragColor = vec4(intensityToColour(intensity) * i, i); // Final output 59 | } 60 | // Note that i2c output is multiplied by i even though i is sent in the alpha channel 61 | // This is because alpha is not 'pre-multiplied' for fragment shaders, you need to do it yourself 62 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/chlast.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/Xdl3W2 2 | 3 | varying vec2 fragCoord; 4 | uniform vec3 iResolution; 5 | uniform sampler2D iChannel0; 6 | uniform float iTime; 7 | 8 | float sp1 = 0.0; 9 | float timesum = 0.0; 10 | float timesum2 = 0.0; 11 | 12 | float intersect( vec3 ro, vec3 rd, vec4 sphere, float reps ) 13 | { 14 | float r = sphere.w + sp1 * 3.0; 15 | 16 | vec3 sc = ro - sphere.xyz; 17 | 18 | float t = ( -ro.z + sphere.z ) / rd.z; 19 | vec3 pt = ro + t * rd; 20 | vec3 diff = pt - sphere.xyz; 21 | float wave1 = sin( atan(diff.y,diff.x) * reps + sp1 + timesum ) * 1.4; 22 | float wave2 = sin( atan(diff.y,diff.x) + sp1 + iTime ) * 10.0; 23 | r+= ( wave1 + texture2D( iChannel0, vec2( wave2 * 0.5, 0.0 ) ).x ) * 0.4; 24 | r+= sp1 * 2.0; 25 | float sum = 0.0; 26 | float test1 = sphere.w + r - length(diff); 27 | float test2 = sphere.w * 0.25 + r - length(diff); 28 | sum -= ( test1 ) > 4.0 ? ( test2 ) < 4.0 ? 3.0 : 0.0 : 0.0; 29 | return sum; 30 | } 31 | 32 | vec3 rotateY( vec3 v, float a ) 33 | { 34 | return vec3( sin(a)*v.x-cos(a)*v.z, v.y, cos(a)*v.x+sin(a)*v.z ); 35 | } 36 | 37 | float color( vec2 uv2 ) 38 | { 39 | vec3 ro = vec3( 0.0, 0.0, 10.0 ); 40 | ro = rotateY( ro, cos( timesum2 * 0.05 ) + 1.57 ); 41 | vec3 rd = -normalize( vec3( uv2, 1.0 ) ); 42 | rd = rotateY( rd, cos( timesum2 * 0.05 ) + 1.57 ); 43 | 44 | float res = 0.0; 45 | 46 | // shapes 47 | res += intersect( ro, rd, vec4( 0.0, 0.0, 0.0, 4.0 ), 10.0 ); 48 | res += intersect( ro, rd, vec4( 8.0, 0.0, -5.0, 1.0 ), 3.0 ); 49 | res += intersect( ro, rd, vec4( -8.0, 0.0, -5.0, 1.0 ), 3.0 ); 50 | if ( res < 0.0 ) 51 | { 52 | res = abs(res*1.0); 53 | } 54 | else 55 | { 56 | res = 0.0; 57 | } 58 | 59 | return res; 60 | } 61 | 62 | void main() 63 | { 64 | sp1 = texture2D( iChannel0, vec2( .0, 0.0) ).x; 65 | vec2 uv = fragCoord.xy; 66 | 67 | timesum = ( -iTime + abs(sp1 * 1.0) ) * 30.0; 68 | timesum2 = ( iTime + abs(sp1 * 0.5) ) * 30.0; 69 | 70 | vec2 uv2 = uv * 2.0 - 1.0; 71 | uv2.x *= 1.78; 72 | float sh1 = texture2D( iChannel0, vec2( uv.y, .5 ) ).x - 0.25; 73 | float sh2 = texture2D( iChannel0, vec2( uv.y * 0.5, .5 ) ).x - 0.25; 74 | float sh3 = texture2D( iChannel0, vec2( uv.y * 0.75, .5 ) ).x - 0.25; 75 | float camult1 = 0.1; 76 | float camult2 = 0.2; 77 | float camult3 = 0.3; 78 | vec3 c = vec3( color( uv2 + vec2( camult1 * sh1, 0.0 ) ), 79 | color( uv2 + vec2( camult2 * sh2, 0.0 ) ), 80 | color( uv2 + vec2( camult3 * sh3, 0.0 ) ) ); 81 | 82 | float tanargmul = 0.5; 83 | vec3 bg = vec3(( 1.0 - pow( abs( tan( uv2.x * tanargmul ) * tan( uv2.y * tanargmul ) ), 2.0 ) ) * 0.5); 84 | bg.r *= 0.5; 85 | bg.g *= 0.7; 86 | gl_FragColor = vec4( clamp( c + bg, 0.0, 1.0 ), 1.0 ); 87 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/circ_spectrum.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/4djGDy 2 | 3 | varying vec2 fragCoord; 4 | uniform vec3 iResolution; 5 | uniform sampler2D iChannel0; 6 | 7 | 8 | void main() 9 | { 10 | vec2 center = vec2(iResolution.x / 2.0, iResolution.y / 2.0); 11 | float dist = distance(fragCoord.xy * iResolution.xy, center); 12 | 13 | float radius = 1.0 + 0.4 * iResolution.y * texture2D(iChannel0, vec2(fragCoord.x, 0.25)).x; 14 | 15 | if (fragCoord.x > 0.25 && fragCoord.x < 0.75 && dist < (radius * 0.5)) 16 | { 17 | vec3 color = vec3(1.0, 1.0, 1.0); 18 | float value = dist / radius; 19 | gl_FragColor = vec4((1.0 - value) * color.x, (1.0 - value) * color.y, (1.0 - value) * color.z, 1.0); 20 | } 21 | else 22 | { 23 | gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/enhanced_spectrum.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/4sySDt 2 | 3 | varying vec2 fragCoord; 4 | uniform vec3 iResolution; 5 | uniform sampler2D iChannel0; 6 | uniform float iTime; 7 | 8 | vec3 B2_spline(vec3 x) { // returns 3 B-spline functions of degree 2 9 | vec3 t = 3.0 * x; 10 | vec3 b0 = step(0.0, t) * step(0.0, 1.0-t); 11 | vec3 b1 = step(0.0, t-1.0) * step(0.0, 2.0-t); 12 | vec3 b2 = step(0.0, t-2.0) * step(0.0, 3.0-t); 13 | return 0.5 * ( 14 | b0 * pow(t, vec3(2.0)) + 15 | b1 * (-2.0*pow(t, vec3(2.0)) + 6.0*t - 3.0) + 16 | b2 * pow(3.0-t,vec3(2.0)) 17 | ); 18 | } 19 | 20 | void main() 21 | { 22 | // create pixel coordinates 23 | vec2 uv = fragCoord.xy; 24 | 25 | float fVBars = 100.; 26 | float fHSpacing = 1.00; 27 | 28 | 29 | float x = floor(uv.x * fVBars)/fVBars; 30 | float fSample = texture2D( iChannel0, vec2(abs(2.0 * x - 1.0), 0.25)).x; 31 | 32 | float squarewave = sign(mod(uv.x, 1.0/fVBars)-0.004); 33 | float fft = squarewave * fSample* 0.5; 34 | 35 | float fHBars = 100.0; 36 | float fVSpacing = 0.180; 37 | float fVFreq = (uv.y * 3.14); 38 | fVFreq = sign(sin(fVFreq * fHBars)+1.0-fVSpacing); 39 | 40 | vec2 centered = vec2(1.0) * uv - vec2(1.0); 41 | float t = iTime / 100.0; 42 | float polychrome = 1.0; 43 | vec3 spline_args = fract(vec3(polychrome*uv.x-t) + vec3(0.0, -1.0/3.0, -2.0/3.0)); 44 | vec3 spline = B2_spline(spline_args); 45 | 46 | float f = abs(centered.y); 47 | vec3 base_color = vec3(1.0, 1.0, 1.0) - f*spline; 48 | vec3 flame_color = pow(base_color, vec3(3.0)); 49 | 50 | float tt = 0.3 - uv.y; 51 | float df = sign(tt); 52 | df = (df + 1.0)/0.5; 53 | vec3 col = flame_color * vec3(1.0 - step(fft, abs(0.3-uv.y))) * vec3(fVFreq); 54 | col -= col * df * 0.180; 55 | 56 | // output final color 57 | gl_FragColor = vec4(col,1.0); 58 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/input_sound.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/Xds3Rr 2 | 3 | varying vec2 fragCoord; 4 | uniform vec3 iResolution; 5 | uniform sampler2D iChannel0; 6 | 7 | void main() { 8 | // first row is frequency data 9 | float fft = texture2D(iChannel0, vec2(fragCoord.x, 0.25)).x; 10 | 11 | // second row is the sound wave, one texel is one mono sample 12 | float wave = texture2D(iChannel0, vec2(fragCoord.x, 0.75)).x; 13 | 14 | // convert frequency to colors 15 | vec3 col = vec3(fft, 4.0 * fft * (1.0 - fft), 1.0 - fft) * fft; 16 | 17 | // add wave form on top 18 | col += 1.0 - smoothstep(0.0, 0.02, abs(wave - fragCoord.y)); 19 | 20 | // output final color 21 | gl_FragColor = vec4(col, 1.0); 22 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/app/src/main/res/raw/music.mp3 -------------------------------------------------------------------------------- /app/src/main/res/raw/origin.fsh: -------------------------------------------------------------------------------- 1 | varying vec2 fragCoord; 2 | uniform sampler2D iChannel0; 3 | 4 | void main() { 5 | gl_FragColor = texture2D(iChannel0, fragCoord.xy); 6 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/rainbow_spectrum.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/ldX3D8 2 | 3 | varying vec2 fragCoord; 4 | uniform vec3 iResolution; 5 | uniform sampler2D iChannel0; 6 | uniform float iTime; 7 | 8 | float bump(float x) { 9 | return abs(x) > 1.0 ? 0.0 : 1.0 - x * x; 10 | } 11 | 12 | void main() 13 | { 14 | vec2 uv = fragCoord.xy; 15 | 16 | float c = 3.0; 17 | vec3 color = vec3(1.0); 18 | color.x = bump(c * (uv.x - 0.75)); 19 | color.y = bump(c * (uv.x - 0.5)); 20 | color.z = bump(c * (uv.x - 0.25)); 21 | 22 | float line = abs(0.01 / abs(0.5-uv.y) ); 23 | uv.y = abs( uv.y - 0.5 ); 24 | 25 | vec4 soundWave = texture2D( iChannel0, vec2(abs(0.5-uv.x)+0.005, 0.25) ).rrrr; 26 | color *= line * (1.0 - 2.0 * abs( 0.5 - uv.xxx ) + pow( soundWave.y, 10.0 ) * 30.0 ); 27 | 28 | gl_FragColor = vec4(color, 0.0); 29 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/sa2_wave.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/lsdGDH 2 | 3 | precision highp float; 4 | 5 | varying vec2 fragCoord; 6 | uniform vec3 iResolution; 7 | uniform sampler2D iChannel0; 8 | uniform float iTime; 9 | 10 | 11 | vec2 hash( vec2 p ) { // rand in [-1,1] 12 | p = vec2( dot(p,vec2(127.1,311.7)), 13 | dot(p,vec2(269.5,183.3)) ); 14 | return -1. + 2.*fract(sin(p+20.)*53758.5453123); 15 | } 16 | 17 | vec4 getFreq(float f) { 18 | float fft = texture2D( iChannel0, vec2(f, 0.25) ).x; 19 | vec3 col = vec3( fft, 4.0 * fft * (1.0 - fft), 1.0 - fft ) * fft; 20 | return max(vec4(col, 1.0), 0.); 21 | } 22 | 23 | #define k iTime * 6. 24 | #define t iTime * 6. 25 | #define v 2. 26 | #define fov 90. 27 | #define PI 3.1415 28 | 29 | void main() { 30 | 31 | vec2 uv = fragCoord.xy - .5; 32 | vec2 muv = uv; 33 | 34 | float volF = getFreq(uv.x + .5).r / v; 35 | float volM = getFreq(.9).r / v; 36 | float volL = getFreq( .25).r / v; 37 | vec4 c = vec4(.5, 0., .2 + sin(k / 16.), 0.) / 4. - .2; 38 | 39 | for (float i = 8.; i > 0.; i--) { 40 | 41 | muv = uv * tan (radians (i * 4. + fov)/2.0); 42 | 43 | muv.y += sin(k + volL + muv.x * 43.) * volM / 2.; 44 | muv.y += sin(2.3 * k + PI + 2. + muv.x * 127.) * volM / 4.; 45 | muv.y += sin(PI + 3. + muv.x * 32. + k * 3.12) * volL / 4.; 46 | 47 | if (muv.y < 0.02 && muv.y > -0.02) { 48 | c += max( 49 | vec4(1. - i * .15) * (1. - abs(muv.y) * 50.), 50 | .0 51 | ); 52 | } 53 | 54 | c -= (muv.y - .5) / 24.; 55 | 56 | } 57 | 58 | uv += .5; 59 | 60 | c += vec4(abs(sin(uv.y * PI) / 5.)) + hash(uv * 5. + t / 100.).x / 12.; 61 | c *= .2 + 0.6 * pow(16.0 * uv.x * uv.y * (1.0 - uv.x) * (1.0 - uv.y), 4.); 62 | 63 | gl_FragColor = c; 64 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/vertext.vsh: -------------------------------------------------------------------------------- 1 | attribute vec2 vPosition; 2 | attribute vec2 vTexCoord; 3 | varying vec2 fragCoord; 4 | 5 | void main() { 6 | fragCoord = vTexCoord; 7 | gl_Position = vec4 ( vPosition.x, vPosition.y, 0.0, 1.0 ); 8 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/waves_remix.fsh: -------------------------------------------------------------------------------- 1 | // Modified from https://www.shadertoy.com/view/4ljGD1 2 | 3 | varying vec2 fragCoord; 4 | uniform vec3 iResolution; 5 | uniform float iTime; 6 | uniform sampler2D iChannel0; 7 | 8 | float squared(float value) { return value * value; } 9 | 10 | float getAmp(float frequency) { return texture2D(iChannel0, vec2(frequency / 512.0, 0.25)).x; } // todo 11 | 12 | float getWeight(float f) { 13 | return (+ getAmp(f-2.0) + getAmp(f-1.0) + getAmp(f+2.0) + getAmp(f+1.0) + getAmp(f)) / 5.0; } 14 | 15 | void main() 16 | { 17 | vec2 uvTrue = fragCoord.xy; 18 | vec2 uv = -1.0 + 2.0 * uvTrue; 19 | 20 | float lineIntensity; 21 | float glowWidth; 22 | vec3 color = vec3(0.0); 23 | 24 | for(float i = 0.0; i < 3.0; i++) { 25 | 26 | uv.y += (0.2 * sin(uv.x + i/7.0 - iTime * 0.6)); 27 | float Y = uv.y + getWeight(squared(i) * 20.0) * 28 | (texture2D(iChannel0, vec2(uvTrue.x, 0.75)).x - 0.5); 29 | lineIntensity = 0.4 + squared(1.6 * abs(mod(uvTrue.x + i / 1.3 + iTime,2.0) - 1.0)); 30 | glowWidth = abs(lineIntensity / (150.0 * Y)); 31 | color += vec3(glowWidth * (2.0 + sin(iTime * 0.13)), 32 | glowWidth * (2.0 - sin(iTime * 0.23)), 33 | glowWidth * (2.0 - cos(iTime * 0.19))); 34 | } 35 | 36 | gl_FragColor = vec4(color, 1.0); 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0dp 4 | 0dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MusicVisualization 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | google() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:3.2.1' 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | jcenter() 14 | google() 15 | } 16 | } 17 | 18 | task clean(type: Delete) { 19 | delete rootProject.buildDir 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | COMPILE_SDK_VERSION=26 2 | BUILD_TOOLS_VERSION=26.0.2 3 | SUPPORT_LIBS_VERSION=27.0.2 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Jan 06 11:44:24 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /image/p1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/image/p1.png -------------------------------------------------------------------------------- /image/p2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/image/p2.png -------------------------------------------------------------------------------- /image/p3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/image/p3.png -------------------------------------------------------------------------------- /image/p4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/image/p4.png -------------------------------------------------------------------------------- /image/p5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekocode/MusicVisualization/96a98736e0e732a6c422de9a0e654c27a90f3e28/image/p5.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------