diff --git a/CdgLib/CdgLib.csproj b/CdgLib/CdgLib.csproj index 56f3a50..402b69e 100644 --- a/CdgLib/CdgLib.csproj +++ b/CdgLib/CdgLib.csproj @@ -42,9 +42,12 @@ - + + + - + + diff --git a/CdgLib/CDGFile.cs b/CdgLib/Graphic.cs similarity index 62% rename from CdgLib/CDGFile.cs rename to CdgLib/Graphic.cs index 20bd18e..587111d 100644 --- a/CdgLib/CDGFile.cs +++ b/CdgLib/Graphic.cs @@ -1,80 +1,40 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.Drawing.Imaging; using System.IO; +using System.Linq; +using System.Text; using System.Threading.Tasks; +using CdgLib.SubCode; namespace CdgLib { - public class CdgFile : FileStream + public class Graphic { - private const int ColourTableSize = 16; - private const byte CdgCommand = 0x9; - private const int CdgInstMemoryPreset = 1; - private const int CdgInstBorderPreset = 2; - private const int CdgInstTileBlock = 6; - private const int CdgInstScrollPreset = 20; - private const int CdgInstScrollCopy = 24; - private const int CdgInstDefTranspCol = 28; - private const int CdgInstLoadColTblLo = 30; - private const int CdgInstLoadColTblHigh = 31; - private const int CdgInstTileBlockXor = 38; - private const byte CdgMask = 0x3f; - private const int CdgPacketSize = 24; - private const int TileHeight = 12; - private const int TileWidth = 6; - public const int FullWidth = 300; - public const int FullHeight = 216; - private const int CdgDisplayWidth = 294; - private const int CdgDisplayHeight = 204; - private readonly int[] _mColourTable = new int[ColourTableSize]; - private readonly byte[,] _mPixelColours = new byte[FullHeight, FullWidth]; + private int _mPresetColourIndex; private int _mBorderColourIndex; + private long _mDuration; private int _mHOffset; private Bitmap _mImage; - private int _mPresetColourIndex; + private CdgFileIoStream _mPStream; private readonly Surface _mPSurface; private int _mTransparentColour; private int _mVOffset; - private long _previousPosition; - public CdgFile(string path, FileMode mode, FileAccess fileAccess) : base(path, mode, fileAccess, FileShare.Read) + private const byte Command = 0x9; + private const byte CdgMask = 0x3f; + private const int CdgDisplayWidth = 294; + private const int CdgDisplayHeight = 204; + private int[,] _graphicData; + public Graphic(IEnumerable packets) { - _mPSurface = new Surface(); + } - public bool Transparent => true; - - public long Duration => Length/CdgPacketSize*1000/300; - - public async Task Render(long position = -1) + public Bitmap ToBitmap() { - if (position < 0) - { - position = _previousPosition + CdgPacketSize; - } - - if (position < _previousPosition) - { - Reset(); - } - - //duration of one packet is 1/300 seconds (4 packets per sector, 75 sectors per second) - //p=t*3/10 t=p*10/3 t=milliseconds, p=packets - var timeToRender = position - _previousPosition; - _previousPosition += timeToRender; - var numberOfSubCodePackets = timeToRender*3/10; - - var subCodePackets = await ReadSubCodeAsync(numberOfSubCodePackets); - foreach (var subCodePacket in subCodePackets) - { - ProcessPacket(subCodePacket); - } - - RenderSurface(); Bitmap myBitmap; using (var bitmapStream = new MemoryStream()) { @@ -85,95 +45,50 @@ namespace CdgLib } myBitmap = GraphicUtil.StreamToBitmap(bitmapStream, FullWidth, FullHeight); } - - if (Transparent) - { - myBitmap.MakeTransparent(myBitmap.GetPixel(1, 1)); - } + myBitmap.MakeTransparent(myBitmap.GetPixel(1, 1)); + return myBitmap; - - } - - private void Reset() - { - Position = 0; - Array.Clear(_mPixelColours, 0, _mPixelColours.Length); - Array.Clear(_mColourTable, 0, _mColourTable.Length); - - _mPresetColourIndex = 0; - _mBorderColourIndex = 0; - _mTransparentColour = 0; - _mHOffset = 0; - _mVOffset = 0; - - _mDuration = 0; - _previousPosition = 0; - - //clear surface - if (_mPSurface.RgbData != null) - { - Array.Clear(_mPSurface.RgbData, 0, _mPSurface.RgbData.Length); - } - } - - private async Task> ReadSubCodeAsync(long numberOfPackets) - { - var subCodePackets = new List(); - var buffer = new byte[CdgPacketSize*numberOfPackets]; - var bytesRead = await ReadAsync(buffer, 0, buffer.Length); - - for (var i = 0; i < bytesRead/CdgPacketSize; i++) - { - var subCodePacket = new SubCodePacket(); - Array.Copy(buffer, i*CdgPacketSize + 0, subCodePacket.Command, 0, 1); - Array.Copy(buffer, i*CdgPacketSize + 1, subCodePacket.Instruction, 0, 1); - Array.Copy(buffer, i*CdgPacketSize + 2, subCodePacket.ParityQ, 0, 2); - Array.Copy(buffer, i*CdgPacketSize + 4, subCodePacket.Data, 0, 16); - Array.Copy(buffer, i*CdgPacketSize + 20, subCodePacket.ParityP, 0, 4); - subCodePackets.Add(subCodePacket); - } - return subCodePackets; } - private void ProcessPacket(SubCodePacket subCodePacketPacket) + private void ProcessPacket(Packet packetPacket) { - if ((subCodePacketPacket.Command[0] & CdgMask) != CdgCommand) return; - var instructionCode = subCodePacketPacket.Instruction[0] & CdgMask; + if ((packetPacket.Command[0] & CdgMask) != Command) return; + var instructionCode = (Instruction)(packetPacket.Instruction[0] & CdgMask); switch (instructionCode) { - case CdgInstMemoryPreset: - MemoryPreset(subCodePacketPacket); + case Instruction.MemoryPreset: + MemoryPreset(packetPacket); break; - case CdgInstBorderPreset: - BorderPreset(subCodePacketPacket); + case Instruction.BorderPreset: + BorderPreset(packetPacket); break; - case CdgInstTileBlock: - TileBlock(subCodePacketPacket, false); + case Instruction.TileBlockNormal: + TileBlock(packetPacket, false); break; - case CdgInstScrollPreset: - Scroll(subCodePacketPacket, false); + case Instruction.ScrollPreset: + Scroll(packetPacket, false); break; - case CdgInstScrollCopy: - Scroll(subCodePacketPacket, true); + case Instruction.ScrollCopy: + Scroll(packetPacket, true); break; - case CdgInstDefTranspCol: - DefineTransparentColour(subCodePacketPacket); + case Instruction.DefineTransparentColor: + DefineTransparentColour(packetPacket); break; - case CdgInstLoadColTblLo: - LoadColorTable(subCodePacketPacket, 0); + case Instruction.LoadColorTableLower: + LoadColorTable(packetPacket, 0); break; - case CdgInstLoadColTblHigh: - LoadColorTable(subCodePacketPacket, 1); + case Instruction.LoadColorTableUpper: + LoadColorTable(packetPacket, 1); break; - case CdgInstTileBlockXor: - TileBlock(subCodePacketPacket, true); + case Instruction.TileBlockXor: + TileBlock(packetPacket, true); break; } } - private void MemoryPreset(SubCodePacket pack) + private void MemoryPreset(Packet pack) { var colour = 0; var ri = 0; @@ -206,14 +121,34 @@ namespace CdgLib { for (ci = 0; ci <= FullWidth - 1; ci++) { - _mPixelColours[ri, ci] = (byte) colour; + _mPixelColours[ri, ci] = (byte)colour; } } } } + private void Reset() + { + Position = 0; + Array.Clear(_mPixelColours, 0, _mPixelColours.Length); + Array.Clear(_mColourTable, 0, _mColourTable.Length); - private void BorderPreset(SubCodePacket pack) + _mBorderColourIndex = 0; + _mTransparentColour = 0; + _mHOffset = 0; + _mVOffset = 0; + + _mDuration = 0; + _previousPosition = 0; + + //clear surface + if (_mPSurface.RgbData != null) + { + Array.Clear(_mPSurface.RgbData, 0, _mPSurface.RgbData.Length); + } + } + + private void BorderPreset(Packet pack) { var colour = 0; var ri = 0; @@ -230,12 +165,12 @@ namespace CdgLib { for (ci = 0; ci <= 5; ci++) { - _mPixelColours[ri, ci] = (byte) colour; + _mPixelColours[ri, ci] = (byte)colour; } for (ci = FullWidth - 6; ci <= FullWidth - 1; ci++) { - _mPixelColours[ri, ci] = (byte) colour; + _mPixelColours[ri, ci] = (byte)colour; } } @@ -243,18 +178,18 @@ namespace CdgLib { for (ri = 0; ri <= 11; ri++) { - _mPixelColours[ri, ci] = (byte) colour; + _mPixelColours[ri, ci] = (byte)colour; } for (ri = FullHeight - 12; ri <= FullHeight - 1; ri++) { - _mPixelColours[ri, ci] = (byte) colour; + _mPixelColours[ri, ci] = (byte)colour; } } } - private void LoadColorTable(SubCodePacket pack, int table) + private void LoadColorTable(Packet pack, int table) { for (var i = 0; i <= 7; i++) { @@ -262,8 +197,8 @@ namespace CdgLib //7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 //X X r r r r g g X X g g b b b b - var byte0 = pack.Data[2*i]; - var byte1 = pack.Data[2*i + 1]; + var byte0 = pack.Data[2 * i]; + var byte1 = pack.Data[2 * i + 1]; var red = (byte0 & 0x3f) >> 2; var green = ((byte0 & 0x3) << 2) | ((byte1 & 0x3f) >> 4); var blue = byte1 & 0xf; @@ -274,13 +209,13 @@ namespace CdgLib if (_mPSurface != null) { - _mColourTable[i + table*8] = _mPSurface.MapRgbColour(red, green, blue); + _mColourTable[i + table * 8] = _mPSurface.MapRgbColour(red, green, blue); } } } - private void TileBlock(SubCodePacket pack, bool bXor) + private void TileBlock(Packet pack, bool bXor) { var colour0 = 0; var colour1 = 0; @@ -294,8 +229,8 @@ namespace CdgLib colour0 = pack.Data[0] & 0xf; colour1 = pack.Data[1] & 0xf; - rowIndex = (pack.Data[2] & 0x1f)*12; - columnIndex = (pack.Data[3] & 0x3f)*6; + rowIndex = (pack.Data[2] & 0x1f) * 12; + columnIndex = (pack.Data[3] & 0x3f) * 6; if (rowIndex > FullHeight - TileHeight) return; @@ -345,18 +280,18 @@ namespace CdgLib //Set the pixel with the new colour. We set both the surfarray //containing actual RGB values, as well as our array containing //the colour indexes into our colour table. - _mPixelColours[rowIndex + i, columnIndex + j] = (byte) newCol; + _mPixelColours[rowIndex + i, columnIndex + j] = (byte)newCol; } } } - private void DefineTransparentColour(SubCodePacket pack) + private void DefineTransparentColour(Packet pack) { _mTransparentColour = pack.Data[0] & 0xf; } - private void Scroll(SubCodePacket pack, bool copy) + private void Scroll(Packet pack, bool copy) { var colour = 0; var hScroll = 0; @@ -425,7 +360,7 @@ namespace CdgLib { for (ci = 0; ci <= FullWidth - 1; ci++) { - temp[(ri + vInc)%FullHeight, (ci + hInc)%FullWidth] = _mPixelColours[ri, ci]; + temp[(ri + vInc) % FullHeight, (ci + hInc) % FullWidth] = _mPixelColours[ri, ci]; } } @@ -442,7 +377,7 @@ namespace CdgLib { for (ri = 0; ri <= vScrollPixels - 1; ri++) { - temp[ri, ci] = (byte) colour; + temp[ri, ci] = (byte)colour; } } } @@ -452,7 +387,7 @@ namespace CdgLib { for (ri = FullHeight + vScrollPixels; ri <= FullHeight - 1; ri++) { - temp[ri, ci] = (byte) colour; + temp[ri, ci] = (byte)colour; } } } @@ -464,7 +399,7 @@ namespace CdgLib { for (ri = 0; ri <= FullHeight - 1; ri++) { - temp[ri, ci] = (byte) colour; + temp[ri, ci] = (byte)colour; } } } @@ -474,7 +409,7 @@ namespace CdgLib { for (ri = 0; ri <= FullHeight - 1; ri++) { - temp[ri, ci] = (byte) colour; + temp[ri, ci] = (byte)colour; } } } @@ -490,26 +425,5 @@ namespace CdgLib } } } - - private void RenderSurface() - { - if (_mPSurface == null) - return; - for (var ri = 0; ri <= FullHeight - 1; ri++) - { - for (var ci = 0; ci <= FullWidth - 1; ci++) - { - if (ri < TileHeight || ri >= FullHeight - TileHeight || ci < TileWidth || - ci >= FullWidth - TileWidth) - { - _mPSurface.RgbData[ri, ci] = _mColourTable[_mBorderColourIndex]; - } - else - { - _mPSurface.RgbData[ri, ci] = _mColourTable[_mPixelColours[ri + _mVOffset, ci + _mHOffset]]; - } - } - } - } } -} \ No newline at end of file +} diff --git a/CdgLib/GraphicUtil.cs b/CdgLib/GraphicUtil.cs index e40ff02..a1ba19b 100644 --- a/CdgLib/GraphicUtil.cs +++ b/CdgLib/GraphicUtil.cs @@ -61,7 +61,7 @@ namespace CdgLib public static Bitmap GetCdgSizeBitmap(string filename) { var bm = new Bitmap(filename); - return ResizeBitmap(ref bm, CdgFile.FullWidth, CdgFile.FullHeight); + return ResizeBitmap(ref bm, GraphicsFile.FullWidth, GraphicsFile.FullHeight); } /// diff --git a/CdgLib/SubCode/Command.cs b/CdgLib/SubCode/Command.cs new file mode 100644 index 0000000..1e9568e --- /dev/null +++ b/CdgLib/SubCode/Command.cs @@ -0,0 +1,7 @@ +namespace CdgLib.SubCode +{ + internal enum Command : byte + { + Graphic = 0x9 + } +} diff --git a/CdgLib/SubCode/Instruction.cs b/CdgLib/SubCode/Instruction.cs new file mode 100644 index 0000000..ed0e1b6 --- /dev/null +++ b/CdgLib/SubCode/Instruction.cs @@ -0,0 +1,45 @@ +namespace CdgLib.SubCode +{ + /// + /// + /// + internal enum Instruction + { + /// + /// Set the screen to a particular color. + /// + MemoryPreset = 1, + /// + /// Set the border of the screen to a particular color. + /// + BorderPreset = 2, + /// + /// Load a 12 x 6, 2 color tile and display it normally. + /// + TileBlockNormal = 6, + /// + /// Scroll the image, filling in the new area with a color. + /// + ScrollPreset = 20, + /// + /// Scroll the image, rotating the bits back around. + /// + ScrollCopy = 24, + /// + /// Define a specific color as being transparent. + /// + DefineTransparentColor = 28, + /// + /// (entries 0-7) Load in the lower 8 entries of the color table. + /// + LoadColorTableLower = 30, + /// + /// (entries 8-15) Load in the upper 8 entries of the color table. + /// + LoadColorTableUpper = 31, + /// + /// Load a 12 x 6, 2 color tile and display it using the XOR method. + /// + TileBlockXor = 38 + } +} \ No newline at end of file diff --git a/CdgLib/SubCodePacket.cs b/CdgLib/SubCode/Packet.cs similarity index 82% rename from CdgLib/SubCodePacket.cs rename to CdgLib/SubCode/Packet.cs index 9bf23bf..c0df716 100644 --- a/CdgLib/SubCodePacket.cs +++ b/CdgLib/SubCode/Packet.cs @@ -1,6 +1,6 @@ -namespace CdgLib +namespace CdgLib.SubCode { - public class SubCodePacket + public class Packet { public byte[] Command = new byte[1]; public byte[] Data = new byte[16]; diff --git a/CdgLib/Surface.cs b/CdgLib/Surface.cs index 6c3467a..dfad705 100644 --- a/CdgLib/Surface.cs +++ b/CdgLib/Surface.cs @@ -4,7 +4,7 @@ namespace CdgLib { public class Surface { - public int[,] RgbData = new int[CdgFile.FullHeight, CdgFile.FullWidth]; + public int[,] RgbData = new int[GraphicsFile.FullHeight, GraphicsFile.FullWidth]; public int MapRgbColour(int red, int green, int blue) { diff --git a/KaraokeConverter/ExportAVI.cs b/KaraokeConverter/ExportAVI.cs index 35ead69..445645e 100644 --- a/KaraokeConverter/ExportAVI.cs +++ b/KaraokeConverter/ExportAVI.cs @@ -19,7 +19,7 @@ namespace KaraokeConverter Bitmap backgroundBmp = null; Bitmap mergedBMP = null; VideoStream aviStream = null; - var myCDGFile = new CdgFile(cdgFileName, FileMode.Open, FileAccess.Read); + var myCDGFile = new GraphicsFile(cdgFileName, FileMode.Open, FileAccess.Read); myCDGFile.RenderAtPosition(0); var bitmap__1 = (Bitmap) myCDGFile.RgbImage; if (!string.IsNullOrEmpty(backgroundFileName)) @@ -27,8 +27,8 @@ namespace KaraokeConverter try { if (IsMovie(backgroundFileName)) - backgroundBmp = MovieFrameExtractor.GetBitmap(0, backgroundFileName, CdgFile.FullWidth, - CdgFile.FullHeight); + backgroundBmp = MovieFrameExtractor.GetBitmap(0, backgroundFileName, GraphicsFile.FullWidth, + GraphicsFile.FullHeight); if (IsGraphic(backgroundFileName)) backgroundBmp = GraphicUtil.GetCdgSizeBitmap(backgroundFileName); } @@ -64,7 +64,7 @@ namespace KaraokeConverter { if (IsMovie(backgroundFileName)) backgroundBmp = MovieFrameExtractor.GetBitmap(position/1000, backgroundFileName, - CdgFile.FullWidth, CdgFile.FullHeight); + GraphicsFile.FullWidth, GraphicsFile.FullHeight); } if (backgroundBmp != null) { diff --git a/KaraokeConverter/Form1.cs b/KaraokeConverter/Form1.cs index ee52b1f..a5dc3c4 100644 --- a/KaraokeConverter/Form1.cs +++ b/KaraokeConverter/Form1.cs @@ -25,7 +25,7 @@ namespace KaraokeConverter #region "Private Declarations" - private CdgFile mCDGFile; + private GraphicsFile mCDGFile; private CdgFileIoStream mCDGStream; private string mCDGFileName; private string mMP3FileName; diff --git a/KaraokePlayer/KaraokeVideoPlayer.cs b/KaraokePlayer/KaraokeVideoPlayer.cs index 7c8af80..03acc8c 100644 --- a/KaraokePlayer/KaraokeVideoPlayer.cs +++ b/KaraokePlayer/KaraokeVideoPlayer.cs @@ -16,7 +16,7 @@ namespace KaraokePlayer public partial class KaraokeVideoPlayer : UserControl { private readonly PictureBox _lyrics = new PictureBox {Dock = DockStyle.Fill}; - private CdgFile _cdgFile; + private GraphicsFile _cdgFile; private Image _lyricImage; private OverlayForm _overlayForm; private DateTime _startTime; @@ -39,7 +39,7 @@ namespace KaraokePlayer public void Play(Uri file) { vlcPlayer.SetMedia(file); - _cdgFile = new CdgFile(Path.ChangeExtension(file.LocalPath, "cdg"), FileMode.Open, FileAccess.Read); + _cdgFile = new GraphicsFile(Path.ChangeExtension(file.LocalPath, "cdg"), FileMode.Open, FileAccess.Read); vlcPlayer.Play(); } @@ -52,7 +52,7 @@ namespace KaraokePlayer { var stopwatch = new Stopwatch(); stopwatch.Start(); - var picture = await _cdgFile.Render((long) (DateTime.Now - _startTime).TotalMilliseconds); + var picture = await _cdgFile.RenderAtTime((long) (DateTime.Now - _startTime).TotalMilliseconds); stopwatch.Reset(); Debug.Print(stopwatch.ElapsedMilliseconds.ToString());