none
DirectSound - проблема в воспроизведении RRS feed

  • Вопрос

  • В общем есть програма - задача которой - захватить звук, передать по 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

Все ответы

  • Попробуйте 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