mirror of
https://github.com/xdsopl/robot36.git
synced 2026-04-09 00:03:41 +00:00
Initial commit
This commit is contained in:
commit
2acdc4f030
44 changed files with 1755 additions and 0 deletions
13
app/src/androidTest/java/xdsopl/robot36/ApplicationTest.java
Normal file
13
app/src/androidTest/java/xdsopl/robot36/ApplicationTest.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package xdsopl.robot36;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
||||
21
app/src/main/AndroidManifest.xml
Normal file
21
app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="xdsopl.robot36" >
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" >
|
||||
<activity
|
||||
android:name="xdsopl.robot36.MainActivity"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
235
app/src/main/java/xdsopl/robot36/DecoderThread.java
Normal file
235
app/src/main/java/xdsopl/robot36/DecoderThread.java
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
|
||||
/*
|
||||
Copyright 2014 Ahmet Inan <xdsopl@googlemail.com>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
app/src/main/java/xdsopl/robot36/ImageView.java
Normal file
59
app/src/main/java/xdsopl/robot36/ImageView.java
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
/*
|
||||
Copyright 2014 Ahmet Inan <xdsopl@googlemail.com>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
96
app/src/main/java/xdsopl/robot36/MainActivity.java
Normal file
96
app/src/main/java/xdsopl/robot36/MainActivity.java
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
/*
|
||||
Copyright 2014 Ahmet Inan <xdsopl@googlemail.com>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
BIN
app/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
BIN
app/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
15
app/src/main/res/layout/activity_main.xml
Normal file
15
app/src/main/res/layout/activity_main.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
|
||||
|
||||
<xdsopl.robot36.ImageView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:id="@+id/image" />
|
||||
</RelativeLayout>
|
||||
23
app/src/main/res/menu/menu_main.xml
Normal file
23
app/src/main/res/menu/menu_main.xml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
|
||||
<item android:id="@+id/action_robot36_mode" android:title="@string/action_robot36_mode"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_robot72_mode" android:title="@string/action_robot72_mode"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_martin1_mode" android:title="@string/action_martin1_mode"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_martin2_mode" android:title="@string/action_martin2_mode"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_scottie1_mode" android:title="@string/action_scottie1_mode"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_scottie2_mode" android:title="@string/action_scottie2_mode"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_scottieDX_mode" android:title="@string/action_scottieDX_mode"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_debug_sync" android:title="@string/action_debug_sync"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_debug_image" android:title="@string/action_debug_image"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
<item android:id="@+id/action_debug_both" android:title="@string/action_debug_both"
|
||||
android:orderInCategory="100" android:showAsAction="never" />
|
||||
</menu>
|
||||
5
app/src/main/res/values-v21/styles.xml
Normal file
5
app/src/main/res/values-v21/styles.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="android:Theme.Material.Light">
|
||||
</style>
|
||||
</resources>
|
||||
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">0dp</dimen>
|
||||
</resources>
|
||||
5
app/src/main/res/values/dimens.xml
Normal file
5
app/src/main/res/values/dimens.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">0dp</dimen>
|
||||
<dimen name="activity_vertical_margin">0dp</dimen>
|
||||
</resources>
|
||||
16
app/src/main/res/values/strings.xml
Normal file
16
app/src/main/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Robot36</string>
|
||||
<string name="action_debug_sync">Debug Sync</string>
|
||||
<string name="action_debug_image">Debug Image</string>
|
||||
<string name="action_debug_both">Debug Image and Sync</string>
|
||||
<string name="action_robot36_mode">Robot36 Mode</string>
|
||||
<string name="action_robot72_mode">Robot72 Mode</string>
|
||||
<string name="action_martin1_mode">Martin1 Mode</string>
|
||||
<string name="action_martin2_mode">Martin2 Mode</string>
|
||||
<string name="action_scottie1_mode">Scottie1 Mode</string>
|
||||
<string name="action_scottie2_mode">Scottie2 Mode</string>
|
||||
<string name="action_scottieDX_mode">ScottieDX Mode</string>
|
||||
|
||||
</resources>
|
||||
8
app/src/main/res/values/styles.xml
Normal file
8
app/src/main/res/values/styles.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
447
app/src/main/rs/decoder.rs
Normal file
447
app/src/main/rs/decoder.rs
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
Copyright 2014 Ahmet Inan <xdsopl@googlemail.com>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue