From 7b7c0a6b759e3d32cc18d461ebb6e3ba2fcbfec4 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 25 Apr 2024 10:54:09 +0200 Subject: [PATCH] added pixel buffer class --- app/src/main/java/xdsopl/robot36/Decoder.java | 32 +++++++++---------- app/src/main/java/xdsopl/robot36/Mode.java | 2 +- app/src/main/java/xdsopl/robot36/PaulDon.java | 14 +++++--- .../main/java/xdsopl/robot36/PixelBuffer.java | 17 ++++++++++ .../main/java/xdsopl/robot36/RGBDecoder.java | 10 +++--- .../main/java/xdsopl/robot36/RawDecoder.java | 11 ++++--- .../java/xdsopl/robot36/Robot_36_Color.java | 18 +++++++---- .../java/xdsopl/robot36/Robot_72_Color.java | 10 +++--- 8 files changed, 72 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/xdsopl/robot36/PixelBuffer.java diff --git a/app/src/main/java/xdsopl/robot36/Decoder.java b/app/src/main/java/xdsopl/robot36/Decoder.java index e940d9b..d28bd32 100644 --- a/app/src/main/java/xdsopl/robot36/Decoder.java +++ b/app/src/main/java/xdsopl/robot36/Decoder.java @@ -7,14 +7,14 @@ Copyright 2024 Ahmet Inan package xdsopl.robot36; import java.util.ArrayList; +import java.util.Arrays; public class Decoder { private final Demodulator demodulator; + private final PixelBuffer pixelBuffer; private final float[] scanLineBuffer; private final float[] scratchBuffer; - private final int[] evenBuffer; - private final int[] oddBuffer; private final int[] scopePixels; private final int[] last5msSyncPulses; private final int[] last9msSyncPulses; @@ -46,8 +46,7 @@ public class Decoder { this.scopePixels = scopePixels; this.scopeWidth = scopeWidth; this.scopeHeight = scopeHeight; - evenBuffer = new int[scopeWidth]; - oddBuffer = new int[scopeWidth]; + pixelBuffer = new PixelBuffer(scopeWidth, 2); demodulator = new Demodulator(sampleRate); double scanLineMaxSeconds = 7; int scanLineMaxSamples = (int) Math.round(scanLineMaxSeconds * sampleRate); @@ -136,15 +135,14 @@ public class Decoder { return bestMode; } - private void copyLines(int lines) { - if (lines > 0) { - System.arraycopy(evenBuffer, 0, scopePixels, scopeWidth * curLine, scopeWidth); - System.arraycopy(evenBuffer, 0, scopePixels, scopeWidth * (curLine + scopeHeight), scopeWidth); - curLine = (curLine + 1) % scopeHeight; - } - if (lines == 2) { - System.arraycopy(oddBuffer, 0, scopePixels, scopeWidth * curLine, scopeWidth); - System.arraycopy(oddBuffer, 0, scopePixels, scopeWidth * (curLine + scopeHeight), scopeWidth); + private void copyLines(boolean okay) { + if (!okay) + return; + for (int row = 0; row < pixelBuffer.height; ++row) { + System.arraycopy(pixelBuffer.pixels, row * pixelBuffer.width, scopePixels, scopeWidth * curLine, pixelBuffer.width); + Arrays.fill(scopePixels, scopeWidth * curLine + pixelBuffer.width, scopeWidth * curLine + scopeWidth, 0); + System.arraycopy(pixelBuffer.pixels, row * pixelBuffer.width, scopePixels, scopeWidth * (curLine + scopeHeight), pixelBuffer.width); + Arrays.fill(scopePixels, scopeWidth * (curLine + scopeHeight) + pixelBuffer.width, scopeWidth * (curLine + scopeHeight) + scopeWidth, 0); curLine = (curLine + 1) % scopeHeight; } } @@ -172,15 +170,16 @@ public class Decoder { boolean pictureChanged = lastMode != mode || Math.abs(lastScanLineSamples - scanLineSamples) > scanLineToleranceSamples || Math.abs(lastSyncPulseIndex + scanLineSamples - pulses[pulses.length - 1]) > syncPulseToleranceSamples; + pixelBuffer.width = scopeWidth; if (pulses[0] >= scanLineSamples && pictureChanged) { int endPulse = pulses[0]; int extrapolate = endPulse / scanLineSamples; int firstPulse = endPulse - extrapolate * scanLineSamples; for (int pulseIndex = firstPulse; pulseIndex < endPulse; pulseIndex += scanLineSamples) - copyLines(mode.decodeScanLine(evenBuffer, oddBuffer, scratchBuffer, scanLineBuffer, pulseIndex, scanLineSamples, frequencyOffset)); + copyLines(mode.decodeScanLine(pixelBuffer, scratchBuffer, scanLineBuffer, pulseIndex, scanLineSamples, frequencyOffset)); } for (int i = pictureChanged ? 0 : lines.length - 1; i < lines.length; ++i) - copyLines(mode.decodeScanLine(evenBuffer, oddBuffer, scratchBuffer, scanLineBuffer, pulses[i], lines[i], frequencyOffset)); + copyLines(mode.decodeScanLine(pixelBuffer, scratchBuffer, scanLineBuffer, pulses[i], lines[i], frequencyOffset)); int shift = pulses[pulses.length - 1] - scanLineReserveSamples; if (shift > scanLineReserveSamples) { adjustSyncPulses(last5msSyncPulses, shift); @@ -225,7 +224,8 @@ public class Decoder { return processSyncPulse(syncPulse20msModes, last20msFrequencyOffsets, last20msSyncPulses, last20msScanLines, syncPulseIndex); } } else if (lastSyncPulseIndex >= scanLineReserveSamples && curSample > lastSyncPulseIndex + (lastScanLineSamples * 5) / 4) { - copyLines(lastMode.decodeScanLine(evenBuffer, oddBuffer, scratchBuffer, scanLineBuffer, lastSyncPulseIndex, lastScanLineSamples, lastFrequencyOffset)); + pixelBuffer.width = scopeWidth; + copyLines(lastMode.decodeScanLine(pixelBuffer, scratchBuffer, scanLineBuffer, lastSyncPulseIndex, lastScanLineSamples, lastFrequencyOffset)); lastSyncPulseIndex += lastScanLineSamples; return true; } diff --git a/app/src/main/java/xdsopl/robot36/Mode.java b/app/src/main/java/xdsopl/robot36/Mode.java index a378049..8a53a07 100644 --- a/app/src/main/java/xdsopl/robot36/Mode.java +++ b/app/src/main/java/xdsopl/robot36/Mode.java @@ -11,5 +11,5 @@ public interface Mode { int getScanLineSamples(); - int decodeScanLine(int[] evenBuffer, int[] oddBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset); + boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset); } diff --git a/app/src/main/java/xdsopl/robot36/PaulDon.java b/app/src/main/java/xdsopl/robot36/PaulDon.java index e779fd3..5245e42 100644 --- a/app/src/main/java/xdsopl/robot36/PaulDon.java +++ b/app/src/main/java/xdsopl/robot36/PaulDon.java @@ -57,9 +57,9 @@ public class PaulDon implements Mode { } @Override - public int decodeScanLine(int[] evenBuffer, int[] oddBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { + public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { if (syncPulseIndex + beginSamples < 0 || syncPulseIndex + endSamples > scanLineBuffer.length) - return 0; + return false; lowPassFilter.cutoff(horizontalPixels, 2 * channelSamples, 2); lowPassFilter.reset(); for (int i = beginSamples; i < endSamples; ++i) @@ -73,9 +73,13 @@ public class PaulDon implements Mode { int vAvgPos = position + vAvgBeginSamples; int uAvgPos = position + uAvgBeginSamples; int yOddPos = position + yOddBeginSamples; - evenBuffer[i] = ColorConverter.YUV2RGB(scratchBuffer[yEvenPos], scratchBuffer[uAvgPos], scratchBuffer[vAvgPos]); - oddBuffer[i] = ColorConverter.YUV2RGB(scratchBuffer[yOddPos], scratchBuffer[uAvgPos], scratchBuffer[vAvgPos]); + pixelBuffer.pixels[i] = + ColorConverter.YUV2RGB(scratchBuffer[yEvenPos], scratchBuffer[uAvgPos], scratchBuffer[vAvgPos]); + pixelBuffer.pixels[i + horizontalPixels] = + ColorConverter.YUV2RGB(scratchBuffer[yOddPos], scratchBuffer[uAvgPos], scratchBuffer[vAvgPos]); } - return 2; + pixelBuffer.width = horizontalPixels; + pixelBuffer.height = 2; + return true; } } diff --git a/app/src/main/java/xdsopl/robot36/PixelBuffer.java b/app/src/main/java/xdsopl/robot36/PixelBuffer.java new file mode 100644 index 0000000..55f14d3 --- /dev/null +++ b/app/src/main/java/xdsopl/robot36/PixelBuffer.java @@ -0,0 +1,17 @@ +/* +Pixel buffer + +Copyright 2024 Ahmet Inan +*/ + +package xdsopl.robot36; + +public class PixelBuffer { + public int[] pixels; + public int width; + public int height; + + PixelBuffer(int width, int height) { + pixels = new int[width * height]; + } +} diff --git a/app/src/main/java/xdsopl/robot36/RGBDecoder.java b/app/src/main/java/xdsopl/robot36/RGBDecoder.java index aeec2a2..6faa66f 100644 --- a/app/src/main/java/xdsopl/robot36/RGBDecoder.java +++ b/app/src/main/java/xdsopl/robot36/RGBDecoder.java @@ -50,9 +50,9 @@ public class RGBDecoder implements Mode { } @Override - public int decodeScanLine(int[] evenBuffer, int[] oddBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { + public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { if (syncPulseIndex + beginSamples < 0 || syncPulseIndex + endSamples > scanLineBuffer.length) - return 0; + return false; lowPassFilter.cutoff(horizontalPixels, 2 * greenSamples, 2); lowPassFilter.reset(); for (int i = 0; i < endSamples - beginSamples; ++i) @@ -64,8 +64,10 @@ public class RGBDecoder implements Mode { int redPos = redBeginSamples + (i * redSamples) / horizontalPixels; int greenPos = greenBeginSamples + (i * greenSamples) / horizontalPixels; int bluePos = blueBeginSamples + (i * blueSamples) / horizontalPixels; - evenBuffer[i] = ColorConverter.RGB(scratchBuffer[redPos], scratchBuffer[greenPos], scratchBuffer[bluePos]); + pixelBuffer.pixels[i] = ColorConverter.RGB(scratchBuffer[redPos], scratchBuffer[greenPos], scratchBuffer[bluePos]); } - return 1; + pixelBuffer.width = horizontalPixels; + pixelBuffer.height = 1; + return true; } } diff --git a/app/src/main/java/xdsopl/robot36/RawDecoder.java b/app/src/main/java/xdsopl/robot36/RawDecoder.java index a85c80c..294b019 100644 --- a/app/src/main/java/xdsopl/robot36/RawDecoder.java +++ b/app/src/main/java/xdsopl/robot36/RawDecoder.java @@ -28,10 +28,10 @@ public class RawDecoder implements Mode { } @Override - public int decodeScanLine(int[] evenBuffer, int[] oddBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { + public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { if (syncPulseIndex < 0 || syncPulseIndex + scanLineSamples > scanLineBuffer.length) - return 0; - int horizontalPixels = evenBuffer.length; + return false; + int horizontalPixels = pixelBuffer.width; lowPassFilter.cutoff(horizontalPixels, 2 * scanLineSamples, 2); lowPassFilter.reset(); for (int i = 0; i < scanLineSamples; ++i) @@ -41,8 +41,9 @@ public class RawDecoder implements Mode { scratchBuffer[i] = freqToLevel(lowPassFilter.avg(scratchBuffer[i]), frequencyOffset); for (int i = 0; i < horizontalPixels; ++i) { int position = (i * scanLineSamples) / horizontalPixels; - evenBuffer[i] = ColorConverter.GRAY(scratchBuffer[position]); + pixelBuffer.pixels[i] = ColorConverter.GRAY(scratchBuffer[position]); } - return 1; + pixelBuffer.height = 1; + return true; } } diff --git a/app/src/main/java/xdsopl/robot36/Robot_36_Color.java b/app/src/main/java/xdsopl/robot36/Robot_36_Color.java index 510e657..9c01e64 100644 --- a/app/src/main/java/xdsopl/robot36/Robot_36_Color.java +++ b/app/src/main/java/xdsopl/robot36/Robot_36_Color.java @@ -62,9 +62,9 @@ public class Robot_36_Color implements Mode { } @Override - public int decodeScanLine(int[] evenBuffer, int[] oddBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { + public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { if (syncPulseIndex + beginSamples < 0 || syncPulseIndex + endSamples > scanLineBuffer.length) - return 0; + return false; float separator = 0; for (int i = 0; i < separatorSamples; ++i) separator += scanLineBuffer[syncPulseIndex + separatorBeginSamples + i]; @@ -85,14 +85,18 @@ public class Robot_36_Color implements Mode { int luminancePos = luminanceBeginSamples + (i * luminanceSamples) / horizontalPixels; int chrominancePos = chrominanceBeginSamples + (i * chrominanceSamples) / horizontalPixels; if (even) { - evenBuffer[i] = ColorConverter.RGB(scratchBuffer[luminancePos], 0, scratchBuffer[chrominancePos]); + pixelBuffer.pixels[i] = ColorConverter.RGB(scratchBuffer[luminancePos], 0, scratchBuffer[chrominancePos]); } else { - int evenYUV = evenBuffer[i]; + int evenYUV = pixelBuffer.pixels[i]; int oddYUV = ColorConverter.RGB(scratchBuffer[luminancePos], scratchBuffer[chrominancePos], 0); - evenBuffer[i] = ColorConverter.YUV2RGB((evenYUV & 0x00ff00ff) | (oddYUV & 0x0000ff00)); - oddBuffer[i] = ColorConverter.YUV2RGB((oddYUV & 0x00ffff00) | (evenYUV & 0x000000ff)); + pixelBuffer.pixels[i] = + ColorConverter.YUV2RGB((evenYUV & 0x00ff00ff) | (oddYUV & 0x0000ff00)); + pixelBuffer.pixels[i + horizontalPixels] = + ColorConverter.YUV2RGB((oddYUV & 0x00ffff00) | (evenYUV & 0x000000ff)); } } - return even ? 0 : 2; + pixelBuffer.width = horizontalPixels; + pixelBuffer.height = 2; + return !even; } } diff --git a/app/src/main/java/xdsopl/robot36/Robot_72_Color.java b/app/src/main/java/xdsopl/robot36/Robot_72_Color.java index a059d17..3b830ba 100644 --- a/app/src/main/java/xdsopl/robot36/Robot_72_Color.java +++ b/app/src/main/java/xdsopl/robot36/Robot_72_Color.java @@ -60,9 +60,9 @@ public class Robot_72_Color implements Mode { } @Override - public int decodeScanLine(int[] evenBuffer, int[] oddBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { + public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int syncPulseIndex, int scanLineSamples, float frequencyOffset) { if (syncPulseIndex + beginSamples < 0 || syncPulseIndex + endSamples > scanLineBuffer.length) - return 0; + return false; lowPassFilter.cutoff(horizontalPixels, 2 * luminanceSamples, 2); lowPassFilter.reset(); for (int i = beginSamples; i < endSamples; ++i) @@ -74,8 +74,10 @@ public class Robot_72_Color implements Mode { int yPos = yBeginSamples + (i * luminanceSamples) / horizontalPixels; int uPos = uBeginSamples + (i * chrominanceSamples) / horizontalPixels; int vPos = vBeginSamples + (i * chrominanceSamples) / horizontalPixels; - evenBuffer[i] = ColorConverter.YUV2RGB(scratchBuffer[yPos], scratchBuffer[uPos], scratchBuffer[vPos]); + pixelBuffer.pixels[i] = ColorConverter.YUV2RGB(scratchBuffer[yPos], scratchBuffer[uPos], scratchBuffer[vPos]); } - return 1; + pixelBuffer.width = horizontalPixels; + pixelBuffer.height = 1; + return true; } }