commit 2acdc4f03043a3a2cb6c39f18a6e36b3c9ee6f61 Author: Ahmet Inan Date: Sun Nov 23 15:49:23 2014 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afbdab3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..b02cc76 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Robot36 \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..217af47 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/.idea/copyright/Apache_License__Version_2_0.xml b/.idea/copyright/Apache_License__Version_2_0.xml new file mode 100644 index 0000000..01124f3 --- /dev/null +++ b/.idea/copyright/Apache_License__Version_2_0.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..ce73a06 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/ainan.xml b/.idea/dictionaries/ainan.xml new file mode 100644 index 0000000..5d44c9f --- /dev/null +++ b/.idea/dictionaries/ainan.xml @@ -0,0 +1,11 @@ + + + + ahmet + ainan + imag + inan + phasor + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..736c7b5 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b153e48 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c759cea --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..c80f219 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..5d1f55d --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +Robot36 +Copyright 2014 Ahmet Inan + +This software is written from scratch. diff --git a/README b/README new file mode 100644 index 0000000..c790748 --- /dev/null +++ b/README @@ -0,0 +1,38 @@ +Copyright 2014 Ahmet Inan + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +-- + +Always wanted to write an Android version of Robot36 the moment i got an Android device. +This way i would be able to show off to my friends on the go :) +Finally took the time to write this prototype in 2 weekends worth of time. +I intentionally wrote this (painfully) in Android Studio (not really an IDE using guy) +so that others (that means YOU) can start developing using this and eventually contribute. +Also wanted to try something new, so instead of using an complex FIR filter i used an complex +cascaded exponential moving average filter after mixing. + +And again, i would like to thank JL Barber (N7CXI) for the specifications in his "Dayton Paper". +I also would like to thank the Renderscript Developers, as their work made the implementation of +Robot36's decoder almost effortless. + +I like to keep the name Robot36 even since Robot36 is able to decode more than just Robot36 as +a great respect for the Robot engineers who developed the awesome Robot36 mode. + +Currently the following modes are supported: + +Robot Modes: 36 & 72 +Martin Modes: 1 & 2 +Scottie Modes: 1, 2 & DX + +Watching of old B/W or unsupported transmission modes works even in the raw "Debug Image" mode. diff --git a/Robot36.iml b/Robot36.iml new file mode 100644 index 0000000..0bb6048 --- /dev/null +++ b/Robot36.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/app.iml b/app/app.iml new file mode 100644 index 0000000..d426274 --- /dev/null +++ b/app/app.iml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..998ebaf --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,26 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.1" + + defaultConfig { + applicationId "xdsopl.robot36" + minSdkVersion 18 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + renderscriptTargetApi 19 + renderscriptSupportMode false + } + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..99a8f9b --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /disk1/ainan/android-sdk-linux/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/xdsopl/robot36/ApplicationTest.java b/app/src/androidTest/java/xdsopl/robot36/ApplicationTest.java new file mode 100644 index 0000000..9587c51 --- /dev/null +++ b/app/src/androidTest/java/xdsopl/robot36/ApplicationTest.java @@ -0,0 +1,13 @@ +package xdsopl.robot36; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6c42a1a --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/java/xdsopl/robot36/DecoderThread.java b/app/src/main/java/xdsopl/robot36/DecoderThread.java new file mode 100644 index 0000000..706c723 --- /dev/null +++ b/app/src/main/java/xdsopl/robot36/DecoderThread.java @@ -0,0 +1,235 @@ + +/* +Copyright 2014 Ahmet Inan + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + */ + +package xdsopl.robot36; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.media.AudioFormat; +import android.media.AudioRecord; +import android.media.MediaRecorder; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.view.SurfaceHolder; + +public class DecoderThread extends Thread { + int canvasWidth = -1, canvasHeight = -1; + boolean takeABreak = true, cantTouchThis = true; + int imageWidth = 320; + int imageHeight = 240; + final SurfaceHolder holder; + final Bitmap bitmap; + final Paint paint; + AudioRecord audio; + final int audioSource = MediaRecorder.AudioSource.MIC; + final int channelConfig = AudioFormat.CHANNEL_IN_MONO; + final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; + final int sampleRate = 44100; + final short[] audioBuffer; + final int[] pixelBuffer; + + RenderScript rs; + Allocation rsDecoderAudioBuffer, rsDecoderPixelBuffer, rsDecoderValueBuffer; + ScriptC_decoder rsDecoder; + + public DecoderThread(Context context, SurfaceHolder holder) { + this.holder = holder; + paint = new Paint(Paint.FILTER_BITMAP_FLAG); + bitmap = Bitmap.createBitmap(2048, 512, Bitmap.Config.ARGB_8888); + pixelBuffer = new int[bitmap.getWidth() * bitmap.getHeight()]; + + int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); + int bufferSizeInSamples = bufferSizeInBytes / 2; + int framesPerSecond = Math.max(1, sampleRate / bufferSizeInSamples); + audioBuffer = new short[framesPerSecond * bufferSizeInSamples]; + + int maxHorizontalLength = 2 * sampleRate; + + rs = RenderScript.create(context); + rsDecoderAudioBuffer = Allocation.createSized(rs, Element.I16(rs), audioBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT); + rsDecoderValueBuffer = Allocation.createSized(rs, Element.U8(rs), maxHorizontalLength, Allocation.USAGE_SCRIPT); + rsDecoderPixelBuffer = Allocation.createSized(rs, Element.I32(rs), pixelBuffer.length, Allocation.USAGE_SHARED | Allocation.USAGE_SCRIPT); + rsDecoder = new ScriptC_decoder(rs); + rsDecoder.bind_audio_buffer(rsDecoderAudioBuffer); + rsDecoder.bind_value_buffer(rsDecoderValueBuffer); + rsDecoder.bind_pixel_buffer(rsDecoderPixelBuffer); + rsDecoder.invoke_initialize(sampleRate, maxHorizontalLength, bitmap.getWidth(), bitmap.getHeight()); + } + void debug_sync() { + synchronized (holder) { + imageWidth = 320; + imageHeight = bitmap.getHeight(); + rsDecoder.invoke_debug_sync(); + } + } + void debug_image() { + synchronized (holder) { + imageWidth = 320; + imageHeight = bitmap.getHeight(); + rsDecoder.invoke_debug_image(); + } + } + void debug_both() { + synchronized (holder) { + imageWidth = 320; + imageHeight = bitmap.getHeight(); + rsDecoder.invoke_debug_both(); + } + } + void robot36_mode() { + synchronized (holder) { + imageWidth = 320; + imageHeight = 240; + rsDecoder.invoke_robot36_mode(); + } + } + void robot72_mode() { + synchronized (holder) { + imageWidth = 320; + imageHeight = 240; + rsDecoder.invoke_robot72_mode(); + } + } + void martin1_mode() { + synchronized (holder) { + imageWidth = 320; + imageHeight = 256; + rsDecoder.invoke_martin1_mode(); + } + } + void martin2_mode() { + synchronized (holder) { + imageWidth = 320; + imageHeight = 256; + rsDecoder.invoke_martin2_mode(); + } + } + void scottie1_mode() { + synchronized (holder) { + imageWidth = 320; + imageHeight = 256; + rsDecoder.invoke_scottie1_mode(); + } + } + void scottie2_mode() { + synchronized (holder) { + imageWidth = 320; + imageHeight = 256; + rsDecoder.invoke_scottie2_mode(); + } + } + void scottieDX_mode() { + synchronized (holder) { + imageWidth = 320; + imageHeight = 256; + rsDecoder.invoke_scottieDX_mode(); + } + } + void resize(int width, int height){ + synchronized (holder) { + canvasWidth = width; + canvasHeight = height; + } + } + void pause() { + synchronized (holder) { + takeABreak = true; + if (audio != null) { + audio.stop(); + audio.release(); + audio = null; + } + } + } + void play() { + synchronized (holder) { + takeABreak = false; + if (audio == null) { + audio = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, audioBuffer.length * 2); + audio.startRecording(); + } + holder.notify(); + } + } + void surfaceCreated() { + synchronized (holder) { + cantTouchThis = false; + } + play(); + } + void surfaceDestroyed() { + synchronized (holder) { + cantTouchThis = true; + } + pause(); + } + void draw(Canvas canvas) { + float sx, sy, px, py; + if (imageWidth * canvasHeight < canvasWidth * bitmap.getHeight()) { + sx = (float)canvasHeight * imageWidth / (bitmap.getWidth() * bitmap.getHeight()); + sy = (float)canvasHeight / bitmap.getHeight(); + px = (canvasWidth - sx * bitmap.getWidth()) / 2.0f; + py = 0.0f; + } else { + sx = (float)canvasWidth / bitmap.getWidth(); + sy = (float)canvasWidth * imageHeight / (bitmap.getHeight() * imageWidth); + px = 0.0f; + py = (canvasHeight - sy * bitmap.getHeight()) / 2.0f; + } + canvas.drawColor(Color.BLACK); + canvas.save(); + canvas.scale(sx, sy, px, py); + canvas.drawBitmap(bitmap, px, py, paint); + canvas.restore(); + } + void decode() { + int samples = audio.read(audioBuffer, 0, audioBuffer.length); + if (samples <= 0) + return; + + rsDecoderAudioBuffer.copyFrom(audioBuffer); + rsDecoder.invoke_decode(samples); + rsDecoderPixelBuffer.copyTo(pixelBuffer); + bitmap.setPixels(pixelBuffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); + } + @Override + public void run() { + while (true) { + synchronized (holder) { + while (cantTouchThis || takeABreak) { + try { + holder.wait(); + } catch (InterruptedException e) { + } + } + decode(); + Canvas canvas = null; + try { + canvas = holder.lockCanvas(null); + draw(canvas); + } finally { + if (canvas != null) + holder.unlockCanvasAndPost(canvas); + } + } + } + } +} diff --git a/app/src/main/java/xdsopl/robot36/ImageView.java b/app/src/main/java/xdsopl/robot36/ImageView.java new file mode 100644 index 0000000..1372415 --- /dev/null +++ b/app/src/main/java/xdsopl/robot36/ImageView.java @@ -0,0 +1,59 @@ + +/* +Copyright 2014 Ahmet Inan + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + */ + +package xdsopl.robot36; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class ImageView extends SurfaceView implements SurfaceHolder.Callback { + final DecoderThread thread; + public ImageView(Context context, AttributeSet attrs) { + super(context, attrs); + SurfaceHolder holder = getHolder(); + holder.addCallback(this); + thread = new DecoderThread(context, holder); + thread.start(); + } + + public void pause() { thread.pause(); } + public void resume() { thread.play(); } + public void debug_sync() { thread.debug_sync(); } + public void debug_image() { thread.debug_image(); } + public void debug_both() { thread.debug_both(); } + public void robot36_mode() { thread.robot36_mode(); } + public void robot72_mode() { thread.robot72_mode(); } + public void martin1_mode() { thread.martin1_mode(); } + public void martin2_mode() { thread.martin2_mode(); } + public void scottie1_mode() { thread.scottie1_mode(); } + public void scottie2_mode() { thread.scottie2_mode(); } + public void scottieDX_mode() { thread.scottieDX_mode(); } + + public void surfaceCreated(SurfaceHolder holder) { + thread.surfaceCreated(); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + thread.resize(width, height); + } + + public void surfaceDestroyed(SurfaceHolder holder) { + thread.surfaceDestroyed(); + } +} diff --git a/app/src/main/java/xdsopl/robot36/MainActivity.java b/app/src/main/java/xdsopl/robot36/MainActivity.java new file mode 100644 index 0000000..f15d34c --- /dev/null +++ b/app/src/main/java/xdsopl/robot36/MainActivity.java @@ -0,0 +1,96 @@ + +/* +Copyright 2014 Ahmet Inan + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + */ + +package xdsopl.robot36; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +public class MainActivity extends Activity { + ImageView view; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + view = (ImageView)findViewById(R.id.image); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + switch (id) { + case R.id.action_robot36_mode: + view.robot36_mode(); + return true; + case R.id.action_robot72_mode: + view.robot72_mode(); + return true; + case R.id.action_martin1_mode: + view.martin1_mode(); + return true; + case R.id.action_martin2_mode: + view.martin2_mode(); + return true; + case R.id.action_scottie1_mode: + view.scottie1_mode(); + return true; + case R.id.action_scottie2_mode: + view.scottie2_mode(); + return true; + case R.id.action_scottieDX_mode: + view.scottieDX_mode(); + return true; + case R.id.action_debug_sync: + view.debug_sync(); + return true; + case R.id.action_debug_image: + view.debug_image(); + return true; + case R.id.action_debug_both: + view.debug_both(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected void onPause() { + view.pause(); + super.onPause(); + } + + @Override + protected void onResume() { + view.resume(); + super.onResume(); + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..4df1894 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..8a36e16 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..f8c2d77 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..dba3c41 --- /dev/null +++ b/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..a013a73 --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 0dp + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..c8b56db --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 0dp + 0dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..5fa7f0e --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,16 @@ + + + + Robot36 + Debug Sync + Debug Image + Debug Image and Sync + Robot36 Mode + Robot72 Mode + Martin1 Mode + Martin2 Mode + Scottie1 Mode + Scottie2 Mode + ScottieDX Mode + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..ff6c9d2 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/rs/decoder.rs b/app/src/main/rs/decoder.rs new file mode 100644 index 0000000..fff89e1 --- /dev/null +++ b/app/src/main/rs/decoder.rs @@ -0,0 +1,447 @@ +/* +Copyright 2014 Ahmet Inan + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + */ + +#pragma version(1) +#pragma rs java_package_name(xdsopl.robot36) + +short *audio_buffer; +uchar *value_buffer; +uchar4 *pixel_buffer; + +static inline uchar4 rgb(uchar r, uchar g, uchar b) { return (uchar4){ b, g, r, 255 }; } + +static inline float2 complex(float a, float b) { return (float2){ a, b }; } +static inline float cabs(float2 z) { return length(z); } +static inline float carg(float2 z) { return atan2(z[1], z[0]); } +static inline float2 cexp(float2 z) +{ + return complex(exp(z[0]) * cos(z[1]), exp(z[0]) * sin(z[1])); +} +static inline float2 cmul(float2 a, float2 b) +{ + return complex(a[0] * b[0] - a[1] * b[1], a[0] * b[1] + a[1] * b[0]); +} +static inline float2 conj(float2 z) { return complex(z[0], -z[1]); } +static inline float2 cdiv(float2 a, float2 b) { return cmul(a, conj(b)) / dot(b, b); } + +static float ema_a(float cutoff, float rate, int order) +{ + float fc = cutoff / sqrt(rootn(2.0f, order) - 1.0f); + float RC = 1.0f / (2.0f * M_PI * fc); + float dt = 1.0f / rate; + return dt / (RC + dt); +} + +static float ema_power_a; +static float avg_power(float input) +{ + static float output; + return output = ema_power_a * input + (1.0f - ema_power_a) * output; +} + +static const int filter_order = 11; +static float ema_cnt_a; +static float2 cnt_lowpass(float2 input) +{ + static float2 output[filter_order]; + for (int i = 0; i < filter_order; ++i) + output[i] = input = ema_cnt_a * input + (1.0f - ema_cnt_a) * output[i]; + return input; +} + +static float ema_dat_a; +static float2 dat_lowpass(float2 input) +{ + static float2 output[filter_order]; + for (int i = 0; i < filter_order; ++i) + output[i] = input = ema_dat_a * input + (1.0f - ema_dat_a) * output[i]; + return input; +} + +static float2 cnt_phasor, cnt_phasor_delta; +static float2 cnt_phasor_rotate() +{ + float2 prev = cnt_phasor; + cnt_phasor = cmul(cnt_phasor, cnt_phasor_delta); + cnt_phasor = normalize(cnt_phasor); + return prev; +} + +static float2 dat_phasor, dat_phasor_delta; +static float2 dat_phasor_rotate() +{ + float2 prev = dat_phasor; + dat_phasor = cmul(dat_phasor, dat_phasor_delta); + dat_phasor = normalize(dat_phasor); + return prev; +} + +static float2 cnt_ddc(float amp) +{ + return cnt_lowpass(amp * cnt_phasor_rotate()); +} + +static float2 dat_ddc(float amp) +{ + return dat_lowpass(amp * dat_phasor_rotate()); +} + +static float cnt_fmd_scale; +static float2 cnt_fmd_prev; +static float cnt_fmd(float2 baseband) +{ + float phase = carg(cdiv(baseband, cnt_fmd_prev)); + cnt_fmd_prev = baseband; + return clamp(cnt_fmd_scale * phase, -1.0f, 1.0f); +} + +static float dat_fmd_scale; +static float2 dat_fmd_prev; +static float dat_fmd(float2 baseband) +{ + float phase = carg(cdiv(baseband, dat_fmd_prev)); + dat_fmd_prev = baseband; + return clamp(dat_fmd_scale * phase, -1.0f, 1.0f); +} + +static int sample_rate, mode, even_hpos; +static int buffer_length, bitmap_width, bitmap_height; +static int sync_length, sync_counter, vpos, hpos; +static int save_cnt, save_dat, seperator_counter; +static int u_sep_begin, u_sep_end, v_sep_begin, v_sep_end; +static int y_begin, y_end, u_begin, u_end, v_begin, v_end; +static int r_begin, r_end, b_begin, b_end, g_begin, g_end; + +static const int mode_raw = 0; +static const int mode_robot36 = 1; +static const int mode_yuv = 2; +static const int mode_rgb = 3; + +void debug_sync() +{ + save_cnt = 1; + save_dat = 0; + mode = mode_raw; + const float sync_len_min = 0.002f; + sync_length = sync_len_min * sample_rate; +} +void debug_image() +{ + save_dat = 1; + save_cnt = 0; + mode = mode_raw; + const float sync_len_min = 0.002f; + sync_length = sync_len_min * sample_rate; +} +void debug_both() +{ + save_cnt = 1; + save_dat = 1; + mode = mode_raw; + const float sync_len_min = 0.002f; + sync_length = sync_len_min * sample_rate; +} +void robot36_mode() +{ + save_dat = 1; + save_cnt = 0; + mode = mode_robot36; + const float tolerance = 0.8f; + const float settling_time = 0.0011f; + const float sync_len = 0.009f; + const float sync_porch_len = 0.003f; + const float sep_porch_len = 0.0015f; + const float y_scan_len = 0.088f; + const float u_scan_len = 0.044f; + const float v_scan_len = 0.044f; + const float seperator_len = 0.0045f; + sync_length = tolerance * sync_len * sample_rate; + y_begin = (sync_porch_len - settling_time) * sample_rate; + y_end = y_begin + y_scan_len * sample_rate; + u_sep_begin = y_end; + u_sep_end = u_sep_begin + seperator_len * sample_rate; + u_begin = u_sep_end + sep_porch_len * sample_rate; + u_end = u_begin + u_scan_len * sample_rate; + v_sep_begin = u_sep_begin; + v_sep_end = u_sep_end; + v_begin = v_sep_end + sep_porch_len * sample_rate; + v_end = v_begin + v_scan_len * sample_rate; +} +void robot72_mode() +{ + save_dat = 1; + save_cnt = 0; + mode = mode_yuv; + const float tolerance = 0.8f; + const float settling_time = 0.0011f; + const float sync_len = 0.009f; + const float sync_porch_len = 0.003f; + const float sep_porch_len = 0.0015f; + const float y_scan_len = 0.138f; + const float u_scan_len = 0.069f; + const float v_scan_len = 0.069f; + const float seperator_len = 0.0045f; + sync_length = tolerance * sync_len * sample_rate; + y_begin = (sync_porch_len - settling_time) * sample_rate; + y_end = y_begin + y_scan_len * sample_rate; + u_sep_begin = y_end; + u_sep_end = u_sep_begin + seperator_len * sample_rate; + u_begin = u_sep_end + sep_porch_len * sample_rate; + u_end = u_begin + u_scan_len * sample_rate; + v_sep_begin = u_end; + v_sep_end = v_sep_begin + seperator_len * sample_rate; + v_begin = v_sep_end + sep_porch_len * sample_rate; + v_end = v_begin + v_scan_len * sample_rate; +} +void martin1_mode() +{ + save_cnt = 0; + save_dat = 1; + mode = mode_rgb; + const float tolerance = 0.5f; + const float sync_len = 0.004862f; + const float sync_porch_len = 0.000572f; + const float r_scan_len = 0.146432f; + const float g_scan_len = 0.146432f; + const float b_scan_len = 0.146432f; + const float seperator_len = 0.000572f; + sync_length = tolerance * sync_len * sample_rate; + g_begin = 0; + g_end = g_begin + g_scan_len * sample_rate; + b_begin = g_end + seperator_len * sample_rate; + b_end = b_begin + b_scan_len * sample_rate; + r_begin = b_end + seperator_len * sample_rate; + r_end = r_begin + r_scan_len * sample_rate; +} +void martin2_mode() +{ + save_cnt = 0; + save_dat = 1; + mode = mode_rgb; + const float tolerance = 0.5f; + const float sync_len = 0.004862f; + const float sync_porch_len = 0.000572f; + const float r_scan_len = 0.073216f; + const float g_scan_len = 0.073216f; + const float b_scan_len = 0.073216f; + const float seperator_len = 0.000572f; + sync_length = tolerance * sync_len * sample_rate; + g_begin = 0; + g_end = g_begin + g_scan_len * sample_rate; + b_begin = g_end + seperator_len * sample_rate; + b_end = b_begin + b_scan_len * sample_rate; + r_begin = b_end + seperator_len * sample_rate; + r_end = r_begin + r_scan_len * sample_rate; +} +void scottie1_mode() +{ + save_cnt = 0; + save_dat = 1; + mode = mode_rgb; + const float tolerance = 0.8f; + const float settling_time = 0.0011f; + const float sync_len = 0.009f; + const float sync_porch_len = 0.0015f; + const float r_scan_len = 0.138240f; + const float g_scan_len = 0.138240f; + const float b_scan_len = 0.138240f; + const float seperator_len = 0.0015f; + sync_length = tolerance * sync_len * sample_rate; + r_begin = (sync_porch_len - settling_time) * sample_rate; + r_end = r_begin + r_scan_len * sample_rate; + g_begin = r_end + seperator_len * sample_rate; + g_end = g_begin + g_scan_len * sample_rate; + b_begin = g_end + seperator_len * sample_rate; + b_end = b_begin + b_scan_len * sample_rate; +} +void scottie2_mode() +{ + save_cnt = 0; + save_dat = 1; + mode = mode_rgb; + const float tolerance = 0.8f; + const float settling_time = 0.0011f; + const float sync_len = 0.009f; + const float sync_porch_len = 0.0015f; + const float r_scan_len = 0.088064f; + const float g_scan_len = 0.088064f; + const float b_scan_len = 0.088064f; + const float seperator_len = 0.0015f; + sync_length = tolerance * sync_len * sample_rate; + r_begin = (sync_porch_len - settling_time) * sample_rate; + r_end = r_begin + r_scan_len * sample_rate; + g_begin = r_end + seperator_len * sample_rate; + g_end = g_begin + g_scan_len * sample_rate; + b_begin = g_end + seperator_len * sample_rate; + b_end = b_begin + b_scan_len * sample_rate; +} +void scottieDX_mode() +{ + save_cnt = 0; + save_dat = 1; + mode = mode_rgb; + const float tolerance = 0.8f; + const float settling_time = 0.0011f; + const float sync_len = 0.009f; + const float sync_porch_len = 0.0015f; + const float r_scan_len = 0.3456f; + const float g_scan_len = 0.3456f; + const float b_scan_len = 0.3456f; + const float seperator_len = 0.0015f; + sync_length = tolerance * sync_len * sample_rate; + r_begin = (sync_porch_len - settling_time) * sample_rate; + r_end = r_begin + r_scan_len * sample_rate; + g_begin = r_end + seperator_len * sample_rate; + g_end = g_begin + g_scan_len * sample_rate; + b_begin = g_end + seperator_len * sample_rate; + b_end = b_begin + b_scan_len * sample_rate; +} + +void initialize(float rate, int length, int width, int height) +{ + sample_rate = rate; + buffer_length = length; + bitmap_width = width; + bitmap_height = height; + + vpos = 0; + even_hpos = hpos = 0; + even_hpos = 0; + sync_counter = 0; + seperator_counter = 0; + + const float dat_carrier = 1900.0f; + const float cnt_carrier = 1200.0f; + const float dat_bandwidth = 800.0f; + const float cnt_bandwidth = 200.0f; + + ema_power_a = ema_a(10.0f, sample_rate, 1); + ema_cnt_a = ema_a(cnt_bandwidth, sample_rate, filter_order); + ema_dat_a = ema_a(dat_bandwidth, sample_rate, filter_order); + + cnt_phasor = complex(0.0f, 1.0f); + cnt_phasor_delta = cexp(complex(0.0f, -2.0f * M_PI * cnt_carrier / sample_rate)); + + dat_phasor = complex(0.0f, 1.0f); + dat_phasor_delta = cexp(complex(0.0f, -2.0f * M_PI * dat_carrier / sample_rate)); + + cnt_fmd_scale = sample_rate / (M_PI * cnt_bandwidth); + dat_fmd_scale = sample_rate / (M_PI * dat_bandwidth); + + robot36_mode(); +} + +static void robot36_decoder() +{ + if (seperator_counter > 0) { + if (vpos < 1) + vpos = 1; + for (int i = 0; i < bitmap_width; ++i) { + uchar even_y = value_buffer[i * (y_end-y_begin) / bitmap_width + y_begin]; + uchar u = value_buffer[i * (u_end-u_begin) / bitmap_width + u_begin]; + uchar odd_y = value_buffer[i * (y_end-y_begin) / bitmap_width + even_hpos + y_begin]; + uchar v = value_buffer[i * (v_end-v_begin) / bitmap_width + even_hpos + v_begin]; + pixel_buffer[bitmap_width * (vpos-1) + i] = rsYuvToRGBA_uchar4(even_y, u, v); + pixel_buffer[bitmap_width * vpos + i] = rsYuvToRGBA_uchar4(odd_y, u, v); + } + even_hpos = hpos = 0; + } else { + for (int i = 0; i < bitmap_width; ++i) + pixel_buffer[bitmap_width * vpos + i] = rgb(0, 0, 0); + if (hpos < buffer_length) + even_hpos = hpos; + else + even_hpos = hpos = 0; + } +} +static void yuv_decoder() +{ + for (int i = 0; i < bitmap_width; ++i) { + uchar y = value_buffer[i * (y_end-y_begin) / bitmap_width + y_begin]; + uchar u = value_buffer[i * (u_end-u_begin) / bitmap_width + u_begin]; + uchar v = value_buffer[i * (v_end-v_begin) / bitmap_width + v_begin]; + pixel_buffer[bitmap_width * vpos + i] = rsYuvToRGBA_uchar4(y, u, v); + } + even_hpos = hpos = 0; +} +static void rgb_decoder() +{ + for (int i = 0; i < bitmap_width; ++i) { + uchar r = value_buffer[i * (r_end-r_begin) / bitmap_width + r_begin]; + uchar g = value_buffer[i * (g_end-g_begin) / bitmap_width + g_begin]; + uchar b = value_buffer[i * (b_end-b_begin) / bitmap_width + b_begin]; + pixel_buffer[bitmap_width * vpos + i] = rgb(r, g, b); + } + even_hpos = hpos = 0; +} +static void raw_decoder() +{ + for (int i = 0; i < bitmap_width; ++i) { + uchar value = value_buffer[i * hpos / bitmap_width]; + pixel_buffer[bitmap_width * vpos + i] = rgb(value, value, value); + } + even_hpos = hpos = 0; +} + +void decode(int samples) { + for (int sample = 0; sample < samples; ++sample) { + float amp = audio_buffer[sample] / 32768.0f; + float power = amp * amp; + if (avg_power(power) < 0.0000001f) + continue; + + float2 cnt_baseband = cnt_ddc(amp); + float2 dat_baseband = dat_ddc(amp); + + float cnt_value = cnt_fmd(cnt_baseband); + float dat_value = dat_fmd(dat_baseband); + + int cnt_active = cabs(dat_baseband) < cabs(cnt_baseband); + uchar cnt_level = save_cnt ? 127.5f - 127.5f * cnt_value : 0.0f; + uchar dat_level = save_dat ? 127.5f + 127.5f * dat_value : 0.0f; + value_buffer[hpos] = cnt_active ? cnt_level : dat_level; + + int cnt_quantized = round(cnt_value); + int dat_quantized = round(dat_value); + + int sync_level = cnt_active && cnt_quantized == 0; + int sync_pulse = !sync_level && sync_counter >= sync_length; + sync_counter = sync_level ? sync_counter + 1 : 0; + + int u_sep = u_sep_begin <= (hpos-even_hpos) && (hpos-even_hpos) <= u_sep_end; + int v_sep = v_sep_begin <= (hpos-even_hpos) && (hpos-even_hpos) <= v_sep_end; + seperator_counter += (u_sep || v_sep) ? dat_quantized : 0; + + if (++hpos >= buffer_length || sync_pulse) { + switch (mode) { + case mode_robot36: + robot36_decoder(); + break; + case mode_yuv: + yuv_decoder(); + break; + case mode_rgb: + rgb_decoder(); + break; + default: + raw_decoder(); + } + if (++vpos >= bitmap_height) + vpos = 0; + seperator_counter = 0; + } + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..2e3fc3e --- /dev/null +++ b/build.gradle @@ -0,0 +1,19 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.13.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1d3591c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8c0fb64 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffaa281 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app'