Sie sind auf Seite 1von 11

11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

Theblogatthebottomofthesea
ProgrammingandGamedevStuff

DIYSynth2:CommonWaveForms
PostedonMay192012byDemofox

ThisisapartoftheDIYSynthesizerseriesofpostswhereeachpostisroughlybuiltupontheknowledgeofthe
previousposts.Ifyouarelost,checktheearlierposts!

Thisisthesecondchapterinaseriesoftutorialsaboutprogrammingyourownsynthesizer

Inthischapterwelltalkaboutoscillators,andsomecommonbasicwaveforms:Sine,Square,Saw,Triangleand
Noise.

Bytheend,youshouldhaveenoughknowledgetomakesomebasicelectronicmelodies.

Youcandownloadthefullsourceforthischapterhere:DIYSynthesizer:Chapter2SourceCode

TheSineWave
Thesinewaveisthebasisoflotsofthingsinaudiosynthesis.Itcanbeusedonitsowntomakesound,multiple
sinewavescanbecombinedtomakeothermorecomplexwaveforms(aswellseeinthenextchapter)anditsalso
thebasisofalotofDSPtheoryandaudioanalysis.Forinstance,thereissomethingcalledFourierAnalysiswhere
youcananalyzesomeaudiodataanditwilltellyouwhataudiofrequenciesareinthatsounddata,andhowstrong
eachis(usefulforadvancedsynthesisanddigitalsignalprocessingakaDSP).Themathofhowtogetthat
informationisbasedonsomesimplepropertiesofsinewaves.Moreinfocanbefoundhere:
http://en.wikipedia.org/wiki/Fourier_analysis.

Ifwewanttouseasinewaveinouraudiodata,thefirstproblemwehitisthatsinehasavaluefrom1to1,butour
audiodatafromthelastchapterisstoredina32bitint,whichhasarangeof2,147,483,648to2,147,483,647,and
isunabletostorefractionalnumbers.

Thesolutionistojustmap1to2,147,483,648,and1to2,147,483,647andallthenumbersinbetweenrepresent
fractionalnumbersbetween1and1.0.25forinstancewouldbecome536,870,911.

Ifinsteadof32bits,wewantedtostorethedatain16bits,or8bits,wecoulddothataswell.Aftergeneratingour
floatingpointaudiodata,wejustconvertitdifferentlytogettothose16bitsand8bits.16bitshavearangeof

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 1/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

32,768to32,767so0.25wouldconvertto8191.In8bits,wavefileswantUNSIGNED8bitnumbers,sotherange
is0to255.Inthatcase,0.25wouldbecome158.

Note,inthecodeforthischapter,imodifiedWriteWaveFiletodothisconversionforussogoingforwardwecan
workwithfloatingpointnumbersonlyandnotworryaboutbitspersampleuntilwewanttowritethewavefile.When
youcallthefunction,youhavetogiveitatemplateparameterspecifyingwhatTYPEyouwanttouseforyour
samples.Thethreesupportedtypesareuint8,int16andint32.Forsimplewaveformslikethoseweareworkingwith
today,thereisnoaudibledifferencebetweenthe3,soallthesamplesjustmake16bitwavefiles.

So,webustoutsomemathandfigureouthereshowtogenerateasinewave,respectingthesamplerateand
frequencywewanttouse:

//makeanaivesinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=sin((float)nIndex*2*(float)M_PI*fFrequency/(float)nSampleRate);
}
WriteWaveFile<int16>("sinenaive.wav",pData,nNumSamples,nNumChannels,nSampleRate);

Thatdoeswork,andifyoulistentothewavefile,itdoessoundcorrect:
NaiveSineWaveGeneration

Itevenlookscorrect:

Thereisasubtleproblemwhengeneratingthesinewavethatwaythoughwhichwewilltalkaboutnext.

PoppingakaDiscontinuity
Theproblemwithhowwegeneratedthewavefileonlybecomesapparentwhenwetrytoplaytwotonesrightnextto
eachother,likeinthefollowingcodesegment:

//makeadiscontinuitous(popping)sinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
if(nIndex<nNumSamples/2)
{
floatfCurrentFrequency=CalcFrequency(3,3);
pData[nIndex]=sin((float)nIndex*2*(float)M_PI*fCurrentFrequency/
(float)nSampleRate);
}
else
{
floatfCurrentFrequency=CalcFrequency(3,4);
pData[nIndex]=sin((float)nIndex*2*(float)M_PI*fCurrentFrequency/
(float)nSampleRate);
}

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 2/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

}
WriteWaveFile<int16>("sinediscon.wav",pData,nNumSamples,nNumChannels,nSampleRate);

Quicknoteaboutanewfunctionshownhere,calledCalcFrequency.Imadethatfunctionsothatyoupassthenote
youwant,andtheoctaveyouwant,anditwillreturnthefrequencyforthatnote.Forinstance,togetmiddleCaka
C4(thetoneallthesesamplesuse),youuseCalcFrequency(3,3),whichreturnsapproximately261.626.

Listentothewavefilegeneratedandyoucanhearapoppingnoisewherethetonechangesfromonefrequencyto
thenext:DiscontinuousSineWave

Sowhyisthis?Thereasonisbecausehowwearegeneratingoursinewavesmakesadiscontinuitywherethe2
wavefileschange.

Hereyoucanseethepointthatthefrequencieschangeandhowaprettysmalldiscontinuitycanmakeaprettybig
impactonyoursound!Thesoundyouarehearinghasanofficialname,calledapop(DSP/synth/otheraudio
peoplewilltalkaboutpoppingintheiraudio,anddiscontinuityisthereasonforit)

Sohowdowefixit?Insteadofmakingthesinewaveberigidlybasedontime,whereforeachpoint,wecalculate
thesinevaluewithnoregardtopreviousvalues,weuseaFreeSpinningOscillator.

ThatisafancywayofsayingwejusthaveavariablekeeptrackofthecurrentPHASE(angle)thatweareatinthe
sinewaveforthecurrentsample,andtogetthenextsample,weadvanceourphasebasedonthefrequencyatthe
time.Basicallyouroscillatorisawheelthatspinsfreely,andourcurrentfrequencyjustsayshowfasttoturnthe
wheel(fromwhereveritisnow)togetthevalueforthenextsample.

Hereswhatthelookslikeincode:


//makeacontinuoussinewavethatchangesfrequencies
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
if(nIndex<nNumSamples/2)
{
floatfCurrentFrequency=CalcFrequency(3,3);
fPhase+=2*(float)M_PI*fCurrentFrequency/(float)nSampleRate;

while(fPhase>=2*(float)M_PI)
fPhase=2*(float)M_PI;

while(fPhase<0)
fPhase+=2*(float)M_PI;

pData[nIndex]=sin(fPhase);
}
else

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 3/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

{
floatfCurrentFrequency=CalcFrequency(3,4);
fPhase+=2*(float)M_PI*fCurrentFrequency/(float)nSampleRate;

while(fPhase>=2*(float)M_PI)
fPhase=2*(float)M_PI;

while(fPhase<0)
fPhase+=2*(float)M_PI;

pData[nIndex]=sin(fPhase);
}
}
WriteWaveFile<int16>("sinecon.wav",pData,nNumSamples,nNumChannels,nSampleRate);

Notethatwekeepthephasebetween0and2*PI.Theresnomathematicalreasonforneedingtodothis,butin
floatingpointmath,ifyouletavaluegettoolarge,itstartstoloseprecision.Thatmeans,thatifyoumadeawave
filethatlastedalongtime,theaudiowouldstarttodegradethelongeritplayed.Ialsouseawhileloopinsteadofa
regularifstatement,becauseifsomeoneusesverylargefrequencies,youcanpass2*PIacoupleoftimesina
singlesample.Also,icheckthatitsabovezero,becauseitisvalidtousenegativefrequencyvalues!Allstufftobe
mindfulofwhenmakingyourownsynthprograms(:

Hereswhatthegeneratedwavefilesoundslike,noticethesmoothtransitionbetweenthetwonotes:
ContinuousSineWave

Andhereswhatitlookslikevisuallywherethewavechangesfrequency,whichyoucanseeisniceandsmooth(the
bottomwave).Thetopwaveisthepoppingsinewaveimageagainatthesamepointintimeforreference.Onthe
smoothwaveitisntevenvisuallynoticeablethatthefrequencyhaschanged.

Onelastwordonthispoppingisactuallysometimesdesiredandcanhelpmakeupapartofagoodsound.For
instance,somepercussionsoundscanmakeuseofpoppingtosoundmoreappropriate!

SineWaveOscillator
Forourfinalincarnationofasinewaveoscillator,heresanicesimplehelperfunction:

floatAdvanceOscilator_Sine(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=2*(float)M_PI*fFrequency/fSampleRate;

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 4/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

while(fPhase>=2*(float)M_PI)
fPhase=2*(float)M_PI;

while(fPhase<0)
fPhase+=2*(float)M_PI;

returnsin(fPhase);
}

Youpassthatfunctionyourcurrentphase,thefrequencyyouwant,andthesamplerate,anditwilladvanceyour
phase,andreturnthevalueforyournextaudiosample.

Heresanexampleofhowtouseit:

//makeasinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=AdvanceOscilator_Sine(fPhase,fFrequency,(float)nSampleRate);
}
WriteWaveFile<int16>("sine.wav",pData,nNumSamples,nNumChannels,nSampleRate);

Hereswhatitsoundslike(nothingnewatthispoint!):
VanillaSineWave

WaveAmplitude,VolumeandClipping
YoucanadjusttheAMPLITUDEofanywaveformbymultiplyingeachsamplebyavalue.Valuesgreaterthanone
increasetheamplitude,makingitlouder,valueslessthanonedecreasetheamplitude,makingitquieter,and
negativevaluesflipthewaveover,butalsohavetheabilitytomakeitquieterorlouder.

Oneplacepeopleusenegativeamplitudes(volumes)isfornoisecancellation.Ifyouhaveacomplexsoundthathas
somenoiseinit,butyouknowthesourceofthenoise,youcantakethatnoice,multiplyitby1togetavolumeof
1,andADDIT(orMIXIT)intothemorecomplexsound,effectivelyremovingthenoisefromthesound.Thereare
otherusestoobutthisisoneconcrete,realworldexample.

Thiscodesamplegeneratesaquieterwavefile:

//makeaquietersinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=AdvanceOscilator_Sine(fPhase,fFrequency,(float)nSampleRate)*0.4f;
}
WriteWaveFile<int16>("sinequiet.wav",pData,nNumSamples,nNumChannels,nSampleRate);

Andhereswhatthatsoundslike:
VanillaSineWaveQuiet

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 5/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

Andhereswhatthatlookslike:

Ifyourecallthough,whenwewriteawavefile,wemap1tothesmallestintnumberwecanstore,and1tothe
highestintnumberwecanstore.Whathappensifwemakesomethingtooloud,sothatitgoesabove1.0orbelow
1.0?

OnewaytofixthiswouldbetoNormalizethesounddata.Tonormalizeit,youwouldloopthrougheachsamplein
thestreamandfindthehighestabsolutevaluesample.Forinstanceifyouhad3samples:1.0,1.2,0.8,the
highestabsolutesamplevaluewouldbe1.2.

Onceyouhavethisvalue,youloopthroughthesamplesinthestreamanddividebythisnumber.Afteryoudothis,
everysampleinthestreamwillbewithintherange1to1.Notethatifyouhadanydatathatwouldbeclipping,this
processhasthesideeffectofmakingyourentirestreamquietersinceitreducestheamplitudeofeverysample.If
youdidnthaveanyclippingdata,thisprocesshasthesideeffectofmakingyourentirestreamlouderbecauseit
increasestheamplitudeofeverysample.

Anotherwaytodealwithitistojustclampthevaluestothe1,1range.Inthecaseofasinewave,thatmeanswe
chopoffthetopand/orthebottomofthewaveandtheresjustaflatplateauwherethenumberswentoutofrange.

Thisiscalledclipping,andalongwithpoppingare2ofthemainproblemspeoplehavewithaudioqualitydegradation.
Aliasingisathird,andissomethingweaddressinthenextchapterbytheway!
(http://en.wikipedia.org/wiki/Aliasing)

Heressomecodeforgeneratingaclippingsinewave:

//makeaclippingsinewave
for(intnIndex=0;nIndex<nNumSamples;++nIndex)
{
pData[nIndex]=AdvanceOscilator_Sine(fPhase,fFrequency,(float)nSampleRate)*1.4f;
}
WriteWaveFile<int16>("sineclip.wav",pData,nNumSamples,nNumChannels,nSampleRate);

Andhereswhatitsoundslike:
VanillaSineWaveClipping

Also,hereswhatitlookslike:

Notethatinthiscase,itdoesntnecessarilysoundBADcomparedtoaregular,nonclippingsinewave,butitdoes
sounddifferent.Thatmightbeagoodthing,orabadthing,dependingonyourintentions.Withmorecomplex
sounds,likevoice,oracousticmusic,thiswillusuallymakeitsoundterrible.Audioengineershavetocarefully
controlthelevels(volumes)ofthechannelsbeingmixed(added)togethertomakesuretheresultingoutputdoesnt
gooutsideofthevalidrangeandcauseclipping.Also,inanaloghardware,goingoutofrangecancausedamageto
thedevicesiftheyarentbuilttoprotectthemselvesfromit!
http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 6/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

Inthecaseofrealtimesynthesis,asyoumightimagine,normalizingwavedataisimpossibletodobecauseit
requiresthatyouknowallthesounddataupfronttobeabletonormalizethedata.Inrealtimeapplications,besides
justmakingsurethelevelskeepeverythinginrange,youalsohavetheoptionofusingacompressorwhichsortof
dynamicallynormalizesonthefly.Checkthisoutformoreinformation:
http://en.wikipedia.org/wiki/Dynamic_range_compression

SquareWaveOscillator
Heresthecodeforthesquarewaveoscillator:

floatAdvanceOscilator_Square(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=fFrequency/fSampleRate;

while(fPhase>1.0f)
fPhase=1.0f;

while(fPhase<0.0f)
fPhase+=1.0f;

if(fPhase<=0.5f)
return1.0f;
else
return1.0f;
}

Notethatweareusingthephaseasifitsapercentage,insteadofanangle.Sinceweareusingitdifferently,that
meansifyouswitchfromsinewavetosquarewave,therewillbeadiscontinuity(apop).However,inpracticethis
happensanywaysalmostallthetimebecauseunlessyouchangefromsinetosquareattheverytoporbottomof
thesinewave,therewillbediscontinuityanyways.Inreality,thisreallydoesntmatter,butyoucouldfixitto
switchonlyonthoseboundaries,oryoucouldusecrossfadingorblendingtofadeonewaveout(decrease
amplitudefrom1to0),whilebringingthenewwavein(increaseamplitudefrom0to1),addingthemtogethertoget
theoutput.Doingsowillmakeasmoothtransitionbutaddssomecomplexity,andsquarewavesbynature
constantlypopanywaysitswhatgivesthemtheirsound!

Hereswhatasquarewavesoundslikeandlookslike:
SquareWave

SawWaveOscillator
Weusedthesawwaveinchapterone.Heresthecodeforasawwaveoscillator:

floatAdvanceOscilator_Saw(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=fFrequency/fSampleRate;

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 7/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

while(fPhase>1.0f)
fPhase=1.0f;

while(fPhase<0.0f)
fPhase+=1.0f;

return(fPhase*2.0f)1.0f;
}

Hereswhatasawwavelooksandsoundslike:
SawWave

Notethatsometimessawwavespointtheotherdirectionandthedropoffisontheleftinsteadofontheright,and
therestofthewaydescendsinsteadofrisesbutasfarasIhaveseen,thereisnoaudibleorpracticaldifference.

TriangleWaveOscillator
Alotofsynthsdontevenbotherwithatrianglewave,andthosethatdo,arejustforapproximationsofasinewave.
Atrianglewavesoundsalotlikeasinewaveandlooksabitlikeittoo.

Heresthecodeforatrianglewaveoscillator:

floatAdvanceOscilator_Triangle(float&fPhase,floatfFrequency,floatfSampleRate)
{
fPhase+=fFrequency/fSampleRate;

while(fPhase>1.0f)
fPhase=1.0f;

while(fPhase<0.0f)
fPhase+=1.0f;

floatfRet;
if(fPhase<=0.5f)
fRet=fPhase*2;
else
fRet=(1.0ffPhase)*2;

return(fRet*2.0f)1.0f;
}

Hereswhatitlooksandsoundslike:
TriangleWave

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 8/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

NoiseOscillator
Believeitornot,evenstatichasitsplacetoo.Itsusedsometimesforpercussion(putanenvelopearoundsome
statictomakeaclapsound),itcanbeusedasalowfrequencyoscillatorakaLFO(theoldholdandsampletype
stuff)andotherthingsaswell.Staticisjustrandomaudiosamples.

Thecodeforanoiseoscillatorisslightlydifferentthantheothers.Youhavetopassitthelastsamplegenerated(you
canpass0ifitsthefirstsample)anditwillcontinuereturningthatlastvalueuntilitstimetogenerateanewrandom
number.Itdetermineswhenitstimebasedonthefrequencyyoupassin.Ahigherfrequencymeanmorerandom
numberswillbechoseninthesameamountofaudiodatawhilealowerfrequencymeansthatfewerrandom
numberswillbechosen.

Atlowerfrequencies(likeinthesample),itkindofsoundslikeanexplosionorrocketshipsoundeffectfromthe80s
whichisfun

Heresthecode:

floatAdvanceOscilator_Noise(float&fPhase,floatfFrequency,floatfSampleRate,floatfLastValue)
{
unsignedintnLastSeed=(unsignedint)fPhase;
fPhase+=fFrequency/fSampleRate;
unsignedintnSeed=(unsignedint)fPhase;

while(fPhase>2.0f)
fPhase=1.0f;

if(nSeed!=nLastSeed)
{
floatfValue=((float)rand())/((float)RAND_MAX);
fValue=(fValue*2.0f)1.0f;

//uncommentthebelowtomakeitslightlymoreintense
/*
if(fValue<0)
fValue=1.0f;
else
fValue=1.0f;
*/

returnfValue;
}
else
{
returnfLastValue;

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 9/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

}
}

Hereswhatitlooksandsoundslike:
Noise

IthinkitkindoflooksliketheArizonadesert

Asaquickaside,ihavetherandomnumbersasrandomfloatingpointnumbers(theycanbeanythingbetween1.0
and1.0).AnotherwaytogeneratenoiseistomakeitsoitwillchooseonlyEITHER1or1andnothinginbetween.
Itgivesaslightlyharshersound.Thecodetodothatisintheoscillatorifyouwanttotryitout,itsjustcommented
out.Thereareotherwaystogeneratenoisetoo(checkoutpinknoisehttp://en.wikipedia.org/wiki/Pink_noise)
butthisoughttobegoodenoughforourimmediateneeds!

MoreExoticWaveForms
TwootheroscillatorsIveusedonoccasionisthesquaredsinewaveandtherectanglewave.

Tocreateasquaredsinewaveallyouneedtodoismultiplyeachsamplebyitself(squaretheaudiosample).This
makesawaveformthatissimilartosinewaves,butalittlebitdifferent,andsoundsabitdifferenttoo.

Arectanglewaveiscreatedbymakingitsothewavespendseithermoreorlesstimeintheupordownpartof
thewave.Insteadofitbeing50%ofthetimeinup,and50%ofthetimeindownyoucanmakeitsoitspends
80%ofthetimeinup,and20%ofthetimeindown.Itmakesitsoundquiteabitdifferent,andthemoredifferentthe
percentagesare,thebrighteritsounds.

Also,youcanaddmultiplewaveformsamplestogethertogetmoreinterestingwaveforms(likeaddingatriangle
andasquarewaveofthesamefrequencytogether,andreducingtheamplitudetoavoidclipping).Thatscalled
additivesynthesisandwelltalkmoreaboutthatnextchapter,includinghowtomakemorecorrectwaveformsusing
sinewavestoavoidaliasing.

Youcanalsomultiplywaveformstogethertocreateother,moreinterestingwaves.Strictlyspeakingthisiscalled
AMsynthesis(amplitudemodulationsynthesis)whichisalsosometimesknownasringmodulationwhendonea
certainway.

Asyoucansee,therearealotofdifferentwaystocreateoscillators,andthewaveformsarejustlimitedbyyour
imagination.Playaroundandtrytomakeyourownoscillatorsandexperiment!

FinalSamples
Nowwehavethesimplebasicsdownforbeingabletocreatemusic.heresasmallsongthatisgeneratedinthe
samplecode:
SimpleSong

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 10/11
11/4/2017 DIYSynth2:CommonWaveForms|Theblogatthebottomofthesea

Andjusttoreinforcehowimportantkeepingyourwavedatacontinuousis,heresthesamewavefile,butabout0.75
secondsinaputaSINGLE1.0samplewhereitdoesntbelong.asinglesamplewrongwhentheres44100samples
persecondandlookhowmuchitaffectstheaudio.
SimpleSongWithPop

UntilNextTime
Nextupwewilltalkaboutaliasingandhowtoavoidit,makingmuchbettersoundingsaw,squareandtriangle
wavesthatarelessharshontheears.

Likethis:

Like
Onebloggerlikesthis.

Related

DIYSynth3:Sampling,Mixing, DIYSynth1:SoundOutput DIYSynth:FlangeEffect


andBandLimitedWaveForms May142012 March162015
June182012 In"AudioSynthesis" In"AudioSynthesis"
In"AudioSynthesis"

Comments
0comments

0Comments Sortby Oldest

Addacomment...

FacebookCommentsPlugin

PostedinAudioSynthesis permalink[http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/]

AboutDemofox

I'magameandengineprogrammeratBlizzardEntertainmentandhavebeenmakinggamessince1990(startingoutwith
QBasicandTI85games)Myshippedtitlesinclude:*HeroesoftheStorm*StarCraftII:HeartoftheSwarm&Legacyof
thevoid*InsanelyTwistedShadowPlanet(PC)*GothamCityImpostors(PC,360,PS3)*LineRider(PC,Wii,DS)Ialso
likehiking,makingmusic,learningcoolnewstuffandattemptingtheimpossible.
ViewallpostsbyDemofox

http://blog.demofox.org/2012/05/19/diysynthesizerchapter2commonwaveforms/ 11/11

Das könnte Ihnen auch gefallen