Initial commit

This commit is contained in:
Ahmet Inan 2014-11-23 15:49:23 +01:00
commit 2acdc4f030
44 changed files with 1755 additions and 0 deletions

View 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);
}
}

View 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>

View 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);
}
}
}
}
}

View 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();
}
}

View 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();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View 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>

View 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>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="android:Theme.Material.Light">
</style>
</resources>

View 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>

View 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>

View 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>

View 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
View 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;
}
}
}