Лучший отвечающий
DirectSound - проблема в воспроизведении

Вопрос
-
В общем есть програма - задача которой - захватить звук, передать по TCP, и воспроизвести на принимающей стороне.Собственно говоря код:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Microsoft.DirectX.DirectSound; using System.IO; using System.Threading; using System.Net; using System.Net.Sockets; namespace My_Voise_Chat { public partial class Form1 : Form { private CaptureBufferDescription captureBufferDescription; private AutoResetEvent autoResetEvent; private Notify notify; private WaveFormat waveFormat; private Capture capture; private int bufferSize; private CaptureBuffer captureBuffer; //Listens and sends data on port 1550, used in synchronous mode. private Device device; private SecondaryBuffer playbackBuffer; private BufferDescription playbackBufferDescription; private bool bStop; private string ip; private int port; private volatile int nUdpClientFlag; private volatile bool bIsCallActive; public MemoryStream mStrem = new MemoryStream(); public MemoryStream mStrem2 = new MemoryStream(); public int send = 0; public int rec = 0; public int num = 0; public string Log = ""; public Thread receiverThread; public Thread senderThread; NetworkStream myns; BinaryWriter mysw; TcpClient myclient; TcpListener mytcpl; Socket mysocket; NetworkStream ns; public Form1() { InitializeComponent(); Initialize(); } private void Initialize() { try { device = new Device(); device.SetCooperativeLevel(this, CooperativeLevel.Normal); CaptureDevicesCollection captureDeviceCollection = new CaptureDevicesCollection(); DeviceInformation deviceInfo = captureDeviceCollection[0]; capture = new Capture(deviceInfo.DriverGuid); short channels = 1; //Stereo. short bitsPerSample = 16; //16Bit, alternatively use 8Bits. int samplesPerSecond = 22050; //11KHz use 11025 , 22KHz use 22050, 44KHz use 44100 etc. //Set up the wave format to be captured. waveFormat = new WaveFormat(); waveFormat.Channels = channels; waveFormat.FormatTag = WaveFormatTag.Pcm; waveFormat.SamplesPerSecond = samplesPerSecond; waveFormat.BitsPerSample = bitsPerSample; waveFormat.BlockAlign = (short)(channels * (bitsPerSample / (short)8)); waveFormat.AverageBytesPerSecond = waveFormat.BlockAlign * samplesPerSecond; captureBufferDescription = new CaptureBufferDescription(); captureBufferDescription.BufferBytes = waveFormat.AverageBytesPerSecond / 5;//approx 200 milliseconds of PCM data. captureBufferDescription.Format = waveFormat; playbackBufferDescription = new BufferDescription(); playbackBufferDescription.BufferBytes = waveFormat.AverageBytesPerSecond / 5; playbackBufferDescription.Format = waveFormat; //playbackBuffer = new SecondaryBuffer(playbackBufferDescription, device); bufferSize = captureBufferDescription.BufferBytes; } catch (Exception ex) { MessageBox.Show(ex.Message, "VoiceChat-Initialize ()", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void button1_Click(object sender, EventArgs e) { Call(); } private void Call() { try { ip = textBox1.Text; port = int.Parse(textBox2.Text); //Get the IP we want to call. InitializeCall(); } catch (Exception ex) { MessageBox.Show(ex.Message, "VoiceChat-Call ()", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void Send() { try { captureBuffer = new CaptureBuffer(captureBufferDescription, capture); CreateNotifyPositions(); int halfBuffer = bufferSize / 2; captureBuffer.Start(true); bool readFirstBufferPart = true; int offset = 0; MemoryStream memStream = new MemoryStream(); MemoryStream memS = new MemoryStream(); bStop = false; while (!bStop) { num++; autoResetEvent.WaitOne(); memStream.Seek(0, SeekOrigin.Begin); captureBuffer.Read(offset, memStream, halfBuffer, LockFlag.None); byte[] dataToWrite = memStream.GetBuffer(); // playbackBuffer = new SecondaryBuffer(playbackBufferDescription, device); // playbackBuffer.Write(0, dataToWrite, LockFlag.None); // playbackBuffer.Play(0, BufferPlayFlags.Default); memS.Seek(0, SeekOrigin.Begin); int Len = dataToWrite.Length; byte[] arrLen = BitConverter.GetBytes(Len); memS.Write(arrLen, 0, arrLen.Length); memS.Write(dataToWrite, 0, dataToWrite.Length); byte[] ToWrite = memS.GetBuffer(); if (offset == 0) { mStrem.Write(ToWrite, 0, ToWrite.Length); } else { mStrem.Write(ToWrite, 0, ToWrite.Length); byte[] ToSend = mStrem.GetBuffer(); Sender(ToSend); mStrem.Seek(0, SeekOrigin.Begin); } readFirstBufferPart = !readFirstBufferPart; offset = readFirstBufferPart ? 0 : halfBuffer; } } catch (Exception ex) { MessageBox.Show(ex.Message, "VoiceChat-Send ()", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { captureBuffer.Stop(); } } public void Sender(byte[] ToWrite) { myclient = new TcpClient(ip, port);//Connecting with server myns = myclient.GetStream(); mysw = new BinaryWriter(myns); mysw.Write(ToWrite, 0, ToWrite.Length); send++; myns.Flush(); myclient.Close(); } private void Receive() { try { //Receive data. IPAddress ipAdd = IPAddress.Parse(ip); mytcpl = new TcpListener(ipAdd, port); mytcpl.Start(); // Start Listening on That Port mytcpl.AcceptTcpClient(); mysocket = mytcpl.AcceptSocket(); // Accept Any Request From Client and Start a Session ns = new NetworkStream(mysocket); mStrem2.Seek(0, SeekOrigin.Begin); for (int i = 0; i < 2; i++) { byte[] arrLenght = new byte[4]; ns.Read(arrLenght, 0, 4); int len = BitConverter.ToInt32(arrLenght, 0); rec++; byte[] byteData = new byte[len]; //Считываем и заносим в очередь int tmp = 0; while (tmp < len) { tmp += ns.Read(byteData, tmp, len - tmp); } mStrem2.Write(byteData, 0, byteData.Length); } byte[] arrRec = mStrem2.GetBuffer(); playbackBuffer = new SecondaryBuffer(playbackBufferDescription, device); //playbackBuffer.Write(0, mStrem2, bufferSize, LockFlag.None); playbackBuffer.Write(0, arrRec, LockFlag.None); playbackBuffer.Play(0, BufferPlayFlags.Default); mytcpl.Stop(); if (mysocket.Connected == true) // Looping While Connected to Receive Another Message { while (true) { Receive(); // Back to First Method } } } catch (Exception ex) { MessageBox.Show(ex.Message, "VoiceChat-Receive ()", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void CreateNotifyPositions() { try { autoResetEvent = new AutoResetEvent(false); notify = new Notify(captureBuffer); BufferPositionNotify bufferPositionNotify1 = new BufferPositionNotify(); bufferPositionNotify1.Offset = bufferSize / 2 - 1; bufferPositionNotify1.EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle(); BufferPositionNotify bufferPositionNotify2 = new BufferPositionNotify(); bufferPositionNotify2.Offset = bufferSize - 1; bufferPositionNotify2.EventNotifyHandle = autoResetEvent.SafeWaitHandle.DangerousGetHandle(); notify.SetNotificationPositions(new BufferPositionNotify[] { bufferPositionNotify1, bufferPositionNotify2 }); } catch (Exception ex) { MessageBox.Show(ex.Message, "VoiceChat-CreateNotifyPositions ()", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void InitializeCall() { try { receiverThread = new Thread(new ThreadStart(Receive)); senderThread = new Thread(new ThreadStart(Send)); bIsCallActive = true; //Start the receiver and sender thread. receiverThread.Start(); senderThread.Start(); } catch (Exception ex) { MessageBox.Show(ex.Message, "VoiceChat-InitializeCall ()", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void UninitializeCall() { bStop = true; bIsCallActive = false; } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (bIsCallActive) { UninitializeCall(); } } private void button2_Click(object sender, EventArgs e) { textBox3.Text = Log; MessageBox.Show("Отправил " + send + ", в то время как принял " + rec); } } }
В общем прога все захватывает, передает, принимает. И вот на этапе воспроизведения начинаются бока. Воспроизводится ровно половина, т.е. я слышу себя отрывками.Проверял принимаемый массив - там есть данные, т.е. дело не в том что он на половину пуст.Как мне кажется я что-то недопонял на этапе задания ссылки на объект - массив:playbackBuffer = new SecondaryBuffer(playbackBufferDescription, device);
Хотя возможно и причина в другом.Заранее спасибо.- Перемещено Tagore Bandlamudi 1 октября 2010 г. 21:56 MSDN Forums consolidation (От:Visual C#)
12 апреля 2010 г. 16:53
Ответы
-
У меня подобная проблема была, я её частично решил добившись приемлимого качества звучания. Собственно ИМХО затык в след:
1. съем звука впринципе нормально, (берешь на принимающей стороне сливаешь звук в файл а потом проигрываешь его, если все ок, то проблема именно в проигрывании. )
2. Смотри, ты постоянно открываешь, закрываешь сокеты. Про то, что это дорого по памяти я говорить не буду, но это время между сэмплами. Совсем его убрать не получится но минимизировать можно. У меня ещё проблема была на время компрессии/декомпрессии. Грубо говоря, если сьем звука идет с дельтой времени между семплами = 0, то на приемной стороне эта дельта уже больше 0. Дельта = (преобразование + доставка). Дальше я посчитал просто:
44100 = 1сек
Х = 1+дельта
т.е. с какой частотой надо проигрывать вторичный буффер, чтобы погасить задержку на обработку. При создании вторичного буффера, (я использую их 2 на всяк случай, просто по очереди загоняю туда сэмплы) я указываю ему что я хочу иметь контроль над частотой:
private void Init() { _device = new Device(_deviceGuid); _device.SetCooperativeLevel(_hwnd, CooperativeLevel.Priority); _wave = new WaveFormat { Channels = 1, BitsPerSample = 16, SamplesPerSecond = 44100, FormatTag = WaveFormatTag.Pcm, BlockAlign = 2,//(short)(_chanells * _bitPerSample / 8), AverageBytesPerSecond = 88200//(_chanells * _bitPerSample / 8) * _samplesPerSecond }; _buffdesc = new BufferDescription { BufferBytes = _wave.AverageBytesPerSecond/2, GlobalFocus = true, Format = _wave, ControlFrequency = true, ControlPan = true }; _playbackBuffer = new SecondaryBuffer(_buffdesc, _device); _playbackBuffer2 = new SecondaryBuffer(_buffdesc, _device); }
а при проигрывании высчитываю изменение частоты проигрывания буффера:
var timestop = DateTime.Now; buffer = Decryptor.Decrypt(buffer, buffer.Length); ALawDecoder.ALawDecode(buffer, out buffer); var delta = DateTime.Now.Subtract(timestop).Milliseconds*2 + 4; var controlbuffer = firstBuffer ? _playbackBuffer : _playbackBuffer2; firstBuffer = !firstBuffer; controlbuffer.Stop(); controlbuffer.SetCurrentPosition(0); controlbuffer.Frequency = 44100/(1 + (delta/1000)); controlbuffer.Write(0, buffer, LockFlag.None); controlbuffer.Play(0, BufferPlayFlags.Default);
не скажу что это окончательное решение... но может что то наведет тебя на мысль как сделать это более правильно.- Предложено в качестве ответа I.Vorontsov 7 июня 2010 г. 7:33
- Помечено в качестве ответа I.Vorontsov 7 июня 2010 г. 9:56
- Снята пометка об ответе I.Vorontsov 7 июня 2010 г. 9:56
- Помечено в качестве ответа I.Vorontsov 9 июня 2010 г. 6:48
4 июня 2010 г. 8:29
Все ответы
-
Попробуйте UDP, на сколько я помню вам это уже рекомендовали
http://social.msdn.microsoft.com/Forums/ru-RU/csharpru/thread/01064df7-80e4-4d14-ad1b-0649e9715af3
12 апреля 2010 г. 20:59 -
К сожалению для решения поставленной мне задачи нужно использовать TCP. И к тому же дело не в протоколе передачи. все передается правильно - ошибка где-то в алгоритме...12 апреля 2010 г. 22:21
-
У меня подобная проблема была, я её частично решил добившись приемлимого качества звучания. Собственно ИМХО затык в след:
1. съем звука впринципе нормально, (берешь на принимающей стороне сливаешь звук в файл а потом проигрываешь его, если все ок, то проблема именно в проигрывании. )
2. Смотри, ты постоянно открываешь, закрываешь сокеты. Про то, что это дорого по памяти я говорить не буду, но это время между сэмплами. Совсем его убрать не получится но минимизировать можно. У меня ещё проблема была на время компрессии/декомпрессии. Грубо говоря, если сьем звука идет с дельтой времени между семплами = 0, то на приемной стороне эта дельта уже больше 0. Дельта = (преобразование + доставка). Дальше я посчитал просто:
44100 = 1сек
Х = 1+дельта
т.е. с какой частотой надо проигрывать вторичный буффер, чтобы погасить задержку на обработку. При создании вторичного буффера, (я использую их 2 на всяк случай, просто по очереди загоняю туда сэмплы) я указываю ему что я хочу иметь контроль над частотой:
private void Init() { _device = new Device(_deviceGuid); _device.SetCooperativeLevel(_hwnd, CooperativeLevel.Priority); _wave = new WaveFormat { Channels = 1, BitsPerSample = 16, SamplesPerSecond = 44100, FormatTag = WaveFormatTag.Pcm, BlockAlign = 2,//(short)(_chanells * _bitPerSample / 8), AverageBytesPerSecond = 88200//(_chanells * _bitPerSample / 8) * _samplesPerSecond }; _buffdesc = new BufferDescription { BufferBytes = _wave.AverageBytesPerSecond/2, GlobalFocus = true, Format = _wave, ControlFrequency = true, ControlPan = true }; _playbackBuffer = new SecondaryBuffer(_buffdesc, _device); _playbackBuffer2 = new SecondaryBuffer(_buffdesc, _device); }
а при проигрывании высчитываю изменение частоты проигрывания буффера:
var timestop = DateTime.Now; buffer = Decryptor.Decrypt(buffer, buffer.Length); ALawDecoder.ALawDecode(buffer, out buffer); var delta = DateTime.Now.Subtract(timestop).Milliseconds*2 + 4; var controlbuffer = firstBuffer ? _playbackBuffer : _playbackBuffer2; firstBuffer = !firstBuffer; controlbuffer.Stop(); controlbuffer.SetCurrentPosition(0); controlbuffer.Frequency = 44100/(1 + (delta/1000)); controlbuffer.Write(0, buffer, LockFlag.None); controlbuffer.Play(0, BufferPlayFlags.Default);
не скажу что это окончательное решение... но может что то наведет тебя на мысль как сделать это более правильно.- Предложено в качестве ответа I.Vorontsov 7 июня 2010 г. 7:33
- Помечено в качестве ответа I.Vorontsov 7 июня 2010 г. 9:56
- Снята пометка об ответе I.Vorontsov 7 июня 2010 г. 9:56
- Помечено в качестве ответа I.Vorontsov 9 июня 2010 г. 6:48
4 июня 2010 г. 8:29