added pixel buffer class

pull/11/head
Ahmet Inan 2024-04-25 10:54:09 +02:00
rodzic 97bdad4b8c
commit 7b7c0a6b75
8 zmienionych plików z 72 dodań i 42 usunięć

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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];
}
}

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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