Voz sobre IP no Delphi
Matheus Degiovani

Com a popularização da banda larga, cada vez mais a comunicação de jogos multiplayer se dá através do uso da voz ao invés de comandos de texto. Frequentemente chamado de VoIP (Voice over IP ou Voz sobre IP), esse tipo de tecnologia têm se tornado popular através de serviços como o TeamSpeak e o Skype (que inclusive permite efetuar ligações para telefones convencionais).

Na melhor tradição da TILT online, adaptamos a biblioteca HawkVoice, que executa codificação e decodificação de fala, para funcionar em ambiente Delphi e criamos um framework para tornar rápida e fácil a implantação de comunicação por voz em sua aplicação.

HawkVoice:

Criada para facilitar o desenvolvimento de aplicações de compressão de voz, a HawkVoice é uma biblioteca extremamente simples, praticamente livre de quaisquer funções supérfluas.

Ela suporta diversos codecs para codificação/decodificação de voz, que utilizam a seguinte taxa de transmissão:

Kbps

Codec

Constante (hawkvoice.pas)

64
32
13.2
4.8
4.5-2.3
2.4
até 2.4
1.8-1.4

G.711 u-law
Intel/DVI ADPCM
GSM
LPC
CELP
LPC10
VBR-LPC10
OpenLPC

HV_PCM_64_CODEC
HV_ADPCM_32_CODEC
HV_GSM_CODEC
HV_LPC_CODEC
HV_CELP_4_5_CODEC, HV_CELP_3_0_CODEC, HV_CELP_2_3_CODEC
HV_LPC10_CODEC
HV_VBR_LPC10_CODEC
HV_LPC_1_8_CODEC, HV_LPC_1_4_CODEC

Quanto maior a taxa, mais largura de banda os pacotes individuais ocupam e melhor a qualidade do som resultante.

Como a própria biblioteca inclui os codecs, nenhum arquivo além da dll principal (hvdi.dll) precisa ser enviado com as aplicações que a utilizam, facilitando o processo de distribuição. A utilização dessa dll é centrada em sete funções principais:

hvdiNewEncState: Cria um estado de codificação (ou seja, faz um setup das informações necessárias para a codificação de voz).

hvdiNewDecState: Cria um estado de decodificação.

hvdiDeleteEncState: Libera um estado de codificação criado anteriormente.

hvdiDeleteDecState: Libera um estado de decodificação criado anteriormente.

hvdiEncStateSetCodec: Escolhe o codec de compressão de voz.

hvdiPacketEncode: Codifica um pacote de voz.

hvdiPacketDecode: Decodifica um pacote de voz.

Como se pode perceber, a utilização dessa biblioteca consiste em criar um estado de codificação no emissor da voz e um estado de decodificação no receptor, e repetidamente chamar as funções de codificação e decoficação.

Framework da TILT:

Porém, como tudo que é bom pode ficar ainda melhor, criamos um conjunto de classes que automatiza esse processo, facilitando inclusive a captura de som pelo microfone.

Para os mais aventureiros, a classe TTiltHawkVoice encapsula o processo descrito acima, facilitando a compressão de dados PCM. Esse é o tipo padrão de codificação de sons, e, entre outros, é encontrado em arquivos Wave e nos dados recebidos através de dispositivos de entrada como um microfone.

Para utilizar essa classe, basta criá-la, escolher o codec para codificação (através das constantes disponibilizadas acima) e chamar a função encode, passando como parâmetros um buffer (que deve ter o tamanho da propriedade framesize) e decodificar com o decode.

Utilizar essa classe permite um melhor controle da aplicação, e deixa o programador livre para, se desejado, utilizar outras bibliotecas de som (como o DirectSound do DirectX) para realizar o input/output dos dados.

No entanto, a grande maioria das aplicações pode sobreviver utilizando as APIs padrões do windows WaveIn (para entrada de voz pelo microfone) e WaveOut (para saída).

Para essas aplicações, o ideal é utilizar a classe TTiltWaveHawkVoice, que simplifica ainda mais esse processo, reduzindo o trabalho do programador a responder um evento (quando um buffer capturado do dispositivo de entrada foi preenchido e codificado) e chamar uma função (quando um buffer deve ser decodificado e enviado ao dispositivo de saída).

Apenas Adicione Água:

Para demonstrar como utilizar essa classe, o exemplo a seguir cria um sitema de comunicação de voz através de redes, utilizando pacotes UDP. Em primeiro lugar, deve-se estabelecer as variáveis globais do sistema:

hawk: TTiltWaveHawkVoice;
inStream, outStream: TMemoryStream;

A variável "hawk" guarda o objeto da classe TTiltWaveHawkVoice que será usado para capturar, codificar, decodificar e reproduzir as vozes dos participantes.

inStream e outStream são áreas da memória utilizadas para ler e escrever dados dos pacotes UDP. O componente de comunicação udp usado será o TNMUDP, encontrado na versão 5 do Delphi, porém componentes de versões diferentes devem requerer mudanças mínimas (em particular, para os componentes indy basta substituir "sendStream(outStream)" por "sendBuffer(outStream.memory^, outStream.size)" para envio e utilizar o próprio stream de dados recebido pelo servidor UDP). Esse componente terá o nome "udp".

A primeira tarefa a ser realizada é inicializar os objetos necessários e abrir o dispositivo de entrada de wave (microfone) assim que o formulário for criado (evento onCreate do formulário principal):

//cria um hawkVoice
hawk:= TTiltWaveHawkVoice.Create;
//cria streams de envio/recebimento
inStream:= TMemoryStream.create;
outStream:= TMemoryStream.create;
//habilita gravação (entrada)
hawk.Gravando:= true;

E finalizá-los no evento onDestroy:

//libera o hawkVoice
hawk.free;
//libera os streams
inStream.free;
outStream.free;

O próprio objeto "hawk" fica responsável por criar buffers, capturar som e codificar os dados para envio. Para ser notificado quando um buffer foi preenchido e está pronto para ser enviado para outros usuários, basta criar um procedimento que agirá como evento:

procedure TForm1.hawkBufferCheio(sender: TObject;
buffer: pointer; size: integer);
begin
//limpa o stream de saída
outStream.Clear;
//escreve o buffer lido para o stream

outStream.Write(buffer^, size);
//envia o stream para o computador remoto
udp.SendStream(outStream);
end;

Esse evento deve ser declarado na área public do formulário principal. Nele, um buffer pronto para envio (variável buffer) é escrita no stream de saída (outStream) e enviado ao micro remoto através do componente udp. Para habilitar a chamada desse evento, após criar o TTiltWaveHawkVoice (no evento onCreate do formulário) basta adicionar a seguinte linha:

//coloca o evento de bufferCheio no hawkVoice 
hawk.OnBufferCheio:= hawkBufferCheio;

Com meia dúzia de linhas, a aplicação já está enviando dados codificados para micros remotos através da rede. O código para decodificação, executado quando um pacote é recebido (evento OnDataReceived do componente UDP), também é bem simples:

//limpa o stream de entrada
inStream.Clear;
//lê os dados recebidos para o stream
udp.ReadStream(inStream);
//envia o buffer recebido para decodificação
hawk.bufferChegou(inStream.Memory, inStream.Size);

E pronto! Seguindo passo a passo a criação desse exemplo, os dados são enviados para o próprio micro local, então pode-se testar a qualidade dos diversos codecs disponibilizados pela HawkVoice. Com algumas melhorias, como a possibilidade de seleção de codec e especificação de endereço remoto, é possível criar um sistema completo para comunicação por voz de maneira bem simples.


Download...
Clique no link para fazer o download dos arquivos. Se sua assinatura do club TILT está para vencer, clique aqui e saiba como renová-la.

TTiltWaveHawkVoice e HawkVoice
 
online