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.
Já
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.