예제 #1
0
        public static DataQueue NewDataQueue(size_t _packetlen, size_t initialslack)
        {
            DataQueue queue = new DataQueue();

            size_t packetlen   = _packetlen > 0 ? _packetlen : 1024;
            size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;

            queue.packet_size = packetlen;

            for (size_t i = 0; i < wantpackets; i++)
            {
                DataQueuePacket packet = new DataQueuePacket((int)packetlen);

                // don't care if this fails, we'll deal later.
                if (packet.data != null)
                {
                    packet.datalen  = 0;
                    packet.startpos = 0;
                    packet.next     = queue.pool;
                    queue.pool      = packet;
                }
            }

            return(queue);
        }
예제 #2
0
        public void Close()
        {
            if (!Initialized)
            {
                return;
            }

            _shutdown = true;
            _enabled  = false;
            if (_thread != null)
            {
                _thread.Join();
                _thread = null;
            }

            if (_workbuf != null)
            {
                Marshal.FreeHGlobal((IntPtr)_workbuf);
                _workbuf = null;
            }

            DataQueuePacket.FreeDataQueue(_queue);

            CloseDevice();
        }
예제 #3
0
 static void FreeDataQueueList(DataQueuePacket packet)
 {
     while (packet != null)
     {
         packet.Dispose();
         packet = packet.next;
     }
 }
예제 #4
0
 public void ClearQueuedAudio()
 {
     if (!Initialized)
     {
         return;// nothing to do.
     }
     // Blank out the device and release the mutex. Free it afterwards.
     lock (_lock)
     {
         // Keep up to two packets in the pool to reduce future malloc pressure.
         DataQueuePacket.ClearDataQueue(_queue, SDL_AUDIOBUFFERQUEUE_PACKETLEN * 2);
     }
 }
예제 #5
0
        public static void ClearDataQueue(DataQueue queue, size_t slack)
        {
            size_t          packet_size  = queue != null ? queue.packet_size : 1;
            size_t          slackpackets = (slack + (packet_size - 1)) / packet_size;
            DataQueuePacket packet;
            DataQueuePacket prev = null;

            if (queue == null)
            {
                return;
            }

            packet = queue.head;

            // merge the available pool and the current queue into one list.
            if (packet != null)
            {
                queue.tail.next = queue.pool;
            }
            else
            {
                packet = queue.pool;
            }

            // Remove the queued packets from the device.
            queue.tail         = null;
            queue.head         = null;
            queue.queued_bytes = 0;
            queue.pool         = packet;

            // Optionally keep some slack in the pool to reduce malloc pressure.
            for (size_t i = 0; packet != null && i < slackpackets; i++)
            {
                prev   = packet;
                packet = packet.next;
            }

            if (prev != null)
            {
                prev.next = null;
            }
            else
            {
                queue.pool = null;
            }

            FreeDataQueueList(packet);// free extra packets
        }
예제 #6
0
        void BufferQueueDrainCallback(byte *stream, size_t len)
        {
            // this function always holds the mixer lock before being called.
            size_t dequeued;

            Debug.Assert(Initialized);// this shouldn't ever happen, right?!

            dequeued = DataQueuePacket.ReadFromDataQueue(_queue, stream, len);
            stream  += dequeued;
            len     -= dequeued;

            if (len > 0)// fill any remaining space in the stream with silence.
            {
                Debug.Assert(DataQueuePacket.CountDataQueue(_queue) == 0);
                Native.SetMemory(stream, _waveFormat.Silence, len);
            }
        }
예제 #7
0
        int QueueAudio(void *data, size_t len)
        {
            if (!Initialized)
            {
                Debug.WriteLine("XAudio2 not initialized yet...");
                return(-1);
            }

            if (len > 0)
            {
                lock (_lock)
                {
                    return(DataQueuePacket.WriteToDataQueue(_queue, data, len));
                }
            }

            return(0);
        }
예제 #8
0
        /*
         * 这个samples是指样本帧中音频缓存区的大小。
         * 样本帧是一块音频数据,其大小指定为 format * channels
         * 其中format指的是每个样本的位数,这里使用WAVE_FORMAT_IEEE_FLOAT即4字节(32位)浮点型。
         * 在PCM中format等同于sampleSize
         *
         * @see: https://my.oschina.net/u/4365632/blog/3319770
         *       https://wiki.libsdl.org/SDL_AudioSpec#Remarks
         */
        public int Initialize(Xstream f, int samples)
        {
            _window  = f;
            _samples = samples;

            _shutdown = false;// just in case.
            _paused   = true;
            _enabled  = true;

            try
            {
                OpenDevice();

                // pool a few packets to start. Enough for two callbacks.
                _queue = DataQueuePacket.NewDataQueue(SDL_AUDIOBUFFERQUEUE_PACKETLEN, (size_t)(_bufferSize * 2));

                // Allocate a scratch audio buffer
                _worklen = _bufferSize;
                _workbuf = (byte *)Marshal.AllocHGlobal(_worklen);

                _thread = new Thread(RunAudio);

                // The audio mixing is always a high priority thread
                _thread.Priority = ThreadPriority.Highest;

                // Start the audio thread
                _thread.Start();
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Failed to open audio: {e.Message}");
                Close();
                return(1);
            }

            Pause(0);// start audio playing.

            return(0);
        }
예제 #9
0
        static DataQueuePacket AllocateDataQueuePacket(DataQueue queue)
        {
            DataQueuePacket packet;

            Debug.Assert(queue != null);

            packet = queue.pool;
            if (packet != null)
            {
                // we have one available in the pool.
                queue.pool = packet.next;
            }
            else
            {
                // Have to allocate a new one!
                packet = new DataQueuePacket((int)queue.packet_size);
                if (packet.data == null)
                {
                    return(null);
                }
            }

            packet.datalen  = 0;
            packet.startpos = 0;
            packet.next     = null;

            Debug.Assert((queue.head != null) == (queue.queued_bytes != 0));
            if (queue.tail == null)
            {
                queue.head = packet;
            }
            else
            {
                queue.tail.next = packet;
            }
            queue.tail = packet;
            return(packet);
        }
예제 #10
0
        public static int WriteToDataQueue(DataQueue queue, void *_data, size_t _len)
        {
            /*
             * 有些变量看似是多余的,这里参考以下几点,请慎重考虑后再作修改!
             *
             * 1.访问静态变量和实例变量将会比访问局部变量多耗费2-3个时钟周期。
             * 2.修改参数通常会使代码的可读性更差,并且可能需要更多的时间来了解函数中实际发生的事情。
             * 因此,通常不建议修改参数。将此优化留给编译器。
             */
            size_t          len         = _len;
            byte *          data        = (byte *)_data;
            size_t          packet_size = queue != null ? queue.packet_size : 0;
            DataQueuePacket orighead;
            DataQueuePacket origtail;
            size_t          origlen;
            size_t          datalen;

            if (queue == null)
            {
                // 连queue都没了,错误没地方存,根本无法返回HResult,还不如直接throw
                throw new InvalidParameterException("queue");
            }

            orighead = queue.head;
            origtail = queue.tail;
            origlen  = origtail != null ? origtail.datalen : 0;

            while (len > 0)
            {
                DataQueuePacket packet = queue.tail;
                Debug.Assert(packet == null || packet.datalen <= queue.packet_size);
                if (packet == null || packet.datalen >= queue.packet_size)
                {
                    // tail packet missing or completely full; we need a new packet.
                    packet = AllocateDataQueuePacket(queue);
                    if (packet.data == null)
                    {
                        OutOfMemoryException err = packet.err;

                        // uhoh, reset so we've queued nothing new, free what we can.
                        if (origtail == null)
                        {
                            packet = queue.head;// whole queue.
                        }
                        else
                        {
                            packet           = origtail.next;// what we added to existing queue.
                            origtail.next    = null;
                            origtail.datalen = origlen;
                        }
                        queue.head = orighead;
                        queue.tail = origtail;
                        queue.pool = null;

                        FreeDataQueueList(packet);// give back what we can.
                        return(err.HResult);
                    }
                }

                datalen = Math.Min(len, packet_size - packet.datalen);
                Native.CopyMemory(packet.data + packet.datalen, data, datalen);
                data               += datalen;
                len                -= datalen;
                packet.datalen     += datalen;
                queue.queued_bytes += datalen;
            }

            return(0);
        }