Générer un son à la volée

Soyez le premier à donner votre avis sur cette source.

Vue 4 301 fois - Téléchargée 588 fois

Description

J'ai adapté différents bouts de codes trouvés ça et là sur la toile afin de pondre un petit programme qui génère un simple LA à 440 Hz. C'est la note de référence du diapason. Mon but était aussi de comprendre comment générer différentes formes d'ondes de bases, à la volée, sans utiliser aucun fichier de data préformaté. Le programme peut donc jouer les 4 types de courbes classiques : sinusoïde, carré, triangle et dent de scie. Le tout réalisé sous Turbo Delphi 2006 Explorer Edition sans aucun composant additionnel et aucune DLL externe. Le code doit pouvoir être compilé sous la plupart des versions de Delphi étant donné que je n'utilise que des bibliothèques standards.

Source / Exemple :


procedure TFRMMain.MakeSound(Waveform, Frequency, Duration: Integer; Volume: Integer);
var
  WaveFormatEx: TWaveFormatEx;
  MS: TMemoryStream;
  i, TempInt, DataCount, RiffCount: integer;
  SoundValue: byte;
  t: double;
const
  Mono: Word = $0001;
  SampleRate: Integer = 44100;
  RiffId: string = 'RIFF';
  WaveId: string = 'WAVE';
  FmtId: string = 'fmt ';
  DataId: string = 'data';
begin
  with WaveFormatEx do
  begin
    wFormatTag := WAVE_FORMAT_PCM;
    nChannels := Mono;
    nSamplesPerSec := SampleRate;
    wBitsPerSample := $0008;
    nBlockAlign := (nChannels * wBitsPerSample) div 8;
    nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
    cbSize := 0;
  end;

  MS := TMemoryStream.Create;

  with MS do
  begin
    DataCount := (Duration * SampleRate) div 1000;
    RiffCount := Length(WaveId) + Length(FmtId) + SizeOf(DWORD) +
    SizeOf(TWaveFormatEx) + Length(DataId) + SizeOf(DWORD) + DataCount;

    Write(RiffId[1], 4);
    Write(RiffCount, SizeOf(DWORD));
    Write(WaveId[1], Length(WaveId));
    Write(FmtId[1], Length(FmtId));

    TempInt := SizeOf(TWaveFormatEx);

    Write(TempInt, SizeOf(DWORD));
    Write(WaveFormatEx, SizeOf(TWaveFormatEx));
    Write(DataId[1], Length(DataId));
    Write(DataCount, SizeOf(DWORD));

    for i := 0 to DataCount - 1 do
    begin
      t := (i * Frequency / SampleRate);

      //sinusoide
      if (Waveform = 1) then SoundValue := 127 + trunc(Volume * sin(2 * pi * t));
      //carre
      if (Waveform = 2) then SoundValue := 127 + trunc(Volume * sign(sin(2 * pi * t)));
      //triangle
      if (Waveform = 3)  then SoundValue := 127 + trunc(Volume * (2 * abs(2 * t - 2 * floor(t) - 1 ) - 1));
      //dent de scie
      if (Waveform = 4)  then SoundValue := 127 + trunc(Volume * (2 * (t - floor(t + 0.5))));

      Write(SoundValue, SizeOf(Byte));
    end;

    sndPlaySound(MS.Memory, SND_MEMORY or SND_SYNC);
    MS.Free;
  end;
end;

Conclusion :


J'espère que ce modeste code source, aussi minimaliste soit-il, pourra servir de base à d'autres qu'à moi-même pour la réalisation de programmes sonores un peu plus évolués...

Codes Sources

A voir également

Ajouter un commentaire Commentaires
sokoban Messages postés 32 Date d'inscription mardi 4 novembre 2003 Statut Membre Dernière intervention 4 novembre 2006
24 juin 2010 à 21:30
Salut à tous !

Merci pour vos commentaires. Cela est très motivant, même si je manque cruellement de temps pour prendre en compte ces remarques.

A bientôt,
Sokoban
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
24 juin 2010 à 11:35
Salut,

tout d'abords merci d'avoir réagit aussi vite et favorablement à la demande de Bacterius au sujet du Zip.
C'est vrai que c'est plus pratique ...

suggestions:

ton code ne fonctionne plus à partir de Delphi 2009 à cause de l'unicode ... la solution est toute simple il suffit de remplacer tes constantes "string" en "AnsiString" et le code fonctionnera dans toutes les versions.
const
Mono: Word = $0001;
SampleRate: Integer = 44100;
RiffId: AnsiString = 'RIFF';
WaveId: AnsiString = 'WAVE';
FmtId: AnsiString = 'fmt ';
DataId: AnsiString = 'data';

Quand on crée un composant que l'on libère dans la même procédure "MS: TMemoryStream", il est vivement conseillé d'utiliser un block "try finally end;" afin d'encadrer son utilisation mais surtout de garantir sa libération quoi qu'il arrive au cour du traitement.

et dans ton programme on pourrait même se passer de la déclaration de variable "MS: TMemoryStream":

//MS := TMemoryStream.Create;

with TMemoryStream.Create do
try
DataCount := (Duration * SampleRate) div 1000;
RiffCount := Length(WaveId) + Length(FmtId) + SizeOf(DWORD) +
SizeOf(TWaveFormatEx) + Length(DataId) + SizeOf(DWORD) + DataCount;

Write(RiffId[1], 4);
Write(RiffCount, SizeOf(DWORD));
Write(WaveId[1], Length(WaveId));
Write(FmtId[1], Length(FmtId));

TempInt := SizeOf(TWaveFormatEx);

Write(TempInt, SizeOf(DWORD));
Write(WaveFormatEx, SizeOf(TWaveFormatEx));
Write(DataId[1], Length(DataId));
Write(DataCount, SizeOf(DWORD));

for i := 0 to DataCount - 1 do
begin
t := (i * Frequency / SampleRate);

//sinusoide
if (Waveform = 1) then SoundValue := 127 + trunc(Volume * sin(2 * pi * t));
//carre
if (Waveform = 2) then SoundValue := 127 + trunc(Volume * sign(sin(2 * pi * t)));
//triangle
if (Waveform = 3) then SoundValue := 127 + trunc(Volume * (2 * abs(2 * t - 2 * floor(t) - 1 ) - 1));
//dent de scie
if (Waveform = 4) then SoundValue := 127 + trunc(Volume * (2 * (t - floor(t + 0.5))));

Write(SoundValue, SizeOf(Byte));
end;

sndPlaySound(Memory, SND_MEMORY or SND_SYNC);
finally
Free;
end;

@++
sokoban Messages postés 32 Date d'inscription mardi 4 novembre 2003 Statut Membre Dernière intervention 4 novembre 2006
23 juin 2010 à 21:59
Salut Bacterius !

Merci pour tes remarques.

Dans la partie code source, la fonction MakeSound a été améliorée. Un paramètre Waveform a été ajouté, qui permet de ne plus être dépendant des composants visuels. Par contre, pas eu le temps de mettre la fonction dans une unité à part, ni de rendre le son asynchrone. Ce sera pour plus tard !

A bientôt,
Sokoban
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
23 juin 2010 à 06:08
Salut Sokoban,
intéressant, j'ai deux-trois remarques :
- pourrais-tu poster un zip contenant la source ? Car sinon, pour compiler, nous on doit créer un nouveau projet, renommer plein de trucs et déposer les composants nous-mêmes sans même connaître le libellé des radiobutton par exemple. Sinon, ça marche bien sous Vista en tout cas !
- l'application freeze pendant le son, et c'est bien normal, pour contourner ça je te conseille d'appeller la fonction qui produit le son depuis un thread séparé du thread principal, ou alors appeller PlaySound avec le flag SND_ASYNC qui permettra à l'application de continuer à fonctionner pendant que le son tourne :)
- la prochaine étape pourrait d'être de créer une petite unité permettant d'appeller la fonction MakeSound dans n'importe-quel projet, en passant à la fonction les caractéristiques du son à émettre (si c'est une onde carrée, sinuosidale ou triangle par exemple), dans une unité "Waves.pas", qu'on aurait plus qu'à ajouter aux projets facilement !

Bonne source pour comprendre un peu mieux comment fonctionnent les flux de données, avec une application intéressante aux formes sinuosidales (format WAV).

Je ne note pas encore, j'attends d'avoir le zip pour pouvoir télécharger la source telle qu'elle doit être exécutée et pouvoir la garder dans mon dossier favoris !

Cordialement, Bacterius !

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.