diff --git a/RedBookPlayer.Models/Hardware/Player.cs b/RedBookPlayer.Models/Hardware/Player.cs
index cda02de..639cc6c 100644
--- a/RedBookPlayer.Models/Hardware/Player.cs
+++ b/RedBookPlayer.Models/Hardware/Player.cs
@@ -1332,55 +1332,6 @@ namespace RedBookPlayer.Models.Hardware
#region Helpers
- ///
- /// Reformat raw subchannel data for a single sector
- ///
- /// Raw subchannel data to format
- /// Dictionary mapping subchannel to formatted data
- public Dictionary ConvertSingleSubchannel(byte[] subchannelData)
- {
- if(subchannelData == null || subchannelData.Length != 96)
- return null;
-
- // Create the output dictionary for the formatted data
- Dictionary formattedData = new Dictionary
- {
- ['P'] = new byte[12],
- ['Q'] = new byte[12],
- ['R'] = new byte[12],
- ['S'] = new byte[12],
- ['T'] = new byte[12],
- ['U'] = new byte[12],
- ['V'] = new byte[12],
- ['W'] = new byte[12],
- };
-
- // Loop through all bytes in the subchannel data and populate
- int index = -1;
- for(int i = 0; i < 96; i++)
- {
- // Get the modulo value of the current byte
- int modValue = i % 8;
- if(modValue == 0)
- index++;
-
- // Retrieve the next byte
- byte b = subchannelData[i];
-
- // Set the respective bit in the new byte data
- formattedData['P'][index] |= (byte)(HasBitSet(b, 7) ? 1 << (7 - modValue) : 0);
- formattedData['Q'][index] |= (byte)(HasBitSet(b, 6) ? 1 << (7 - modValue) : 0);
- formattedData['R'][index] |= (byte)(HasBitSet(b, 5) ? 1 << (7 - modValue) : 0);
- formattedData['S'][index] |= (byte)(HasBitSet(b, 4) ? 1 << (7 - modValue) : 0);
- formattedData['T'][index] |= (byte)(HasBitSet(b, 3) ? 1 << (7 - modValue) : 0);
- formattedData['U'][index] |= (byte)(HasBitSet(b, 2) ? 1 << (7 - modValue) : 0);
- formattedData['V'][index] |= (byte)(HasBitSet(b, 1) ? 1 << (7 - modValue) : 0);
- formattedData['W'][index] |= (byte)(HasBitSet(b, 0) ? 1 << (7 - modValue) : 0);
- }
-
- return formattedData;
- }
-
///
/// Reformat raw subchannel data for multiple sectors
///
@@ -1395,14 +1346,14 @@ namespace RedBookPlayer.Models.Hardware
int modValue = subchannelData.Length / 96;
Dictionary formattedData = new Dictionary
{
- ['P'] = new byte[12 * modValue],
- ['Q'] = new byte[12 * modValue],
- ['R'] = new byte[12 * modValue],
- ['S'] = new byte[12 * modValue],
- ['T'] = new byte[12 * modValue],
- ['U'] = new byte[12 * modValue],
- ['V'] = new byte[12 * modValue],
- ['W'] = new byte[12 * modValue],
+ ['P'] = new byte[8 * modValue],
+ ['Q'] = new byte[8 * modValue],
+ ['R'] = new byte[8 * modValue],
+ ['S'] = new byte[8 * modValue],
+ ['T'] = new byte[8 * modValue],
+ ['U'] = new byte[8 * modValue],
+ ['V'] = new byte[8 * modValue],
+ ['W'] = new byte[8 * modValue],
};
// Read in 96-byte chunks
@@ -1410,29 +1361,22 @@ namespace RedBookPlayer.Models.Hardware
{
byte[] buffer = new byte[96];
Array.Copy(subchannelData, i * 96, buffer, 0, 96);
- Dictionary singleData = ConvertSingleSubchannel(buffer);
+ var singleSubchannel = new SubchannelData(buffer);
+ Dictionary singleData = singleSubchannel.ConvertData();
- Array.Copy(singleData['P'], 0, formattedData['P'], 12 * i, 12);
- Array.Copy(singleData['Q'], 0, formattedData['Q'], 12 * i, 12);
- Array.Copy(singleData['R'], 0, formattedData['R'], 12 * i, 12);
- Array.Copy(singleData['S'], 0, formattedData['S'], 12 * i, 12);
- Array.Copy(singleData['T'], 0, formattedData['T'], 12 * i, 12);
- Array.Copy(singleData['U'], 0, formattedData['U'], 12 * i, 12);
- Array.Copy(singleData['V'], 0, formattedData['V'], 12 * i, 12);
- Array.Copy(singleData['W'], 0, formattedData['W'], 12 * i, 12);
+ Array.Copy(singleData['P'], 0, formattedData['P'], 8 * i, 8);
+ Array.Copy(singleData['Q'], 0, formattedData['Q'], 8 * i, 8);
+ Array.Copy(singleData['R'], 0, formattedData['R'], 8 * i, 8);
+ Array.Copy(singleData['S'], 0, formattedData['S'], 8 * i, 8);
+ Array.Copy(singleData['T'], 0, formattedData['T'], 8 * i, 8);
+ Array.Copy(singleData['U'], 0, formattedData['U'], 8 * i, 8);
+ Array.Copy(singleData['V'], 0, formattedData['V'], 8 * i, 8);
+ Array.Copy(singleData['W'], 0, formattedData['W'], 8 * i, 8);
}
return formattedData;
}
- ///
- /// Check if a bit is set in a byte
- ///
- /// Byte value to check
- /// Index of the bit to check
- /// True if the bit was set, false otherwise
- private bool HasBitSet(byte value, int bitIndex) => (value & (1 << bitIndex)) != 0;
-
#endregion
}
}
\ No newline at end of file
diff --git a/RedBookPlayer.Models/Hardware/SubchannelData.cs b/RedBookPlayer.Models/Hardware/SubchannelData.cs
new file mode 100644
index 0000000..93b2d24
--- /dev/null
+++ b/RedBookPlayer.Models/Hardware/SubchannelData.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+
+namespace RedBookPlayer.Models.Hardware
+{
+ ///
+ /// Represents subchannel data for a single sector
+ ///
+ ///
+ internal class SubchannelData
+ {
+ public SubchannelPacket[] Packets { get; private set; } = new SubchannelPacket[4];
+
+ ///
+ /// Create a new subchannel data from a byte array
+ ///
+ public SubchannelData(byte[] bytes)
+ {
+ if(bytes == null || bytes.Length != 96)
+ return;
+
+ byte[] buffer = new byte[24];
+ for(int i = 0; i < 4; i++)
+ {
+ Array.Copy(bytes, 24 * i, buffer, 0, 24);
+ Packets[i] = new SubchannelPacket(buffer);
+ }
+ }
+
+ ///
+ /// Convert the packet data into separate named subchannels
+ ///
+ public Dictionary ConvertData()
+ {
+ if(this.Packets == null || this.Packets.Length != 4)
+ return null;
+
+ // Prepare the output formatted data
+ Dictionary formattedData = new Dictionary
+ {
+ ['P'] = new byte[8],
+ ['Q'] = new byte[8],
+ ['R'] = new byte[8],
+ ['S'] = new byte[8],
+ ['T'] = new byte[8],
+ ['U'] = new byte[8],
+ ['V'] = new byte[8],
+ ['W'] = new byte[8],
+ };
+
+ // Loop through all subchannel packets
+ for(int i = 0; i < 4; i++)
+ {
+ Dictionary singleData = Packets[i].ConvertData();
+
+ Array.Copy(singleData['P'], 0, formattedData['P'], 2 * i, 2);
+ Array.Copy(singleData['Q'], 0, formattedData['Q'], 2 * i, 2);
+ Array.Copy(singleData['R'], 0, formattedData['R'], 2 * i, 2);
+ Array.Copy(singleData['S'], 0, formattedData['S'], 2 * i, 2);
+ Array.Copy(singleData['T'], 0, formattedData['T'], 2 * i, 2);
+ Array.Copy(singleData['U'], 0, formattedData['U'], 2 * i, 2);
+ Array.Copy(singleData['V'], 0, formattedData['V'], 2 * i, 2);
+ Array.Copy(singleData['W'], 0, formattedData['W'], 2 * i, 2);
+ }
+
+ return formattedData;
+ }
+ }
+}
\ No newline at end of file
diff --git a/RedBookPlayer.Models/Hardware/SubchannelPacket.cs b/RedBookPlayer.Models/Hardware/SubchannelPacket.cs
new file mode 100644
index 0000000..2051439
--- /dev/null
+++ b/RedBookPlayer.Models/Hardware/SubchannelPacket.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+
+namespace RedBookPlayer.Models.Hardware
+{
+ ///
+ /// Represents a single packet of subcode data
+ ///
+ ///
+ internal class SubchannelPacket
+ {
+ public byte Command { get; private set; }
+ public byte Instruction { get; private set; }
+ public byte[] ParityQ { get; private set; } = new byte[2];
+ public byte[] Data { get; private set; } = new byte[16];
+ public byte[] ParityP { get; private set; } = new byte[4];
+
+ ///
+ /// Create a new subchannel packet from a byte array
+ ///
+ public SubchannelPacket(byte[] bytes)
+ {
+ if(bytes == null || bytes.Length != 24)
+ return;
+
+ this.Command = bytes[0];
+ this.Instruction = bytes[1];
+
+ Array.Copy(bytes, 2, this.ParityQ, 0, 2);
+ Array.Copy(bytes, 4, this.Data, 0, 16);
+ Array.Copy(bytes, 20, this.ParityP, 0, 4);
+ }
+
+ ///
+ /// Convert the data into separate named subchannels
+ ///
+ public Dictionary ConvertData()
+ {
+ if(this.Data == null || this.Data.Length != 16)
+ return null;
+
+ // Create the output dictionary for the formatted data
+ Dictionary formattedData = new Dictionary
+ {
+ ['P'] = new byte[2],
+ ['Q'] = new byte[2],
+ ['R'] = new byte[2],
+ ['S'] = new byte[2],
+ ['T'] = new byte[2],
+ ['U'] = new byte[2],
+ ['V'] = new byte[2],
+ ['W'] = new byte[2],
+ };
+
+ // Loop through all bytes in the subchannel data and populate
+ int index = -1;
+ for(int i = 0; i < 16; i++)
+ {
+ // Get the modulo value of the current byte
+ int modValue = i % 8;
+ if(modValue == 0)
+ index++;
+
+ // Retrieve the next byte
+ byte b = this.Data[i];
+
+ // Set the respective bit in the new byte data
+ formattedData['P'][index] |= (byte)(HasBitSet(b, 7) ? 1 << (7 - modValue) : 0);
+ formattedData['Q'][index] |= (byte)(HasBitSet(b, 6) ? 1 << (7 - modValue) : 0);
+ formattedData['R'][index] |= (byte)(HasBitSet(b, 5) ? 1 << (7 - modValue) : 0);
+ formattedData['S'][index] |= (byte)(HasBitSet(b, 4) ? 1 << (7 - modValue) : 0);
+ formattedData['T'][index] |= (byte)(HasBitSet(b, 3) ? 1 << (7 - modValue) : 0);
+ formattedData['U'][index] |= (byte)(HasBitSet(b, 2) ? 1 << (7 - modValue) : 0);
+ formattedData['V'][index] |= (byte)(HasBitSet(b, 1) ? 1 << (7 - modValue) : 0);
+ formattedData['W'][index] |= (byte)(HasBitSet(b, 0) ? 1 << (7 - modValue) : 0);
+ }
+
+ return formattedData;
+ }
+
+ ///
+ /// Check if a bit is set in a byte
+ ///
+ /// Byte value to check
+ /// Index of the bit to check
+ /// True if the bit was set, false otherwise
+ private bool HasBitSet(byte value, int bitIndex) => (value & (1 << bitIndex)) != 0;
+ }
+}
\ No newline at end of file