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); }
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(); }
static void FreeDataQueueList(DataQueuePacket packet) { while (packet != null) { packet.Dispose(); packet = packet.next; } }
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); } }
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 }
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); } }
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); }
/* * 这个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); }
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); }
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); }