using System;
using System.Text;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.IO;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Pcm2Tsmu")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Pcm2Tsmu")]
[assembly: AssemblyCopyright("Copyright ©  2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM

[assembly: Guid("e1d01c70-8946-11db-b606-0800200c9a66")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
[assembly: AssemblyVersion("1.3.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")]


class Pcm2Tsmu
{
    static int Main(string[] args)
    {
        // 03 C0 11 40 Header PCM, int 16 bits, 1 channels, samplerate 48000
        // 03 C0 31 40 Header PCM, int 16 bits, 2 channels, samplerate 48000
        // 07 80 41 40 Header PCM, int 16 bits, 3 channels, samplerate 48000 2.1
        // 07 80 71 40 Header PCM, int 16 bits, 4 channels, samplerate 48000
        // 0B 40 81 40 Header PCM, int 16 bits, 5 channels, samplerate 48000
        // 0B 40 91 40 Header PCM, int 16 bits, 6 channels, samplerate 48000
        // 0F 00 A1 40 Header PCM, int 16 bits, 7 channels, samplerate 48000 default value: L R C BL BR SL SR
        // 0F 00 B1 40 Header PCM, int 16 bits, 8 channels, samplerate 48000

        // 05 A0 11 C0 Header PCM, int 24 bits, 1 channels, samplerate 48000
        // 05 A0 31 C0 Header PCM, int 24 bits, 2 channels, samplerate 48000
        // 0B 40 41 C0 Header PCM, int 24 bits, 3 channels, samplerate 48000
        // 0B 40 71 C0 Header PCM, int 24 bits, 4 channels, samplerate 48000
        // 10 E0 81 C0 Header PCM, int 24 bits, 5 channels, samplerate 48000
        // 10 E0 91 C0 Header PCM, int 24 bits, 6 channels, samplerate 48000
        // 16 80 A1 C0 Header PCM, int 24 bits, 7 channels, samplerate 48000
        // 16 80 B1 C0 Header PCM, int 24 bits, 8 channels, samplerate 48000

        // 0B 40 34 C0 Header PCM, int 24 bits, 2 channels, samplerate 96000
        // 16 80 74 C0 Header PCM, int 24 bits, 4 channels, samplerate 96000
        // 0F 00 74 40 Header PCM, int 16 bits, 4 channels, samplerate 96000

        // 0F 00 35 40 Header PCM, int 16 bits, 2 channels, samplerate 192000
        byte[] h = {16, 224, 145, 192};         // 24 bits, 6 ch, 48 KHz
        short    nc = 6;                        // channels
        int   srate = 48000;                    // samples/sec
        short bpsam = 24;                       // bits/sample
        int   block = 4320;                     // block size = nc x bpsam x srate / 1600
        int   i, bytesRead;                     // aux
        uint aux;

        try {
            if (args.Length < 1 || (args.Length == 1 && "-" == args[0])) {
                Console.WriteLine("###############################################################");
                Console.WriteLine(" Pcm2Tsmu v1.3.0.0: Fix pcm for TsMuxer format. (Tebasuna 2008)");
                Console.WriteLine("###############################################################");
                Console.WriteLine(" ");
                Console.WriteLine("Usage:");
                Console.WriteLine(" Pcm2Tsmu <input.pcm> [output.pcm] [-i #] [-c #] [-s #]");
                Console.WriteLine(" ");
                Console.WriteLine("Where:");
                Console.WriteLine(" <input.pcm>  must be a lpcm up to 8 channels, int 16-20-24 bits,");
                Console.WriteLine("              and 48, 96 or 192 KHz.");
                Console.WriteLine(" ");
                Console.WriteLine(" [output.pcm] If not present, input.pcm is used suffixed with: _tsm");
                Console.WriteLine(" ");
                Console.WriteLine(" [-i #]          BitsPerSample Integer. Default 24 (20 not tested).");
                Console.WriteLine(" ");
                Console.WriteLine(" [-c #]          NumChannels. Default 6.");
                Console.WriteLine(" ");
                Console.WriteLine(" [-s #]          SampleRate. Default 48000.");
                Console.WriteLine(" ");
                Console.WriteLine("Note: To use STDIN use - as input filename (now output.pcm is necessary).");
                return 2;
            }
// Parameters
            string inputFile = args[0];
            string sx = args[0];
            if (sx.Length > 4) sx = sx.Substring(0, sx.Length - 4) + "_tsm.pcm";
            for (i = 1; i<args.Length; i++) {
                switch (args[i]) {
                    case "-i":
                        bpsam = short.Parse(args[++i]);
                        break;
                    case "-c":
                        nc = short.Parse(args[++i]);
                        break;
                    case "-s":
                        srate = int.Parse(args[++i]);
                        break;
                    default:
                        sx = args[i];
                        break;
                }
// Some data validation from parameters
            if (nc <1 || nc >8) throw new Exception("Allowed only NumChannels 1 to 8");
            if (bpsam != 16 && bpsam != 20 && bpsam != 24) throw new Exception("Allowed only BitsPerSample 16, 20 or 24");
            if (srate != 48000 && srate != 96000 && srate != 192000) throw new Exception("Allowed only SampleRate 48000, 96000 or 192000");
            }
// Construct the output pcm header
            srate = srate / 1600;                  // 48000 / 1600 = 30, 96000/1600 = 60
            switch (nc) {
                case 1:
                    h[2] = 17;                             //          0x11
                    block = srate * (nc + 1) * bpsam;
                    break;
                case 2:
//                    h[2] = 33;                           // 2c also  0x21 ?
                    h[2] = 49;                             //          0x31
                    block = srate * nc * bpsam;            // Block size  30 x nc x bpsam
                    break;
                case 3:
                    h[2] = 65;                             // 3 chan   0x41
//                    h[2] = 81;                           // 2.1 chan 0x51
                    block = srate * (nc + 1) * bpsam;
                    break;
                case 4:
//                    h[2] = 97;                           // 3.1 chan 0x61
                    h[2] = 113;                            // 4 chan   0x71
                    block = srate * nc * bpsam;
                    break;
                case 5:
                    h[2] = 129;                            //          0x81
                    block = srate * (nc + 1) * bpsam;
                    break;
                case 6:
                    h[2] = 145;                            //          0x91
                    block = srate * nc * bpsam;            // Block size  30 x nc x bpsam
                    break;
                case 7:
                    h[2] = 161;                            //          0xA1
                    block = srate * (nc + 1) * bpsam;
                    break;
                case 8:
                    h[2] = 177;                            //          0xB1
                    block = srate * nc * bpsam;            // Block size  30 x nc x bpsam
                    break;
            }
            aux = (uint)block;
            h[1] = (byte)(aux % 256);
            aux = aux>>8;
            h[0] = (byte)aux;
            if (srate == 60)  h[2] = (byte)(h[2] + 3);     //  96 KHz
            if (srate == 120)  h[2] = (byte)(h[2] + 4);    // 192 KHz
            h[3] = (byte)(16 * (bpsam - 12));              // 64, 128, 192 for 16, 20, 24 bits

// Begin
            BinaryReader r = new BinaryReader(inputFile == "-" ? Console.OpenStandardInput() : new FileStream(inputFile, FileMode.Open));
            try {
// BufferI must be the max possible Block (64*9*5*7 = 20160)
               byte[] bufI = new byte[23040];
               GCHandle gchI = GCHandle.Alloc(bufI, GCHandleType.Pinned);
// Open output file and write.
               try {
                  FileStream f1 = new FileStream(sx, FileMode.OpenOrCreate);
                  BinaryWriter w1 = new BinaryWriter(f1);
// Write data
                  bytesRead = r.Read(bufI, 0, block);
                  while (0 < bytesRead) {
                     w1.Write(h);
                     w1.Write(bufI, 0, bytesRead);
                     i = block - bytesRead;
                     while ( (0 < bytesRead) && (i > 0) ) {
                        bytesRead = r.Read(bufI, 0, i);
                        w1.Write(bufI, 0, bytesRead);
                        i -= bytesRead;
                     }
                     bytesRead = r.Read(bufI, 0, block);
                  }
// End
                  w1.Close();
                  f1.Close();
               }
               finally {
                   gchI.Free();
               }
            }
            finally {
                if ("-" != inputFile) (r as IDisposable).Dispose();
           }
            return 0;
        }
        catch (Exception exc)
        {
            Console.WriteLine(exc.ToString());
            return -1;
        }
    }
}
