kopia lustrzana https://github.com/xdsopl/robot36
added pixel buffer class
rodzic
97bdad4b8c
commit
7b7c0a6b75
|
@ -7,14 +7,14 @@ Copyright 2024 Ahmet Inan <xdsopl@gmail.com>
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
Pixel buffer
|
||||
|
||||
Copyright 2024 Ahmet Inan <xdsopl@gmail.com>
|
||||
*/
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue