Criando um Tocador de Áudio com Playlist

Última modificação em 16/01/11


Você, provavelmente, já desejou criar seu próprio player de áudio. E isso não é difícil, se usarmos o componente MediaPlayer

Faça o download do programa pronto. Para adicionar músicas à Playlist, cique com o botão direito sobre a área em branco e escolha "Adicionar música".

do Delphi, presente na paleta System. Então, mãos à obra.

Crie um novo projeto e vamos adicionar a ele três componentes Panel: um ficará alinhado na parte superior do formulário, outro na lateral esquerda e o terceiro ocupará a área restante. Mas para que eles se comportem da maneira desejada, vamos ajustar a propriedade Align de cada um deles. Para o Panel que ficará alinhado ao topo, ajuste-a para alTop. Para o que ficará à esquerda, mude para alLeft. O outro, ainda não deve ser alterado: faremos isso via código posteriormente. Assim, sua aplicação, por enquanto, tem essa aparência:


OK, é uma aparência nada agradável, mas vamos modificá-la aos poucos. Clique no Panel1 e vamos alterar sua propriedade Caption para algo que indique o objetivo do programa. Digite, por exemplo, Playlist. Nos panels dois e três, simplesmente apague o texto dos Captions - eles ficarão vazios.

Agora, da paleta Additional, pegue um componente Splitter e coloque próximo ao Panel2. Este componente será muito útil: ele permitirá o redimensionamento do segundo painel quando você executar seu programa, permitindo o ajuste de sua largura conforme seu gosto.  Sempre que o segundo painel for redimensionado, o terceiro painel irá, também, se auto-ajustar ao espaço que lhe sobrou. Mas, para que isso ocorra, vamos inserir o seguinte código no evento OnCreate do formulário:

//Código para o evento OnCreate do form1
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel3.Align := alClient;

end;

O alinhamento alClient diz ao Panel que ele deverá ocupar toda a área restante do formulário. Podemos também eliminar as bordas dos painéis, para que fiquem mais sutis.Para isso, altere a propriedade BevelOuter de cada Panel para bvNone.

Agora, pegue um componente ListBox da paleta Standard e coloque-o no terceiro Panel. Esta ListBox conterá os nomes das músicas que serão executadas. Ajuste o componente no centro do Panel e altere sua propriedade Anchors para que akLeft, akTop, akRight e akBottom fiquem como True. Isso permitirá que, quando o terceiro panel sofrer um redimensionamento, a Listbox o acompanhe. No entanto, deixe um pouco mais de espaço próximo ao rodapé da Listbox, pois lá iremos adicionar os botões que controlarão a playlist. Eis o que você deve ter até o momento:


Se você executar seu aplicativo neste ponto, verá que o redimensionamento com o Splitter já funciona, influenciando nas dimensões dos Panels 2 e 3 e, conseqüentemente, na da Listbox.

Vamos adicionar, agora, o nosso componente principal: o MediaPlayer, da paleta System. Coloque-o no Panel1 e altere sua propriedade Visible para False. Nós não desejamos ver os botões deste componente, pois iremos criar nossos próprios botões de comando para nossa playlist - isso nos permitirá um controle maior sobre as ações do aplicativo.

Para os botões, usaremos seis SpeedButtons (), da paleta Additional. Coloque-os abaixo da ListBox. É interessante alterar a propriedade Name de cada um deles de acordo com a função que irá exercer. Assim, na ordem, renomeie-os para BtnBack, BtnNext, BtnPlay, BtnPause, BtnStop e BtnSair - eles serão, respectivamente, os botões de voltar, avançar, tocar, pausar e parar as músicas da playlist; o último irá fechar o aplicativo. Devemos também colocar pequenas figuras nestes botões. No arquivo zipado disponibilizado para download estão as figuras usadas para cada botão, prontas para você utilizar. Basta associá-las à propriedade Glyph de cada SpeedButton.

Ainda, abaixo da seqüência de botões, coloque uma TrackBar (paleta Win32). Ela será usada como controle de volume do software. Ajuste a propriedade ThumbLength da TrackBar para 14. Isso deixará o seu "ponteiro" em um tamanho menor, mais discreto. Ajuste também a propriedade TickMarks para tmTopLeft. Assim, as marcas indicadoras de posição (os "risquinhos" milimétricos da TrackBar) ficarão na parte superior do componente, e não abaixo dele. Agora, ajuste a propriedade TickStyle para tsManual. Isso eliminará todos os "risquinhos" da TrackBar, deixando apenas os indicadores inicial e final de posição, proporcionando ao  componente um visual mais "limpo". Por fim, altere o valor da propriedade Max para 99. Esse será o volume máximo que o componente deverá atingir. Cabe aqui uma explicação: teoricamente, o volume máximo deveria ser 100 (ou seja, 100% do total obtido), mas por um falha do componente, ao chegar a 100, o volume é totalmente zerado - o que não pode acontecer. Por isso, deixe-o com o valor de 99. Essa diferença de 1 ponto não faz diferença ao ouvido humano.
 

De volta à paleta Standard, adicione ao formulário, em qualquer local, um componente PopupMenu. Este é um menu especial, daqueles que são acionados quando clicamos em cima de algum objeto com o botão direito do mouse. O que queremos aqui é que, ao clicarmos com o botão direito do mouse na Listbox, surja um menu com as opções de adicionar e remover faixas da playlist. Para isso, clique na ListBox e busque a propriedade PopupMenu. Clique na caixa de seleção logo à frente e escolha PopupMenu1. Pronto: a ligação entre o nosso menu e a Listbox está feita.
 

Falta adicionar opções ao menu. Para isso, clique duas vezes sobre o componente que você acabou de colocar no formulário e digite Adicionar música e pressione ENTER. Depois, digite Remover faixa e pressione ENTER. Você terá algo semelhante a isto:


O nosso pequeno menu já tem opções suficientes agora. Mas, óbvio, falta codificá-las, mas faremos isso depois. Por enquanto, precisamos de mais dois componentes: um Timer (paleta System) e um OpenDialog, da paleta Dialogs.
 

Codificando o aplicativo

Agora é que entramos na parte mais trabalhosa. Dê dois cliques no seu formulário para entrarmos na Unit1, onde iremos iniciar a codificação. Antes da seção Implementation, vamos declarar algumas variáveis que serão necessárias:

Lista : TStringList;
PLAYING : Boolean = FALSE;
Cont : Integer = 0 ;

A variável Lista, do tipo TStringList, é capaz de armazenar uma lista de textos: é ideal para guardar nossa playlist, que nada mais é do que uma porção de caminhos de pastas e arquivos de áudio. O objetivo é que a lista sempre carregue e salve um arquivo .TXT (texto simples) contendo os nomes das músicas escolhidas. A variável PLAYING, do tipo Boolean, permite que o sistema saiba se ainda há música  em execução ou não: sempre que se pressionar o botão Play, PLAYING deverá ser True (isto é, está em execução); sempre que se pressionar o botão Stop, ela deverá ser False. Note que ela já foi declarada e, ao mesmo tempo, iniciada como False, pois o aplicativo não inicia tocando música de imediato. E, por fim, a variável Cont, que armazenará qual faixa da variável Lista está sendo tocada. Isso é necessário para que, ao retomarmos uma música depois de uma pausa (botão Pause), o player continue executando a mesma música, ao invés de retornar para a primeira da lista de novo. Note também que Cont já foi iniciada com 0 (zero), pois 0 é o primeiro item de uma TStringList, 1 é o segundo, 2 é o terceiro, e assim por diante. Ou seja, ao começar a tocar, inicia-se pela primeira música da Lista.

Vamos usar a variável Lista agora: no evento OnCreate do formulário, após a linha Panel3.Align := alClient, digite o seguinte:

Lista := TSTringList.Create;

if FileExists(ExtractFilePath(ParamStr(0))+'Lista.txt') then begin
 Lista.LoadFromFile(ExtractFilePath(ParamStr(0))+'Lista.txt');

for i:=0 to Lista.Count-1 do
ListBox1.Items.Add(ExtractFileName(Lista.Strings[i]));
end;

 

Vamos analisar o que fizemos: toda lista de Strings precisa ser criada na memória primeiro para poder ser usada; por isso, usamos TStringList.Create e atribuímos essa nova lista à variável já declarada. Depois, verificamos se já existia previamente um arquivo texto com alguma playlist salva anteriormente com a função FileExists. Se FileExists for True, então carrega a lista com as músicas através da função LoadFromFile. Note a utilização de uma função adicional: ExtractFilePath(ParamStr(0)). Este é um meio que se tem de descobrir onde está o executável do nosso programa. Como desejamos armazenar nosso arquivo texto (Lista.txt) no mesmo local do executável para facilitar, usamos ParamStr(0), que contém todo o caminho de pastas até o nome do arquivo executável e, ao mesmo tempo, extraímos com ExtractFilePath apenas a informação que nos interessava: o diretório do executável.

Por fim, usamos um laço for...do para adicionarmos ao componente ListBox, um a um, todos os arquivos contidos em Lista.txt, mas sem o caminho das pastas, deixando apenas o nome do arquivo listado. O caminho de pastas pode ser retirado usando-se a função ExtractFileName(), que extrai de uma string apenas a porção que corresponde exatamente o nome do arquivo.

Estes procedimentos acontecem todos no evento OnCreate do form. Vamos codificar agora a opção do menu Popup Adicionar Música. Em seu evento OnClick, digite este código:

 if OpenDialog1.Execute then begin
  Lista.Add(OpenDialog1.FileName);
  ListBox1.Items.Add(ExtractFileName(OpenDialog1.FileName));
 
end;


O código acima faz com que uma caixa de diálogo "Abrir Arquivo", típica do Windows, apareça na tela para que o usuário possa escolher qual arquivo deseja adicionar à playlist. Com o método Execute, estamos dizendo que, se o usuário escolher um arquivo e clicar no botão abrir, o nome do arquivo será passado à nossa Lista (através do método Add()) e também à playlist que aparece na ListBox (através de Items.Add()), mas não sem antes remover todo o caminho de pastas (de novo!) para que possamos exibir apenas o nome da música.

O menu Popup Remover faixa faz o inverso. Ele pergunta, em primeiro lugar, se desejamos mesmo remover a faixa selecionada. Para isso, usamos Application.Messagebox, que mostra uma caixa de mensagens na tela, típica do Windows também, e que retorna IDYES se o usuário clicar em "Sim", e IDNO se escolher "Não":

if application.messagebox(pchar('Remover faixa '+ ListBox1.Items.strings[ListBox1.ItemIndex] + '?'), pchar(Caption),36) = IDYES then begin
 Lista.Delete(ListBox1.ItemIndex);
 ListBox1.DeleteSelected;
 
if Cont > 0 then
  Cont := Cont - 1;
end;


Note que, para sabermos em qual música o usuário clicou, usamos uma propriedade interessante da ListBox chamada ItemIndex. Ela armazena a posição em que está o texto sobre o qual o mouse clicou. O que precisamos fazer depois é pedir que esse item seja removido da lista - aliás, de ambas: da lista que armazena todo o caminho de pastas até as músicas e também das ListBox. A linha Lista.Delete(ListBox1.ItemIndex) apaga o nome do arquivo que contém o caminho completo e ListBox1.DeleteSelected apaga o item selecionado na ListBox que, teoricamente, é o que mais importa para o usuário, pois é o item que ele vê na tela. O que vem depois é um complemento para evitar falhas na execução da playlist: o nosso contador Cont, que guarda o número da música em execução, precisa também saber que a música atualmente clicada foi removida e que, em virtude disso, ele deve voltar para a faixa anterior. Foi o que fizemos em Cont := Cont - 1.  Mas note que ele só subtrairá 1 do valor de Cont se realmente houver mais músicas na listagem - ou seja, se Cont > 0  pois, se Cont for zero, significa que não há como voltar para a música anterior: a lista está vazia.
 

Os botões Play, Pause, Stop, Avançar e Voltar

Agora, vamos à melhor parte: codificar os botões de Play e Stop. Primeiro, o Play. No seu evento OnClick, coloque isto:

 if PLAYING then MediaPlayer1.Resume

  else begin

   MediaPlayer1.Close;
   MediaPlayer1.FileName := Lista.Strings[Cont];
   ListBox1.Selected[Cont] := True;
   MediaPlayer1.Open;
   MediaPlayer1.Play;
   Timer1.Enabled := True;//habilita timer
 end;

PLAYING := True;

Ao clicar em Play, nosso software precisa verificar se ele deverá começar a tocar uma faixa desde o começo ou se é para continuar uma faixa anteriormente pausada. Para isso, testamos nossa variável PLAYING: se ela for True, indica que a faixa já estava em execução e, portanto, provavelmente agora estava pausada, devendo continuar a partir do ponto de parada. Isso pode ser feito com o método Resume do MediaPlayer: ele prossegue tocando a música exatamente a partir do ponto onde ela se encontrava. É o que faz a linha  if PLAYING then MediaPlayer1.Resume. Mas se PLAYING não era True, então não havia música em execução, devendo começar uma desde o início.

Precisamos, então, mostrar ao player qual é a faixa que ele deve tocar, escolhendo a atual da nossa lista através de Lista.Strings[Cont]. Lembre-se que Cont guarda qual música da nossa lista deve ser tocada e, passando Cont como parâmetro para nossa Lista através da propriedade Strings, pegamos o nome do arquivo a ser executado na seqüência. Isso quer dizer que, se Cont contiver o valor 5, ao fazermos Lista.Strings[Cont], nossa Lista saberá que deve pegar a sexta linha e atribuir ao MediaPlayer. É o que acontece em MediaPlayer1.FileName := Lista.Strings[Cont].

Adicionalmente, a ListBox deve mostrar qual é a faixa selecionada com  ListBox1.Selected[Cont] := True. Essa linha permite isso, usando a mesma variável Cont para que a ListBox possa indicar qual linha deve estar marcada. O que vem a seguir é apenas uma consequencia: o MediaPlayer deve ser aberto com Open e executar a canção com Play. Além disso, o Timer deve ser habilitado com Timer1.Enabled:=True e nossa variável PLAYING deve passar para True, indicando ao software que há uma faixa sendo reproduzida.

O botão de Stop é mais fácil:

 if PLAYING then begin
  MediaPlayer1.Stop;
  PLAYING := FALSE;
  Timer1.Enabled := False;
 end;

Aqui, se há faixa sendo tocada, o MediaPlayer deve ser parado através da função Stop. Além disso, PLAYING deve se tornar False e o Timer deve ficar desabilitado, com Timer1.Enabled := False.

E, mais fácil ainda, é o botão de pausa:

 MediaPlayer1.Pause;


Aqui, nada de duvidoso: apenas chamamos a função Pause do componente MediaPlayer. Mas, para os botões de Avançar e Retroceder, as coisas são um pouco diferentes. Para cada vez que clicarmos num deles, a execução da faixa atual e o timer deverão ser parados; o MediaPlayer deve ser fechado, um novo arquivo musical da lista deve ser atribuído a ele em sua propriedade FileName para, então, ele ser aberto novamente e ter sua função Play chamada. Como os dois botões precisarão da mesma funcionalidade, é melhor criarmos uma procedure separada e depois apenas chamá-la em cada um dos eventos OnClick. Para isso, localize o início dos códigos da sua Unit e, entre as declarações Type e Private, acrescente esta:

 procedure MudaFaixa(Valor : Integer);

Isso apenas indica ao Delphi que desejamos criar uma nova procedure. Para criá-la de fato, clique com o cursor do mouse sobre ela e pressione CTRL+SHIFT+C no teclado. Isso fará com que o "corpo" da função seja criado. Nele, digite o seguinte:

if (Valor <> -1) and (Valor <> 1) then exit;

if PLAYING then begin
MediaPlayer1.Stop;
Timer1.Enabled := False;
PLAYING := FALSE;
end;

Cont := Cont + Valor;
MediaPlayer1.Close;


if
(Cont > (ListBox1.Items.Count -1)) then
Cont := 0
else
if
(Cont < 0) then
Cont := (ListBox1.Items.Count -1) ;


MediaPlayer1.FileName := Lista.Strings[Cont];
ListBox1.Selected[Cont]:=true;
MediaPlayer1.Open;
MediaPlayer1.Play;

Timer1.Enabled := True;
PLAYING := True;


Note o uso de uma variável extra que estamos usando desde que a função foi declarada, na parte superior da Unit: a variável Valor. Através dela, passaremos um parâmetro para a função que deverá ser 1 se desejamos avançar uma música; -1 se quisermos retroceder uma música. Qualquer valor diferente de 1 e -1 será ignorado pela nossa procedure. É o que nos diz a primeira linha: if (Valor <> -1) and (Valor <> 1) then exit. Isso significa que se Valor receber algo diferente de 1 ou -1, simplesmente "caia fora", ou seja, não execute o resto da função.

O próximo bloco de código é responsável por interromper a execução da faixa sempre que quisermos avançar ou voltar em nossa playlist. Por isso, precisamos parar o MediaPlayer (usando o método Stop), parar o Timer (deixando Enabled em False) e dizer que, naquele instante, a música que estava sendo reproduzida parou, através de PLAYING:=False.

Depois disso, nossa variável Cont deverá "pular" de faixa, adicionando o conteúdo da variável Valor, para saber se ela deve buscar a próxima faixa da Lista ou a anterior. É nesse ponto que nosso programa usará o 1 ou -1, pois um destes valores estará armazenado em Valor. Além disso, o MediaPlayer deve ser fechado com o método Close.

Agora, um detalhe importante, para o qual o internauta Flávio Bernardes, que leu este artigo, me chamou a atenção: note que se já estivermos executando a última faixa da lista e a procedure receber, novamente, o valor 1, para tentar ir para uma faixa posterior,  o nosso aplicativo irá disparar um erro, já que não há mais faixas. O mesmo acontecerá se, estando na primeira faixa, nossa procedure receber de novo o valor -1, como que tentando voltar para uma faixa anterior que não existe.

Para resolver este problema, precisamos fazer o seguinte: se estivermos no final da lista (onde a quantidade total de faixas será ListBox1.Items.Count-1), deveremos voltar para a primeira faixa (e Cont será zero de novo, pois a primeira música está na posição zero). Do mesmo modo, se estivermos na primeira (onde Cont já é zero) e tentarmos voltar para uma anterior, devemos, então, executar a última da lista, e Cont deverá ser atribuído de ListBox1.Items.Count-1. Observe:

if (Cont > (ListBox1.Items.Count -1)) then
Cont := 0

Portanto, para quando Cont ultrapassar o valor final da lista, Cont deverá receber zero de novo, para tocar a primeira faixa. Mas, se Cont receber um número menor que zero, então não há faixas antes da atual e, portanto, deverá ir para a última da lista:

if (Cont < 0) then
Cont := (ListBox1.Items.Count -1) ;
 

Feito isso, um novo arquivo de áudio deve ser atribuído ao MediaPlayer, através de sua propriedade FileName, usando o valor atual do contador Cont, que pegará a faixa correta da lista de Strings. É o que acontece na linha  MediaPlayer1.FileName := Lista.Strings[Cont]. Mais uma vez, precisamos mostrar qual é, então, a nova música que está selecionada na ListBox, através da linha  ListBox1.Selected[Cont]:=true. Por fim, abre-se o player com a função Open e inicia-se a reprodução novamente, com a função Play. Neste momento, o Timer é novamente habilitado e PLAYING passa, outra vez, para True.

Com isso, já podemos codificar os botões Avançar e Voltar. No primeiro, coloque o seguinte no seu evento OnClick:

 MudaFaixa(1);

No segundo, coloque isto no evento OnClick:

 MudaFaixa(-1);

Isso faz com que a função MudaFaixa() receba o valor 1 para avançar, sabendo que deverá ir para a faixa seguinte, e -1 para retroceder, sabendo que deverá voltar uma faixa.
 

O Timer do Aplicativo

Vamos codificar o Timer agora. Já habilitamos e desabilitamos o Timer diversas vezes em nosso código, mas o que ele fará realmente? Eis o que deve ser digitado no evento OnTimer do componente Timer1:

 if MediaPlayer1.Position = MediaPlayer1.Length then begin
  with MediaPlayer1 do begin
   Cont := Cont + 1;
   if Cont > Lista.Count-1 then
    Cont := 0;
  Close ;
   if FileExists(Lista.Strings[Cont]) then begin
     FileName := Lista.Strings[Cont];
     Open ;
     Play;
   end
 else
    Cont := Cont + 1;
  end;
//with MediaPlayer
 end;
//if MediaPlayer

ListBox1.Selected[Cont] := True ;


A primeira linha verifica o andamento da música do MediaPlayer; se a posição da faixa (Position) for igual a sua largura (Length), então significa que aquela faixa chegou ao final e o player deve  mudar para a seguinte. Ele faz isso somando 1 ao valor do contador Cont, fechando o MediaPlayer com Close e obtendo o novo nome de arquivo de áudio a executar. Abre-se, então, o novo arquivo (com Open) e inicia-se sua reprodução com o método Play. Note que, aqui, antes de iniciar a nova faixa, é recomendável verificar se o arquivo contido na playlist ainda existe no disco do computador com a função FileExists(), conforme mostrado na linha if FileExists(Lista.Strings[Cont]) then... Do contrário, pode-se tentar executar um MP3 que nem sequer está mais no HD e aí teremos um erro na tela. A função FileExists() retorna True se o arquivo existir e False se não existir. Caso o arquivo em questão não mais exista, o if pula para o último else do bloco e adiciona mais 1 ao Cont, para tentar a faixa subseqüente - até que uma faixa presente no disco seja encontrada. Note também outro teste com if que foi realizado: nas linhas if Cont > Lista.Count-1 then Cont := 0 estamos orientando nosso programa a buscar novamente a primeira música da playlist (Cont := 0) caso o valor de Cont ultrapasse a quantidade de músicas existentes na listagem (isto é, quando Cont > Lista.Count-1, o que significa que não há próxima música, mas que deve-se retornar ao começo).

Ao mesmo tempo, após iniciar a música, pedimos que a ListBox mostrasse qual a faixa atualmente em execução, selecionando-a com ListBox1.Selected[Cont] := True.

Observe também uma maneira diferente de nos referirmos ao MediaPlayer: logo no início do código, temos with MediaPlayer1 do begin. Isso não é de todo necessário, mas serve para tornar mais rápida a digitação. Sem a palavra reservada with, toda vez que nos referíssemos a um método ou propriedade do MediaPlayer, teríamos que digitar o nome do componente:

     MediaPlayer1.FileName := Lista.Strings[Cont];
     MediaPlayer1.Open ;
     MediaPlayer1.Play;

Englobando esta mesma porção de código em um with, basta chamar seus métodos e propriedades que o Delphi já sabe a qual componente estamos nos referindo:

  with MediaPlayer1 do begin
   ...
     FileName := Lista.Strings[Cont];
     Open ;
     Play;
   ...
  end; //with MediaPlayer

Observe quer todo With deve terminar com um end (note o end que delimita seu final na linha terminada com end;//with MediaPlayer).


Ajustando o menu Popup

Devemos, neste momento, fazer uma pequena modificação num código já implementado: o do menu Popup Remover faixa. Volte ao seu evento OnClick e adicione ao código existente a linha em destaque abaixo:

if application.messagebox(pchar('Remover faixa '+ ListBox1.Items.strings[ListBox1.ItemIndex] + '?'), pchar(Caption),36) = IDYES then begin
 BtnStopClick(self);
 Lista.Delete(ListBox1.ItemIndex);
 ListBox1.DeleteSelected;
 
if Cont > 0 then
  Cont := Cont - 1;
end;

A linha que adicionamos faz uma chamada estratégica ao botão Stop. Como ainda não havíamos codificado esse botão ao escrever o código deste menu Popup, deixamos temporariamente esta linha de lado. Mas agora ela deve ser usada - e por um motivo muito simples: se a faixa que for excluída coincidir de ser justamente aquela que estiver em execução naquele momento, o player irá se perder ao tentar continuar executando-a. Por isso, o melhor a fazer é pará-lo (chamando o próprio evento OnClick do botão BtnStop) para que o contador possa fazer os ajustes necessários e a listagem possa ser ajustada à nova quantidade de músicas (afinal, excluir uma faixa altera a quantidade de músicas presentes na lista).
 

Controlando o Volume do Som

Vamos ver como podemos aumentar ou diminuir o som dos alto-falantes. Para isso, adicione a Unit MMSystem à cláusula uses do seu aplicativo (observe o detalhe em vermelho na figura ao lado).

Depois, entre as sessões type e private, digite o seguinte:

    procedure TestaVolume;

 

Pressione CTRL+SHIFT+C para criar o corpo dessa nova procedure e, nela, implemente o seguinte código:

procedure TForm1.TestaVolume;
 var
  WaveCaps : TWaveOutCaps;
  Volume : DWord;
begin
 
  if waveOutGetDevCaps(WAVE_MAPPER, @WaveCaps, sizeof(TWaveOutCaps)) = MMSYSERR_NOERROR then
  if (WaveCaps.dwSupport and WAVECAPS_VOLUME) <> 0 then begin
 
  WaveOutGetVolume(Integer(WAVE_MAPPER), @Volume);

  with TrackBar1 do
   Position := 100 - Trunc(LoWord(Volume) / $FFFF * 100);
  end;
end;

Aqui, temos uma variável do tipo TWaveOutCaps e outra do tipo DWord. TWaveOutCaps é um objeto que armazena dados do hardware de áudio presente no sistema.

Para descobrir se teremos condição de modificar em tempo real o volume dos auto-falantes, usamos a função WaveOutGetDeviceCaps( ) que armazenará as capacidades de áudio do sistema na variável WaveCaps. Se a função retornar MMSYSERR_NOERROR significa que o hardware de áudio está presente e não apresentou nenhum erro. Aí, testamos a propriedade dwSupport  de TWaveOutCaps com WAVECAPS_VOLUME. Quando testadas juntas, dessa maneira:

 if (WaveCaps.dwSupport and WAVECAPS_VOLUME)...

o resultado obtido será diferente de zero (<> 0) se houver suporte para alteração de volume, e igual a zero caso contrário. Portanto, se for diferente de zero, devemos obter qual a "posição" deste volume com a função WaveOutGetVolume. Esta função obtém um "id" (identificador) para o dispositivo de áudio através do parâmetro WAVE_MAPPER (que deve ser convertido para inteiro com Integer( ) para poder ser usado) e um "ponteiro" para a variável Volume (@Volume). O Windows trabalha muito com "ponteiros", referenciados no Delphi por "arroba" (@).

Uma vez que os dados do volume estão armazenados na variável Volume, precisamos "quebrar" esses dados em partes para poder atribuir o valor correto à propriedade Position da TrackBar. A variável Volume, do tipo DWord, é um cardinal (um tipo de Inteiro) que armazena valores entre 0 e 65.535. O Windows costuma unir em um só número várias informações. Por exemplo, a variável Volume contém, na verdade, informações sobre os dois canais de áudio - o da esquerda e o da direita. Mas precisamos de apenas um para determinar o volume atual - por essa razão, quebra-se a informação com LoWord(), para obtermos apenas uma parte dela. Depois, divide-se o valor obtido por $FFFF (que é 65.535 em hexadecimal). A função Trunc() serve apenas para "truncar" o resultado da divisão, garantindo que tenhamos um número inteiro como resposta. Ao final, a linha Position := 100 - Trunc(LoWord(Volume) / $FFFF * 100) atribui, em percentuais, a posição correta do volume do sistema naquele instante.

Agora, precisamos criar a função que nos permitirá modificar o volume. Para isso, entre as sessões type e private, digite o seguinte:

    procedure MudaVolume;


Pressione CTRL+SHIFT+C e complete o corpo da procedure conforme segue:

procedure TForm1.MudaVolume;
 var
  WaveCaps : TWaveOutCaps;
  VolDir, VolEsq : Word;
begin
 
  VolEsq := Trunc((TrackBar1.Position-100) / 100 * $FFFF);
  VolDir := Trunc((TrackBar1.Position-100) / 100 * $FFFF);
 
  if waveOutGetDevCaps(WAVE_MAPPER, @WaveCaps, sizeof(TWaveOutCaps)) = MMSYSERR_NOERROR then
   if (WaveCaps.dwSupport and WAVECAPS_VOLUME) <> 0 then
     WaveOutSetVolume(Integer(WAVE_MAPPER), MakeLong(VolEsq,VolDir));

end;


Aqui, as duas primeiras linhas são simples: apenas capturam a posição da TrackBar e atribuem, em percentuais, ao volume dos alto-falantes esquerdo e direito. Depois, testa-se novamente se é possível alterar o volume usando WaveOutGetDevCaps() e, em caso positivo, ajusta o novo volume com a função WaveOutSetVolume(). Esta função também usa o parâmetro WAVE_MAPPER, que obtém um id para o dispositivo de áudio e, depois, "une" os dois canais obtidos em um único número inteiro longo (Longint), através da função MakeLong().
 

Últimas Modificações

Uma vez criadas as funções de volume, devemos chamá-las nos locais corretos de nossa aplicação. A função que testa o suporte à modificação de volume deve ser chamada logo no evento OnCreate do formulário:

TestaVolume;


A função que altera o volume deve estar no evento OnChange da TrackBar:

MudaVolume ;

Para incrementar o software, podemos ainda colocar um componente Image (paleta Additional) no Panel2 para que possamos exibir uma figura no local. O arquivo zipado traz três figuras JPG de sugestão, mas você pode modificar a seu critério. Ajuste o tamanho do componente Image para que fique quase igual às dimensões do Panel2. Depois, ajuste as propriedades Anchors do Image, deixando todos os itens em True. Por fim, altere a propriedade Stretch para True também. A propriedade Stretch, quando configurada dessa maneira,  faz com que a figura escolhida ajuste-se automaticamente ao tamanho do componente.

Para que o componente Image funcione a contento, ainda devemos acrescentar a Unit JPEG na cláusulas Uses. Somente assim o componente estará apto a receber também figuras no formato JPG ou JPEG.
 

 

Melhorando o Programa

Podemos ainda acrescentar algumas outras funcionalidades ao programa. Por exemplo, que tal uma barra de progressão que mostre o andamento da faixa atual? Podemos usar o Timer que já temos no nosso form e adicionar uma ProgressBar, da paleta Win32. Posicione-a abaixo do controle de volume. Altere sua propriedade Smooth para True e ajuste sua propriedade Anchors para que akLeft, akRight e akBottom fiquem configuradas como True. No evento OnTimer do Timer1, faça as modificações marcadas em destaque:

 if MediaPlayer1.Position = MediaPlayer1.Length then begin
  with MediaPlayer1 do begin
 Cont := Cont + 1;
 if Cont > Lista.Count-1 then
  Cont := 0;
 Close ;
 if FileExists(Lista.Strings[Cont]) then begin
  TimeFormat := tfHMS;
  FileName := Lista.Strings[Cont];
  Open ;
  Play;
 end
  else
    Cont := Cont + 1;
   end;
//with MediaPlayer
  end;
//if MediaPlayer

 if PLAYING then begin
  ProgressBar1.Max := MediaPlayer1.Length;
  ProgressBar1.Position := MediaPlayer1.Position;
end;


A linha TimeFormat := tfHMS apenas configura  o formato de tempo adotado pelo MediaPlayer. Aqui, estamos instruindo-o para que adote o padrão, que é Hora-Minuto-Segundo. Ao final do código, se houver música em execução (isto é, quando PLAYING = true) , então a barra de progressão (ProgressBar1) deverá ter sua posição máxima ajustada ao tamanho (Length) da faixa e sua posição atual igual à posição da música. Assim, sempre que a faixa do MediaPlayer estiver em "andamento", a posição (Position) da ProgressBar deverá acompanhá-lo.

 


Última modificação em 16/01/11