Я пытаюсь передавать звуковые образцы с моего микрофона на мои колонки , используя DirectSound и C #, Это должно быть похоже на «прослушивание микрофона», но позже я хочу использовать это для чего-то другого. Протестировав мой подход, я заметил тихий тиклинг, взрывающиеся шумы на заднем плане. Я предполагаю, что это связано с задержкой между записью и воспроизведением буфера, которая должна быть больше, чем задержка при записи фрагментов.
Если установить задержку между записью и воспроизведением менее 50 мс. В основном это работает, но иногда я получаю действительно громкие трескучие шумы . Поэтому я решил с задержкой не менее 50 мс. Это работает хорошо для меня, но задержка системного «прослушивания устройства» кажется намного короче. Я предполагаю, что это около 15-30 мс, и почти не заметно. В течение 50 мс я получаю хотя бы небольшой эффект реверберации.
Далее я покажу вам мой микрофон код (частично):
Инициализация выполняется следующим образом:
capture = new Capture(device);
// Creating the buffer
// Determining the buffer size
bufferSize = format.AverageBytesPerSecond * bufferLength / 1000;
while (bufferSize % format.BlockAlign != 0) bufferSize += 1;
chunkSize = Math.Max(bufferSize, 256);
bufferSize = chunkSize * BUFFER_CHUNKS;
this.bufferLength = chunkSize * 1000 / format.AverageBytesPerSecond; // Redetermining the buffer Length that will be used.
captureBufferDescription = new CaptureBufferDescription();
captureBufferDescription.BufferBytes = bufferSize;
captureBufferDescription.Format = format;
captureBuffer = new CaptureBuffer(captureBufferDescription, capture);
// Creating Buffer control
bufferARE = new AutoResetEvent(false);
// Adding notifier to buffer.
bufferNotify = new Notify(captureBuffer);
BufferPositionNotify[] bpns = new BufferPositionNotify[BUFFER_CHUNKS];
for(int i = 0 ; i < BUFFER_CHUNKS ; i ++) bpns[i] = new BufferPositionNotify() { Offset = chunkSize * (i+1) - 1, EventNotifyHandle = bufferARE.SafeWaitHandle.DangerousGetHandle() };
bufferNotify.SetNotificationPositions(bpns);
Захват будет проходить следующим образом:
// Initializing
MemoryStream tempBuffer = new MemoryStream();
// Capturing
while (isCapturing && captureBuffer.Capturing)
{
bufferARE.WaitOne();
if (isCapturing && captureBuffer.Capturing)
{
captureBuffer.Read(currentBufferPart * chunkSize, tempBuffer, chunkSize, LockFlag.None);
ReportChunk(applyVolume(tempBuffer.GetBuffer()));
currentBufferPart = (currentBufferPart + 1) % BUFFER_CHUNKS;
tempBuffer.Dispose();
tempBuffer = new MemoryStream(); // Reset Buffer;
}
}
// Finalizing
isCapturing = false;
tempBuffer.Dispose();
captureBuffer.Stop();
if (bufferARE.WaitOne(bufferLength + 1)) currentBufferPart = (currentBufferPart + 1) % BUFFER_CHUNKS; // That on next start the correct bufferpart will be read.
stateControlARE.Set();
Во время захвата ReportChunk
передает данные в динамик как событие, на которое можно подписаться. динамик часть инициализируется следующим образом:
// Creating the dxdevice.
dxdevice = new Device(device);
dxdevice.SetCooperativeLevel(hWnd, CooperativeLevel.Normal);
// Creating the buffer
bufferDescription = new BufferDescription();
bufferDescription.BufferBytes = bufferSize;
bufferDescription.Format = input.Format;
bufferDescription.ControlVolume = true;
bufferDescription.GlobalFocus = true; // That sound doesn't stop if the hWnd looses focus.
bufferDescription.StickyFocus = true; // - " -
buffer = new SecondaryBuffer(bufferDescription, dxdevice);
chunkQueue = new Queue<byte[]>();
// Creating buffer control
bufferARE = new AutoResetEvent(false);
// Register at input device
input.ChunkCaptured += new AInput.ReportBuffer(input_ChunkCaptured);
Данные помещаются методом события в очередь, просто:
chunkQueue.Enqueue(buffer);
bufferARE.Set();
Заполнение буфера воспроизведения и запуск / остановка буфера воспроизведения выполняется другим потоком:
// Initializing
int wp = 0;
bufferARE.WaitOne(); // wait for first chunk
// Playing / writing data to play buffer.
while (isPlaying)
{
Thread.Sleep(1);
bufferARE.WaitOne(BufferLength * 3); // If a chunk is played and there is no new chunk we try to continue and may stop playing, else may the buffer runs out.
// Note that this may fails if the sender was interrupted within one chunk
if (isPlaying)
{
if (chunkQueue.Count > 0)
{
while (chunkQueue.Count > 0) wp = writeToBuffer(chunkQueue.Dequeue(), wp);
if (buffer.PlayPosition > wp - chunkSize * 3 / 2) buffer.SetCurrentPosition(((wp - chunkSize * 2 + bufferSize) % bufferSize));
if (!buffer.Status.Playing)
{
buffer.SetCurrentPosition(((wp - chunkSize * 2 + bufferSize) % bufferSize)); // We have 2 chunks buffered so we step back 2 chunks and play them while getting new chunks.
buffer.Play(0, BufferPlayFlags.Looping);
}
}
else
{
buffer.Stop();
bufferARE.WaitOne(); // wait for a filling chunk
}
}
}
// Finalizing
isPlaying = false;
buffer.Stop();
stateControlARE.Set();
writeToBuffer
просто записывает в очередь блок в буфер на this.buffer.Write(wp, data, LockFlag.None);
и заботится о bufferSize
и chunkSize
и wp
, которые представляют последнюю позицию записи. Я думаю, что это все, что важно в моем коде. Возможно, определения отсутствуют, и, по крайней мере, есть другой метод, который запускает / останавливает = контролирует потоки.
Я разместил этот код на тот случай, если я допустил ошибку при заполнении буфера или неверная инициализация. Но я предполагаю, что эта проблема возникает из-за того, что выполнение байт-кода C # выполняется слишком медленно или что-то в этом роде. Но, в конце концов, мой вопрос все еще открыт: Мой вопрос заключается в том, как уменьшить задержку и как избежать помех, которых не должно быть?