Soyez le premier à donner votre avis sur cette source.
Vue 4 301 fois - Téléchargée 588 fois
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;
24 juin 2010 à 21:30
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
24 juin 2010 à 11:35
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;
@++
23 juin 2010 à 21:59
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
23 juin 2010 à 06:08
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.