Sound (Voice) over Network

Started by
14 comments, last by hplus0603 11 years, 3 months ago

Oh yea, and for outputting the audio, SDL has a function that lets you directly write the PCM data out to the hardware to be played, I utilized this functionality in a game engine project and built a scripted audio synth engine ontop of it.

look at the Command_S_XXXXX() functions, specifically Command_S_End() which makes the SDL Mix_QuickLoad_RAW call itself.

http://revolude.svn.sourceforge.net/viewvc/revolude/source/audio.cpp?revision=62&view=markup

Advertisement

Well I've got OpenAL setup so I can just play it through that without a problem.

I think tomorrow I'm going to atleast get the encoded audio sent to server and across to clients, having clients decode what they get sent. From there I'll have a much better idea of what needs fixing. I'll be missing packets and have packets in the wrong order but I should be able to atleast hear something through my speakers.

I'll be testing on localhost anyway so It will probably be audible :)

FWIW: VDK and Ventrilo are commercial products. Mumble you can run your own for free.

Btw: not using compression is a poor choice IMO. If you just get 4:1 data compression, you can go from 11 kHz/8bit data to 22 kHz/16bit data with the same data rate, which is a HUGE improvement in quality.
enum Bool { True, False, FileNotFound };
So I got my encoded voice sending across UDP and seems to play smoothly (on localhost) Now I need to know what I need to do to have it play nicely when connected to other servers.

So from what you've all told me there's 2 main things I need to solve. Packet reordering and packet loss.

Let's start with packet reordering. I imagine I'd have to wait for a certain amount of packets to come in before I can put them in order and play them. How many packets should I be waiting for before i sort them though? 5, 6, 7, 10? If I'm waiting too long then there's going to be a lot of latency.

Any other problems I'll have to solve? I want this to be on par with something like the voice chat in Source Engine games.
A simple algorithm that works OK is this:

1) prefix each packet with a sequence number. A single byte is enough. If you detect an packet that is re-ordered, or a duplicate of a previously received packet, then drop it. You can make this detection simply:
char delta = (char)(receivedpacketseq - lastreceivedpacketseq);
if (delta <= 0) { drop packet; }
else { receive packet, set lastreceivedpacketseq = receivedpacketseq; }

This uses magic of signed/unsigned two's complement math to do the right thing. Just increment the sequence number by one for each packet you send, and let it roll over to 0 after it reaches 255.

2) keep a queue of received data. Let's count this queue in "received packets." Each time you update sound (typically, each time through your main loop,) run this algorithm:

bool playing = false;
queue<packet> receivedqueue;

void update() {
  if (playing) {
    if (soundcard needs data) {
      if (receivedqueue.empty()) {
        playing = false;
        fill with zeros;
      }
      else {
        if (receivedqueue.size() > 3) {
          receivedqueue.erase_the_two_first_elements();
        }
        fill from queue;
      }
    }
  }
  else {
    fill with zeros;
    if (queue.size() >= 2) {
      playing = true;
    }
  }
}

void packet_received(packet p) {
  receivedqueue.push_back(p);
}

If you use threading, add locking as needed.

This is the simplest, most robust algorithm that I know about, and uses a nice interaction between UDP delivery semantics, network behavior, sound card behavior, and general sound playback to deliver robust, reliable sound that compensates for some amount of network jitter, and adapts to network changes over time.

If the jitter is more than two packet's worth of data, you need a better network :-) You could detect this and up the values "2" and "3" in the algorithm above, although this will lead to more playout latency (necessarily, to compensate for the jitter.)
enum Bool { True, False, FileNotFound };

Thanks, I'll give it a try. You say to drop the packet if it's not the in correct order, why not just swap them around?

drop the packet if it's not the in correct order, why not just swap them around?

Because you likely already started playing out zeros where the late packet would have been, and re-ordered packets is very uncommon anyway. If they happen, it's typically on connections so bad you're unlikely to get a good connection anyway (microwave links during rainstorms, et c.)

However, if you can safely pay attention to the out-of-order-arrived packet, then it's of course fine to do so, assuming the code doesn't become too complex or it introduces other problems.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement