PDA

View Full Version : tone generation in .WAV format using Pascal



paul_nicholls
06-06-2008, 01:13 AM
Hi guys, I don't know if this site is of any use to you http://juhara.com/article-7-tone-generator-with-delphi.html but it shows how to generate tones as .WAV formatted data.

I reckon it could easily be adapted to do other outputs like tones with custom ADSR (Attack Decay Sustain Release) envelopes for synthesizing notes from different musical instrument types (just an idea).

cheers,
Paul

arthurprs
06-06-2008, 01:28 AM
so this is how we make a synth :?

cool 8)

i will try some things, maybe i can replace some .wav effects generating then at program start :D

chronozphere
06-06-2008, 06:55 AM
I played with that some time ago. But i think it's quite hard to generate any "usable" sounds yourself. If you want to achieve certain effect's you're better off using existing software. Generally you will only be able to generate simple "runescape-ish" sounds. :)

Nevertheless, it's alway's nice to play with it. You sure learn alot by doing that.

arthurprs
06-06-2008, 04:19 PM
I played with that some time ago. But i think it's quite hard to generate any "usable" sounds yourself. If you want to achieve certain effect's you're better off using existing software. Generally you will only be able to generate simple "runescape-ish" sounds. :)

Nevertheless, it's alway's nice to play with it. You sure learn alot by doing that.

i read a litle about synths today, i will try write some kind of oscilator today, and with luch , using multiple oscilators, generate some cool sounds :P

User137
07-06-2008, 08:42 AM
Maybe OpenAL fits well in this kind of testing? Doesn't it take wave data same way as OpenGL takes textures, so would be ideal for realtime wave changing.

chronozphere
07-06-2008, 11:09 AM
I have experience with OpenAL, and i totally agree with you. You could easily generate WAVE data on-the-fly and feed it to OpenAL. It might be even easier than saving it to disk/mem with a header and play it using WinAPI routines. :)

arthurprs
07-06-2008, 03:52 PM
at the moment im using directsound, i will try openal later for other plataforms compatibility

progress -> 10% :(

arthurprs
09-06-2008, 09:45 PM
someone know others sources of info about programming synths ?

everything i try seach on google i receive results about paid products or other stuff.

paul_nicholls
10-06-2008, 02:22 AM
Perhaps this site could help (not pascal, but hey)

http://www.codeproject.com/KB/audio-video/synthtoolkitparti.aspx

downloads require a free registration, but that shouldn't be an issue - I haven't had any problems (spam, etc).
cheers,
Paul

paul_nicholls
10-06-2008, 06:21 AM
Oh, if it helps I have whipped up an array of note frequencies and some constants for note names (use as indices into the array).

<note>s = sharp
<note>b = flat
other = normal

I calculated them from this site http://www.techlib.com/reference/musical_note_frequencies.htm


cNote_C = 0;
cNote_Cs = 1; cNote_Db = 1;
cNote_D = 2;
cNote_Ds = 3; cNote_Eb = 3;
cNote_E = 4;
cNote_F = 5;
cNote_Fs = 6; cNote_Gb = 6;
cNote_G = 7;
cNote_Gs = 8; cNote_Ab = 8;
cNote_A = 9;
cNote_Es = 10; cNote_Bb = 10;
cNote_B = 11;
cNotes : Array[0..11,0..8] Of Single =
(
//octave
//0 1 2 3 4 5 6 7 8
{C } (16.351875 , 32.70375 , 65.4075 , 130.815 , 261.63 , 523.26 , 1046.52 , 2093.04 , 4186.08),
{C#/Db} (17.3242080860764, 34.6484161721528, 69.2968323443056, 138.593664688611, 277.187329377222, 554.374658754445, 1108.74931750889, 2217.49863501778, 4434.99727003556),
{D } (18.3543591061988, 36.7087182123977, 73.4174364247953, 146.834872849591, 293.669745699181, 587.339491398363, 1174.67898279672, 2349.35796559345, 4698.7159311869),
{D#/Eb} (19.4457660936351, 38.8915321872702, 77.7830643745405, 155.566128749081, 311.132257498162, 622.264514996324, 1244.52902999265, 2489.05805998529, 4978.11611997059),
{E } (20.6020715177497, 41.2041430354995, 82.4082860709989, 164.816572141998, 329.633144283996, 659.266288567991, 1318.53257713598, 2637.06515427197, 5274.13030854393),
{F } (21.8271344404066, 43.6542688808133, 87.3085377616265, 174.617075523253, 349.234151046506, 698.468302093012, 1396.93660418602, 2793.87320837205, 5587.7464167441),
{F#/Gb} (23.1250433952296, 46.2500867904591, 92.5001735809182, 185.000347161836, 370.000694323673, 740.001388647346, 1480.00277729469, 2960.00555458938, 5920.01110917876),
{G } (24.5001300327029, 49.0002600654058, 98.0005201308116, 196.001040261623, 392.002080523246, 784.004161046492, 1568.00832209299, 3136.01664418597, 6272.03328837194),
{G#/Ab} (25.9569835766525, 51.913967153305 , 103.82793430661 , 207.65586861322 , 415.31173722644 , 830.62347445288 , 1661.24694890576, 3322.49389781152, 6644.98779562304),
{A } (27.5004661403537, 55.0009322807073, 110.001864561415, 220.003729122829, 440.007458245659, 880.014916491317, 1760.02983298263, 3520.05966596527, 7040.11933193054),
{A#/Bb} (29.1357289533821, 58.2714579067643, 116.542915813528, 233.085831627057, 466.171663254114, 932.343326508228, 1864.68665301646, 3729.37330603291, 7458.74661206582),
{B } (30.8682295533639, 61.7364591067279, 123.472918213456, 246.945836426911, 493.891672853823, 987.783345707646, 1975.56669141529, 3951.13338283058, 7902.26676566117)
);



some of the frequencies (Hz) may be out slightly, but not by much so you would never notice.

cheers,
Paul

paul_nicholls
10-06-2008, 10:40 PM
I have updated the cNotes array as I am now using the A note in octave 4 as the origin for my calculations and now the values are exactly the same as in the original table I had.


cNote_C = 0;
cNote_Cs = 1; cNote_Db = 1;
cNote_D = 2;
cNote_Ds = 3; cNote_Eb = 3;
cNote_E = 4;
cNote_F = 5;
cNote_Fs = 6; cNote_Gb = 6;
cNote_G = 7;
cNote_Gs = 8; cNote_Ab = 8;
cNote_A = 9;
cNote_Es = 10; cNote_Bb = 10;
cNote_B = 11;
cNotes : Array[0..11,0..8] Of Single =
(
//Octave
//Note 0 1 2 3 4 5 6 7 8
{C} (16.3515978312874, 32.7031956625748, 65.4063913251497, 130.812782650299, 261.625565300599, 523.251130601197, 1046.50226120239, 2093.00452240479, 4186.00904480958),
{C#/Db} (17.3239144360545, 34.647828872109 , 69.295657744218 , 138.591315488436, 277.182630976872, 554.365261953744, 1108.73052390749, 2217.46104781498, 4434.92209562995),
{D} (18.354047994838 , 36.7080959896759, 73.4161919793519, 146.832383958704, 293.664767917408, 587.329535834815, 1174.65907166963, 2349.31814333926, 4698.63628667852),
{D#/Eb} (19.4454364826301, 38.8908729652601, 77.7817459305202, 155.56349186104 , 311.126983722081, 622.253967444162, 1244.50793488832, 2489.01586977665, 4978.03173955329),
{E} (20.6017223070544, 41.2034446141088, 82.4068892282175, 164.813778456435, 329.62755691287 , 659.25511382574 , 1318.51022765148, 2637.02045530296, 5274.04091060592),
{F} (21.8267644645628, 43.6535289291255, 87.307057858251 , 174.614115716502, 349.228231433004, 698.456462866008, 1396.91292573202, 2793.82585146403, 5587.65170292806),
{F#/Gb} (23.1246514194772, 46.2493028389543, 92.4986056779086, 184.997211355817, 369.994422711634, 739.988845423269, 1479.97769084654, 2959.95538169308, 5919.91076338615),
{G} (24.4997147488593, 48.9994294977187, 97.9988589954373, 195.997717990875, 391.995435981749, 783.990871963499, 1567.981743927 , 3135.96348785399, 6271.92697570799),
{G#/Ab} (25.9565435987466, 51.9130871974931, 103.826174394986, 207.652348789973, 415.304697579945, 830.60939515989 , 1661.21879031978, 3322.43758063956, 6644.87516127912),
{A} (27.5 , 55 , 110 , 220 , 440 , 880 , 1760 , 3520 , 7040 ),
{A#/Bb} (29.1352350948806, 58.2704701897613, 116.540940379522, 233.081880759045, 466.16376151809 , 932.32752303618 , 1864.65504607236, 3729.31009214472, 7458.62018428944),
{B} (30.8677063285078, 61.7354126570155, 123.470825314031, 246.941650628062, 493.883301256124, 987.766602512248, 1975.5332050245 , 3951.06641004899, 7902.13282009799)
);



cheers,
Paul

arthurprs
11-06-2008, 06:56 PM
thanks, i will study the sample of codeproject and try something later.

paul_nicholls
11-06-2008, 10:25 PM
I did notice that I had a typo:

cNote_Es = 10

in the constants should be

cNote_As = 10

cheers,
Paul

arthurprs
12-06-2008, 01:20 AM
paul, the source of codeproject don't include the main stuff part =( (synth)

did you know other place to get a sample?

paul_nicholls
12-06-2008, 05:09 AM
Not sure if it helps, but you can find at least one of the dependencies (MIDI Toolkit) here http://www.codeproject.com/KB/audio-video/MIDIToolkit.aspx

cheers,
Paul

VilleK
12-06-2008, 08:04 AM
I hope these links may be helpful:

My softsynth page with links to more information about synth programming: http://www.emix8.org/static.php?page=SoftSynth

And my open source project ZGameEditor (http://www.zgameeditor.org) features a synth with source code.

arthurprs
12-06-2008, 09:47 PM
Not sure if it helps, but you can find at least one of the dependencies (MIDI Toolkit) here http://www.codeproject.com/KB/audio-video/MIDIToolkit.aspx

cheers,
Paul

same as the another, the code that "matters" is not included :(



I hope these ]http://www.emix8.org/static.php?page=SoftSynth[/url]

And my open source project ZGameEditor (http://www.zgameeditor.org) features a synth with source code.
i liked what i heard from the demo, i will look at the source latter,
and some awesome links!!!

thanks for the help guys :)

paul_nicholls
18-06-2008, 12:58 AM
Using information off the net about Direct Digital Synthesis (http://www.analog.com/library/analogDialogue/archives/38-08/dds.html) as an example, I have created a Direct Digital Synthesizer class TDDS which can currently output square and sine waves (will upgrade to do triangle and sawtooth as well later).

It can also be used to point to a custom lookup table so you can produce your own arbitrary waveform at the desired frequency and sample rate on top of the 4 built in wave types.

You would do this using a custom TPhaseToAmplitude function to generate your own custom output.

I hope you guys find it useful :)


Unit direct_digital_synthensizer;
{$IFDEF fpc}
{$MODE DELPHI} {$H+}
{$ENDIF}
Interface

Type
{................................................. .............................}
TDDS = Class;
TPhaseToAmplitude = Function(Const ADDS : TDDS) : Single;
TNumberOfBits = 2..31;
TWaveType = (wtSawtooth,wtSquare,wtTriangle,wtSine,wtCustom);
TDefinedWaveType = Low(TWaveType)..Pred(High(TWaveType));
TDDS = Class
Private
FOnPhaseToAmplitude : TPhaseToAmplitude;
FPhaseRegResolution : TNumberOfBits;
FPhaseRegMaxPlus1 : LongWord;
FPhaseRegMax : LongWord;
FPhaseRegHalfMax : LongWord;
FPhaseRegQuaterMax : LongWord;
FWaveType : TWaveType;
FSampleRate : LongWord;
FFrequency : Single;
FPhase : LongWord;
FPhaseReg : LongWord;
FPhaseRegIncrement : LongWord;
FAmplitude : Single;
Procedure FSetOnPhaseToAmplitude(Const AValue : TPhaseToAmplitude);
Procedure SetPhaseRegIncrement;
Public
Constructor Create(Const APhaseRegResolution : TNumberOfBits);
Procedure SetWaveType (Const AValue : TDefinedWaveType);
Procedure SetSampleRate(Const AValue : LongWord);
Procedure SetFrequency (Const AValue : Single);
Procedure Reset;
Procedure Synthesize;
Property PhaseRegResolution : TNumberOfBits Read FPhaseRegResolution;
Property PhaseRegMaxPlus1 : LongWord Read FPhaseRegMaxPlus1;
Property PhaseRegMax : LongWord Read FPhaseRegMax;
Property PhaseRegHalfMax : LongWord Read FPhaseRegHalfMax;
Property PhaseRegQuaterMax : LongWord Read FPhaseRegQuaterMax;
Property WaveType : TWaveType Read FWaveType;
Property SampeRate : LongWord Read FSampleRate;
Property Frequency : Single Read FFrequency;
Property Phase : LongWord Read FPhase;
Property Amplitude : Single Read FAmplitude;
Property OnPhaseToAmplitude : TPhaseToAmplitude Read FOnPhaseToAmplitude
Write FSetOnPhaseToAmplitude;
End;
{................................................. .............................}

Implementation

{................................................. .............................}

{................................................. .............................}
Function PhaseToAmplitude_Sawtooth(Const ADDS : TDDS) : Single;
Begin
Result := 0;
End;
{................................................. .............................}

{................................................. .............................}
Function PhaseToAmplitude_Square(Const ADDS : TDDS) : Single;
Begin
If ADDS.Phase <= ADDS.PhaseRegHalfMax Then
Result := -1
Else
Result := +1;
End;
{................................................. .............................}

{................................................. .............................}
Function PhaseToAmplitude_Triangle(Const ADDS : TDDS) : Single;
Begin
Result := 0;
End;
{................................................. .............................}

{................................................. .............................}
Function PhaseToAmplitude_Sine(Const ADDS : TDDS) : Single;
Begin
Result := Sin(2 * PI * ADDS.Phase/ADDS.PhaseRegMaxPlus1);
End;
{................................................. .............................}

{................................................. .............................}
Constructor TDDS.Create(Const APhaseRegResolution : TNumberOfBits);
Begin
Inherited Create;
FOnPhaseToAmplitude := Nil;
FPhaseRegResolution := APhaseRegResolution;
FPhaseRegMaxPlus1 := LongWord((1 Shl FPhaseRegResolution));
FPhaseRegMax := LongWord((1 Shl FPhaseRegResolution) - 1);
FPhaseRegHalfMax := LongWord(FPhaseRegMaxPlus1 Shr 1);
FPhaseRegQuaterMax := LongWord(FPhaseRegMaxPlus1 Shr 2);
FSampleRate := 44100;
FFrequency := 100;
SetPhaseRegIncrement;
SetWaveType(wtSine);
Reset;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TDDS.FSetOnPhaseToAmplitude(Const AValue : TPhaseToAmplitude);
Begin
If Not Assigned(AValue) Then Exit;
FOnPhaseToAmplitude := AValue;
FWaveType := wtCustom;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TDDS.SetWaveType(Const AValue : TDefinedWaveType);
Begin
FWaveType := AValue;
Case FWaveType Of
wtSawtooth : FOnPhaseToAmplitude := PhaseToAmplitude_Sawtooth;
wtSquare : FOnPhaseToAmplitude := PhaseToAmplitude_Square;
wtTriangle : FOnPhaseToAmplitude := PhaseToAmplitude_Triangle;
wtSine : FOnPhaseToAmplitude := PhaseToAmplitude_Sine;
End;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TDDS.SetPhaseRegIncrement;
Begin
FPhaseRegIncrement := LongWord(Trunc(FPhaseRegMax * FFrequency / FSampleRate)) And FPhaseRegMax;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TDDS.SetSampleRate(Const AValue : LongWord);
Begin
FSampleRate := AValue;
SetPhaseRegIncrement;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TDDS.SetFrequency(Const AValue : Single);
Begin
FFrequency := AValue;
SetPhaseRegIncrement;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TDDS.Reset;
Begin
FPhase := 0;
FPhaseReg := 0;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TDDS.Synthesize;
Begin
FPhase := FPhaseReg;
FAmplitude := FOnPhaseToAmplitude(Self);
{$R-}
FPhaseReg := LongWord(FPhaseReg + FPhaseRegIncrement) And FPhaseRegMax;
{$R+}
End;
{................................................. .............................}

{................................................. .............................}
End.


You set up the desired parameters in the class, call the Reset method once, and each time you need a sample, just call the Synthesize method to generate a value you can then read using the Amplitude or Phase properties.

cheers,
Paul

paul_nicholls
20-08-2008, 06:26 AM
If it helps anyone, I have made a wav_buffer unit which allows you to create WAV format output to a stream like so:


Program WAV_buffer_test;
Uses
Classes,
wav_buffer;

Const
cDuration_mSec = 1000;
cNumberOfChannels = 1;
cSampleRate = 44100;
cBitsPerSample = 16;
Var
fs &#58; TFileStream;
Buffer &#58; TWAVBuffer;
i &#58; LongWord;
Sample &#58; Single;
Begin
fs &#58;= TFileStream.Create&#40;'c&#58;\test.wav',fmCreate&#41;;
Try
Buffer &#58;= TWAVBuffer.Create&#40;fs,cDuration_mSec,cNumberOfChann els,cBitsPerSample,cSampleRate&#41;;
For i &#58;= 0 To Buffer.NumberOfSamples - 1 Do
Begin
// 1 complete sinewave cycle over the entire duration
Sample &#58;= Sin&#40;2 * PI * i / Buffer.NumberOfSamples&#41;;
Buffer.WriteSamples&#40;&#91;Sample&#93;&#41;;
End;
Finally
fs.Free;
End;
End.

for 2 channels (stereo) just add another sample to the Buffer.WriteSamples line like so:


Buffer.WriteSamples&#40;&#91;SampleL,SampleR&#93;&#41;;

See unit below:


Unit wav_buffer;
&#123;$IFDEF fpc&#125;
&#123;$MODE DELPHI&#125; &#123;$H+&#125;
&#123;$ENDIF&#125;
Interface

Uses
Classes;

Type
&#123;................................................. .............................&#125;
TWAVBuffer = Class
Private
FStream &#58; TStream;
FDuration_mSec &#58; LongWord;
FNumberOfChannels &#58; Byte;
FBitsPerSample &#58; Byte;
FSampleRate &#58; LongWord;
FNumberOfSamples &#58; LongWord;
FDataSize &#58; LongWord;
Procedure WriteWAVHeader;
Public
Constructor Create&#40;Const AStream &#58; TStream;
Const ADuration_mSec &#58; LongWord;
Const ANumberOfChannels &#58; Byte;
Const ABitsPerSample &#58; Byte;
Const ASampleRate &#58; LongWord&#41;;
Procedure Reset;
Procedure WriteSamples&#40;Const ASamples &#58; Array Of Single&#41;;
Property NumberOfSamples &#58; LongWord Read FNumberOfSamples;
End;
&#123;................................................. .............................&#125;

Implementation

&#123;................................................. .............................&#125;

&#123;................................................. .............................&#125;
Constructor TWAVBuffer.Create&#40;Const AStream &#58; TStream;
Const ADuration_mSec &#58; LongWord;
Const ANumberOfChannels &#58; Byte;
Const ABitsPerSample &#58; Byte;
Const ASampleRate &#58; LongWord&#41;;
Begin
FStream &#58;= AStream;
FDuration_mSec &#58;= ADuration_mSec;
FNumberOfChannels &#58;= ANumberOfChannels;
FBitsPerSample &#58;= ABitsPerSample;
FSampleRate &#58;= ASampleRate;
If Not &#40;FNumberOfChannels In&#91;1,2&#93;&#41; Then FNumberOfChannels &#58;= 1;
If Not &#40;FBitsPerSample In&#91;8,16&#93;&#41; Then FBitsPerSample &#58;= 8;
FNumberOfSamples &#58;= &#40;FDuration_mSec * FSampleRate&#41; Div 1000;
FDataSize &#58;= &#40;FBitsPerSample Shr 3&#41; * FNumberOfChannels * FNumberOfSamples;
WriteWAVHeader;
End;
&#123;................................................. .............................&#125;

&#123;................................................. .............................&#125;
Procedure TWAVBuffer.WriteWAVHeader;
Const
WAVE_FORMAT_PCM = 1;
RiffId &#58; AnsiString = 'RIFF';
WaveId &#58; AnsiString = 'WAVE';
FmtId &#58; AnsiString = 'fmt ';
DataId &#58; AnsiString = 'data';

Type
TWaveHeader = Packed Record
wFormatTag &#58; Word; &#123; format type &#125;
nChannels &#58; Word; &#123; number of channels &#40;i.e. mono, stereo, etc.&#41; &#125;
nSamplesPerSec &#58; LongWord; &#123; sample rate &#125;
nAvgBytesPerSec &#58; LongWord; &#123; for buffer estimation &#125;
nBlockAlign &#58; Word; &#123; block size of data &#125;
wBitsPerSample &#58; Word; &#123; number of bits per sample of mono data &#125;
cbSize &#58; Word; &#123; the count in bytes of the size of &#125;
End;
Var
WaveHeader &#58; TWaveHeader;
RiffCount &#58; Integer;
TempInt &#58; LongWord;
Begin
With WaveHeader Do
Begin
wFormatTag &#58;= WAVE_FORMAT_PCM;
nChannels &#58;= FNumberOfChannels;
nSamplesPerSec &#58;= FSampleRate;
wBitsPerSample &#58;= FBitsPerSample;
nBlockAlign &#58;= nChannels * wBitsPerSample Shr 3;
nAvgBytesPerSec &#58;= nSamplesPerSec * nBlockAlign;
cbSize &#58;= 0;
End;
&#123;Calculate length of sound data and of file data&#125;
RiffCount &#58;= Length&#40;WaveId&#41; + Length&#40;FmtId&#41; + SizeOf&#40;LongWord&#41; +
SizeOf&#40;TWaveHeader&#41; + Length&#40;DataId&#41; + SizeOf&#40;LongWord&#41; + FDataSize; // file data
&#123;write out the wave header&#125;
FStream.Write&#40;RiffId&#91;1&#93; , 4&#41;; // 'RIFF'
FStream.Write&#40;RiffCount , SizeOf&#40;LongWord&#41;&#41;; // file data size
FStream.Write&#40;WaveId&#91;1&#93; , Length&#40;WaveId&#41;&#41;; // 'WAVE'
FStream.Write&#40;FmtId&#91;1&#93; , Length&#40;FmtId&#41;&#41;; // 'fmt '
TempInt &#58;= SizeOf&#40;TWaveHeader&#41;;
FStream.Write&#40;TempInt , SizeOf&#40;LongWord&#41;&#41;; // TWaveFormat data size
FStream.Write&#40;WaveHeader , SizeOf&#40;WaveHeader&#41;&#41;; // WaveFormatEx record
FStream.Write&#40;DataId&#91;1&#93; , Length&#40;DataId&#41;&#41;; // 'data'
FStream.Write&#40;FDataSize , SizeOf&#40;LongWord&#41;&#41;; // sound data size
End;
&#123;................................................. .............................&#125;

&#123;................................................. .............................&#125;
Procedure TWAVBuffer.Reset;
Begin
FStream.Seek&#40;0,soFromBeginning&#41;;
WriteWAVHeader;
End;
&#123;................................................. .............................&#125;

&#123;................................................. .............................&#125;
Procedure TWAVBuffer.WriteSamples&#40;Const ASamples &#58; Array Of Single&#41;;
Var
Sample_8Bit &#58; Byte;
Sample_16Bit &#58; SmallInt;
Sample &#58; Single;
i &#58; Integer;
Begin
For i &#58;= 0 To High&#40;ASamples&#41; Do
Begin
Sample &#58;= ASamples&#91;i&#93;;
// clip sample to between &#91;-1,+1&#93;
If Sample <1> +1.0 Then Sample &#58;= +1.0;
// write sample to stream
If FBitsPerSample = 8 Then
Begin
Sample_8Bit &#58;= 127 + Trunc&#40;127 * Sample&#41;;
FStream.Write&#40;Sample_8Bit,SizeOf&#40;Sample_8Bit&#41;&#41;;
End
Else
If FBitsPerSample = 16 Then
Begin
Sample_16Bit &#58;= Trunc&#40;32767 * Sample&#41;;
FStream.Write&#40;Sample_16Bit,SizeOf&#40;Sample_16Bit&#41;&#41;;
End;
End;
End;
&#123;................................................. .............................&#125;

&#123;................................................. .............................&#125;
End.

I hope someone finds this useful :-)
cheers,
Paul

chronozphere
20-08-2008, 01:38 PM
As far as i can tell, you do not need an array to store the tone frequencies. They can be calculated using some formula (You have to search wikipedia, I'm sure it will be there somewhere ;) )

paul_nicholls
20-08-2008, 10:46 PM
As far as i can tell, you do not need an array to store the tone frequencies. They can be calculated using some formula (You have to search wikipedia, I'm sure it will be there somewhere ;) )

Sorry, I'm not sure what you are talking about.

I'm not storing tone frequencies in any array...

EDIT: Unless you mean my TDDS class in one of my earlier posts?

That is using an array to store tones that are calculated only once so it is faster, and also I can use custom waveforms that are much more complex than the simple Sine, Square, Triangle, Sawtooth waveforms.

I have got an updated version of that now too if you are interested, as well as a TWavetable class which holds TWaveform classes (individual custom waveforms; as simple or complex as you want).

cheers,
Paul

chronozphere
21-08-2008, 06:05 AM
That is using an array to store tones that are calculated only once so it is faster, and also I can use custom waveforms that are much more complex than the simple Sine, Square, Triangle, Sawtooth waveforms.


Okay.. That makes sense. :)

Edit: You might want to pre-calculate this data instead of copying it from anothe resource. Then you will be able to generate a bigger array (more octaves) and you will have more precise data. Check out the "Tone frequencie" section on this wiki-page:

http://en.wikipedia.org/wiki/Note

This looks very cool.. Audio has always been like a second hobby of mine, and i often tried to find audio-programming resources. Thanks for sharing these sources. :)

paul_nicholls
21-08-2008, 06:57 AM
Hi chronozphere,

I'm glad I could help :-)

BTW, when you asked about me storing tones in arrays, did you mean the note frequencies themselves?

If so, then yes I am doing that...I have an array I filled with my calculated versions of the notes that I can then lookup using constants.

I have 12 notes (0 - 11) in each of 9 (0 - 8) octaves...

I used the note formula mentioned in a similar page to the one you posted.

I now have a TOscillator class (uses the TDDS class internally) for producing sine, cosine, triangle, sawtooth, square, and noise waveforms at the desired frequency. Also can use the TWaveform class I told you abotu for custom waveforms.

I also have a TEnvelope class to shape the output volume of the oscillator, and a TMixer class that can mix multiple outputs into one output.

Let me know if you are interested in these too :)

cheers,
Paul

paul_nicholls
02-03-2009, 04:32 AM
Hi all,
I'm re-posting my TWAVBuffer class code as the previous time I posted it, the output code was garbled, and I also want to test the new formatter :D


Unit wav_buffer;
{$IFDEF fpc}
{$MODE DELPHI}
{$ENDIF}
{$H+}
Interface

Uses
Classes;

Type
{................................................. .............................}
TWAVBuffer = Class
Private
FStream : TStream;
FDuration_mSec : LongWord;
FNumberOfChannels : Byte;
FBitsPerSample : Byte;
FSampleRate : LongWord;
FNumberOfSamples : LongWord;
FDataSize : LongWord;
Procedure WriteWAVHeader;
Public
Constructor Create(Const AStream : TStream;
Const ADuration_mSec : LongWord;
Const ANumberOfChannels : Byte;
Const ABitsPerSample : Byte;
Const ASampleRate : LongWord);
Procedure Reset;
Procedure WriteSamples(Const ASamples : Array Of Single);
Property NumberOfSamples : LongWord Read FNumberOfSamples;
End;
{................................................. .............................}

Implementation

{................................................. .............................}

{................................................. .............................}
Constructor TWAVBuffer.Create(Const AStream : TStream;
Const ADuration_mSec : LongWord;
Const ANumberOfChannels : Byte;
Const ABitsPerSample : Byte;
Const ASampleRate : LongWord);
Begin
FStream := AStream;
FDuration_mSec := ADuration_mSec;
FNumberOfChannels := ANumberOfChannels;
FBitsPerSample := ABitsPerSample;
FSampleRate := ASampleRate;
If Not (FNumberOfChannels In[1,2]) Then FNumberOfChannels := 1;
If Not (FBitsPerSample In[8,16]) Then FBitsPerSample := 8;
FNumberOfSamples := (FDuration_mSec * FSampleRate) Div 1000;
FDataSize := (FBitsPerSample Shr 3) * FNumberOfChannels * FNumberOfSamples;
WriteWAVHeader;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TWAVBuffer.WriteWAVHeader;
Const
WAVE_FORMAT_PCM = 1;
RiffId : AnsiString = 'RIFF';
WaveId : AnsiString = 'WAVE';
FmtId : AnsiString = 'fmt ';
DataId : AnsiString = 'data';

Type
TWaveHeader = Packed Record
wFormatTag : Word; { format type }
nChannels : Word; { number of channels (i.e. mono, stereo, etc.) }
nSamplesPerSec : LongWord; { sample rate }
nAvgBytesPerSec : LongWord; { for buffer estimation }
nBlockAlign : Word; { block size of data }
wBitsPerSample : Word; { number of bits per sample of mono data }
cbSize : Word; { the count in bytes of the size of }
End;
Var
WaveHeader : TWaveHeader;
RiffCount : Integer;
TempInt : LongWord;
Begin
With WaveHeader Do
Begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := FNumberOfChannels;
nSamplesPerSec := FSampleRate;
wBitsPerSample := FBitsPerSample;
nBlockAlign := nChannels * wBitsPerSample Shr 3;
nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
cbSize := 0;
End;
{Calculate length of sound data and of file data}
RiffCount := Length(WaveId) + Length(FmtId) + SizeOf(LongWord) +
SizeOf(TWaveHeader) + Length(DataId) + SizeOf(LongWord) + FDataSize; // file data
{write out the wave header}
FStream.Write(RiffId[1] , 4); // 'RIFF'
FStream.Write(RiffCount , SizeOf(LongWord)); // file data size
FStream.Write(WaveId[1] , Length(WaveId)); // 'WAVE'
FStream.Write(FmtId[1] , Length(FmtId)); // 'fmt '
TempInt := SizeOf(TWaveHeader);
FStream.Write(TempInt , SizeOf(LongWord)); // TWaveFormat data size
FStream.Write(WaveHeader , SizeOf(WaveHeader)); // WaveFormatEx record
FStream.Write(DataId[1] , Length(DataId)); // 'data'
FStream.Write(FDataSize , SizeOf(LongWord)); // sound data size
End;
{................................................. .............................}

{................................................. .............................}
Procedure TWAVBuffer.Reset;
Begin
FStream.Seek(0,soFromBeginning);
WriteWAVHeader;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TWAVBuffer.WriteSamples(Const ASamples : Array Of Single);
Var
Sample_8Bit : Byte;
Sample_16Bit : SmallInt;
Sample : Single;
i : Integer;
Begin
For i := 0 To High(ASamples) Do
Begin
Sample := ASamples[i];
// clip sample to between [-1,+1]
If Sample < -1.0 Then Sample := -1.0
Else If Sample > +1.0 Then Sample := +1.0;
// write sample to stream
If FBitsPerSample = 8 Then
Begin
Sample_8Bit := 127 + Trunc(127 * Sample);
FStream.Write(Sample_8Bit,SizeOf(Sample_8Bit));
End
Else
If FBitsPerSample = 16 Then
Begin
Sample_16Bit := Trunc(32767 * Sample);
FStream.Write(Sample_16Bit,SizeOf(Sample_16Bit));
End;
End;
End;
{................................................. .............................}

{................................................. .............................}
End.


cheers,
Paul