Hur spela upp noter med wavetable via mjukvara?
Hur spela upp noter med wavetable via mjukvara?
Halloj!
Har en liten fråga om just detta, hur man samplar ut eller wavesteppa ut en vågform i rätt frekvens beroende på vilken not man spelar.
Håller på med en egen liten design, där jag använder en PIC 16F628A och dess PWM modul för att generera vågformer genom PWM och ett RC-filter. Använder då bara en ut pinne och RC-filter, ingen D/A omvandlare.
Jag har lyckats få ordning på det mesta vad gäller att "outputta" en sinusvåg i ett 50 bytes långt wavetable som är hyffsat bra (se bild) i 390,625 Hz, med en output sample frekvens på 19531,25 Hz och upplösning på 8-bit. Detta efter en del hårslitande med prescalers, och table lookups över 256 bytes block.
Nästa problem som jag kommit till nu är mer algoritmiskt. Hur man justerar frekvensen på en t.ex 64 bytes vågform så man kan spela noter? Iomed att vågformen är "fast" 64 bytes, måste man ju steppa igenom den olika snabbt beroende på vilken frekvens man vill ha den i. Det blir ju tidsödande att kalkylera varje steg matematiskt i "realtid", så någon form av förgenererad tabell skulle vara bra.
Jag har kommit fram till en lösning/variant själv (vet inte om det är "standard") vilket är att förkalkylera ett 16-bitars step värde för varje not, som adderas till 16-bitars pekare (eller 8.8 bitar kanske man kan säga), men man använder bara de övre 8 bitarna för själva steppningen/pekningen. På detta sätt får man ju fraktioner av ett heltal eftersom t.ex. 0,05 som stepvärde inte funkar bra på heltal (blir ju 0 hela tiden vilket innebär att man aldrig kommer längre än till första byten i vågformen).
Jag har sökt en del på webben, men har inte hittat någon riktigt bra förklaring på hur andra löser detta. Dom flesta lämnar ute den biten när dom förklarar, och tittar man på kod så är den dåligt dokumenterad och ofta dryg att sätta sig in i.
Tänkte om någon annan har någon bättre lösning på detta problem?
Har en liten fråga om just detta, hur man samplar ut eller wavesteppa ut en vågform i rätt frekvens beroende på vilken not man spelar.
Håller på med en egen liten design, där jag använder en PIC 16F628A och dess PWM modul för att generera vågformer genom PWM och ett RC-filter. Använder då bara en ut pinne och RC-filter, ingen D/A omvandlare.
Jag har lyckats få ordning på det mesta vad gäller att "outputta" en sinusvåg i ett 50 bytes långt wavetable som är hyffsat bra (se bild) i 390,625 Hz, med en output sample frekvens på 19531,25 Hz och upplösning på 8-bit. Detta efter en del hårslitande med prescalers, och table lookups över 256 bytes block.
Nästa problem som jag kommit till nu är mer algoritmiskt. Hur man justerar frekvensen på en t.ex 64 bytes vågform så man kan spela noter? Iomed att vågformen är "fast" 64 bytes, måste man ju steppa igenom den olika snabbt beroende på vilken frekvens man vill ha den i. Det blir ju tidsödande att kalkylera varje steg matematiskt i "realtid", så någon form av förgenererad tabell skulle vara bra.
Jag har kommit fram till en lösning/variant själv (vet inte om det är "standard") vilket är att förkalkylera ett 16-bitars step värde för varje not, som adderas till 16-bitars pekare (eller 8.8 bitar kanske man kan säga), men man använder bara de övre 8 bitarna för själva steppningen/pekningen. På detta sätt får man ju fraktioner av ett heltal eftersom t.ex. 0,05 som stepvärde inte funkar bra på heltal (blir ju 0 hela tiden vilket innebär att man aldrig kommer längre än till första byten i vågformen).
Jag har sökt en del på webben, men har inte hittat någon riktigt bra förklaring på hur andra löser detta. Dom flesta lämnar ute den biten när dom förklarar, och tittar man på kod så är den dåligt dokumenterad och ofta dryg att sätta sig in i.
Tänkte om någon annan har någon bättre lösning på detta problem?
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Re: Hur spela upp noter med wavetable via mjukvara?
Det är nog generellt svårt att få till en "bra" lösning på en så pass
enkel processor som en 16F628A'a. Men visst, tabeller med beräknade
steg för de olika noterna är nog givet i alla fall. Alla noter är ju kända
till sin frekvens redan vid "design-time", så att säga.
Men, som sagt, så mycket mer en ett jul experiment lär det inte
bli på en 16F628A, sannolikt behöver man en PIC24 eller hellre
en dsPIC som har lite mer och bättre hårdvara för detta.
EDIT: Skulle ha stått "kul experiment", men det blev ju ganska
OK även med felskrivningen ovan...
enkel processor som en 16F628A'a. Men visst, tabeller med beräknade
steg för de olika noterna är nog givet i alla fall. Alla noter är ju kända
till sin frekvens redan vid "design-time", så att säga.

Men, som sagt, så mycket mer en ett jul experiment lär det inte
bli på en 16F628A, sannolikt behöver man en PIC24 eller hellre
en dsPIC som har lite mer och bättre hårdvara för detta.
EDIT: Skulle ha stått "kul experiment", men det blev ju ganska
OK även med felskrivningen ovan...

Re: Hur spela upp noter med wavetable via mjukvara?
Jo, detta är ju mer ett kul jul experiment
. Ordet "design" kanske var fel valt av mig ovan, har inte tänkt använda just detta i något större projekt, mer än lärdomen då.
Att det vart 16F628A var mest för att det var den jag hade liggandes när jag fick impulsen att ge mig in i detta. Tänkte från början mest testa vilken kvalité man kan få ut på en sinusvåg med PWM lösningen, sedan byggdes det på med att kanske spela upp några noter också. Jag har ju iaf 256 instruktionscykler på mig per interrupt/sample, så jag tänkte jag iaf skulle kunna hinna mixa ihop två kanaler och spela en tvåstämmig Fur Elise eller nåt.
Siktar på en ljudkvalite nånstans mellan en ZX Spektrum och C64.

Att det vart 16F628A var mest för att det var den jag hade liggandes när jag fick impulsen att ge mig in i detta. Tänkte från början mest testa vilken kvalité man kan få ut på en sinusvåg med PWM lösningen, sedan byggdes det på med att kanske spela upp några noter också. Jag har ju iaf 256 instruktionscykler på mig per interrupt/sample, så jag tänkte jag iaf skulle kunna hinna mixa ihop två kanaler och spela en tvåstämmig Fur Elise eller nåt.

Siktar på en ljudkvalite nånstans mellan en ZX Spektrum och C64.
Re: Hur spela upp noter med wavetable via mjukvara?
RB har en intressant algoritm för ljud med PIC: http://www.romanblack.com/picsound.htm
Re: Hur spela upp noter med wavetable via mjukvara?
Japp!
Har varit och kollat på den sidan när jag började med detta, och testade även den metoden innan jag gick över till hårdvaru PWM modulen. Fast sidan tar upp det problem jag redan löst, dvs att konvertera ett sample från vågformen till en PWM puls under en PWM/sample period. Eller rättare sagt att sampla om en vågform till PWM. Det smarta med att köra hårdvaru PWM modulen är att man bara matar den direkt med ett sample från ett wavetable (under förutsättning att man ställt in och kalkylerat allt rätt), så sköter den att sätta porten hög/låg under dutycykeln och kör en interrupt när PWM perioden är klar. Då tar man bara stoppar in nästa sample. Avlastar processorn och minskar komplexiteten i koden enormt mot om man skulle bitbangat utan hårdvaru PWM (vilket jag testade först enligt ovan nämnda metod + någon till).
Det problem som återstår för mig är lika oavsett om du kör PWM eller DAC, vilket är att ändra frekvensen på själva samplade vågformen (har inget med PWMen som sådan att göra mer än att den styr output rate vilket du även har om du kör med DAC).
Tanken är att jag skall ha tre (eller fyra) wavetables på 64 bytes vardera (sinus, sågtand, triangel och eventuellt brus, men brus behöver ju inget table). Min sample frekvens ut är 19531.25 Hz vilket då betyder att om jag steppar ut vågformen i skala 1:1 (skickar på en byte/sample från vågformen varje PWM/output period) så är frekvensen på vågformen 19531.25/64 = 305,17 Hz (Om jag räknat rätt). Frågan jag har är vilken den bästa algoritmen är för att kalkylera hur jag skall få ut samma vågform i t.ex 440 Hz som är ett A i fjärde oktaven?
Tror dock jag kör på som jag tänkt, skulle bara vara tråkigt om jag upptäcker att jag tänkt fel när jag har kodat klart... Fast man är ju van
Har varit och kollat på den sidan när jag började med detta, och testade även den metoden innan jag gick över till hårdvaru PWM modulen. Fast sidan tar upp det problem jag redan löst, dvs att konvertera ett sample från vågformen till en PWM puls under en PWM/sample period. Eller rättare sagt att sampla om en vågform till PWM. Det smarta med att köra hårdvaru PWM modulen är att man bara matar den direkt med ett sample från ett wavetable (under förutsättning att man ställt in och kalkylerat allt rätt), så sköter den att sätta porten hög/låg under dutycykeln och kör en interrupt när PWM perioden är klar. Då tar man bara stoppar in nästa sample. Avlastar processorn och minskar komplexiteten i koden enormt mot om man skulle bitbangat utan hårdvaru PWM (vilket jag testade först enligt ovan nämnda metod + någon till).
Det problem som återstår för mig är lika oavsett om du kör PWM eller DAC, vilket är att ändra frekvensen på själva samplade vågformen (har inget med PWMen som sådan att göra mer än att den styr output rate vilket du även har om du kör med DAC).
Tanken är att jag skall ha tre (eller fyra) wavetables på 64 bytes vardera (sinus, sågtand, triangel och eventuellt brus, men brus behöver ju inget table). Min sample frekvens ut är 19531.25 Hz vilket då betyder att om jag steppar ut vågformen i skala 1:1 (skickar på en byte/sample från vågformen varje PWM/output period) så är frekvensen på vågformen 19531.25/64 = 305,17 Hz (Om jag räknat rätt). Frågan jag har är vilken den bästa algoritmen är för att kalkylera hur jag skall få ut samma vågform i t.ex 440 Hz som är ett A i fjärde oktaven?
Tror dock jag kör på som jag tänkt, skulle bara vara tråkigt om jag upptäcker att jag tänkt fel när jag har kodat klart... Fast man är ju van

Re: Hur spela upp noter med wavetable via mjukvara?
Är det en släkting till Robert Yannes vi har med oss här på forumet? 

Re: Hur spela upp noter med wavetable via mjukvara?
Tackar för tipsen! Kollade tråden vilket var ungefär det jag kommit fram till, fast jag kör lookup, ingen kalkylering för varje sample. Genererade 16-bitars lookup tables för varje not med Perl. Lyckades efter mycket om och men få ut lite olika noter. Jämförde 220 och 440 Hz med något jag hittade på Youtube (Tack youtube!
) och jag hörde ingen skillnad, så 16 bitars upplösning (eller 8.8 eftersom jag bara använder de övre 8 bitarna när jag utför själva stegningen till nästa sample) verkar vara tillräckligt.
Har dock lite störande spegelfrekvenser när jag kopplar in lurar. Får fixa det med nåt analogt filter sedan.

Har dock lite störande spegelfrekvenser när jag kopplar in lurar. Får fixa det med nåt analogt filter sedan.
Re: Hur spela upp noter med wavetable via mjukvara?
Jag lekte lite med detta och min grundtanke är som följer:
Man tar en tabell över vågformen, uppdelad i många steg. Med en PIC kan 256 steg vara bra men jag testade med 360 steg, en för varje grad.
Tabellen är såklart "normaliserat".
Sedan avgörs frekvensen som spelas upp av hur stora steg som tas i tabellen, detta då uppdateringen av PWM-delen är med fast hastighet. Vid låga frekvenser kan/kommer detta betyda att samma "vinkel" skickas till PWM-delen mer än en gång.
Fler toner tillsamman sker vid att addera de olika resultat och normalisera dom, spelas alltså 2 toner samtidig (tänker t.ex. DTMF) läser man deras ögonblickliga värde i tabellen, delar varje värde med 2 och adderar slutresultatet.
Om man nöjer sig med telefonkvalitet (300Hz-3kHz) behöver man inte uppdatera snabbare än kanske 15kHz vilket borde ge riklig tid att utföra uppslag, delning och addition med en PIC på 8MHz, det är ju ändå 133 instruktioner/sample. Det exakta behov beror på vilken kvalitet man vill ha ut såklart.
Man kan använda fler kurvformer vid att ha olika tabeller.
Man tar en tabell över vågformen, uppdelad i många steg. Med en PIC kan 256 steg vara bra men jag testade med 360 steg, en för varje grad.
Tabellen är såklart "normaliserat".
Sedan avgörs frekvensen som spelas upp av hur stora steg som tas i tabellen, detta då uppdateringen av PWM-delen är med fast hastighet. Vid låga frekvenser kan/kommer detta betyda att samma "vinkel" skickas till PWM-delen mer än en gång.
Fler toner tillsamman sker vid att addera de olika resultat och normalisera dom, spelas alltså 2 toner samtidig (tänker t.ex. DTMF) läser man deras ögonblickliga värde i tabellen, delar varje värde med 2 och adderar slutresultatet.
Om man nöjer sig med telefonkvalitet (300Hz-3kHz) behöver man inte uppdatera snabbare än kanske 15kHz vilket borde ge riklig tid att utföra uppslag, delning och addition med en PIC på 8MHz, det är ju ändå 133 instruktioner/sample. Det exakta behov beror på vilken kvalitet man vill ha ut såklart.
Man kan använda fler kurvformer vid att ha olika tabeller.
Re: Hur spela upp noter med wavetable via mjukvara?
Gör så här, använd dig av ett timerinterrupt.
I timerinterruptet adderar du ett värde från en lookuptable som motsvarar den frekvensen du vill spela till en, säg unsigned 16 bit variabel.
Denna variabel använder du sedan för att plocka värdena ur wavetablen.
Jag har inte alls räknat på vilka slags frekvenser/noggranhet du kan tänkas komma upp i med den processorn du har.
I timerinterruptet adderar du ett värde från en lookuptable som motsvarar den frekvensen du vill spela till en, säg unsigned 16 bit variabel.
Denna variabel använder du sedan för att plocka värdena ur wavetablen.
Jag har inte alls räknat på vilka slags frekvenser/noggranhet du kan tänkas komma upp i med den processorn du har.
Re: Hur spela upp noter med wavetable via mjukvara?
Ja, där har du en schyst implementation av det jag beskrev, skillnaden är bara att jesper adderar till fasackumulatorn i main-loopen istället för i ett timer interrupt.
Re: Hur spela upp noter med wavetable via mjukvara?
Tackar för alla svar så långt!
Har haft en liten paus sedan sist. Har kommit så långt iaf att jag nu kan spela upp en 64 bytes "förgenererad" sinusvåg i rätt frekvens för respektive not. Dvs ett A oktav 4 blir 440 Hz som sig bör. Har även fått till en N00b tracker som gör att jag kan spela ett antal sinus noter i X antal millisekunder som kan bilda en melodi. Dock enbart en oscillator och kanal än, även om det är enkelt att modifiera för fler. Koden för volym och mixning finns, fast används inte nu i debug stadiet.
Slutsatsen jag dragit så långt är att en sinusvåg eller sågtand eller triangel eller puls helt omodifierad låter ren "skit"
. Vi snackar ZX-81 här, typ.
Efter lite efterforskning har jag kommit fram till att det krävs ljudtekniska finesser som LFO, VFO, Envelope, Filter, Fasmodulering, Arpeggio, Slide och PWM (förutom den PWM som används för att generera varje sample) för att få fräna synthljud. I vissa fall "mixning" mellan flera oscillatorer.
Har kollat lite på SID/chip trackers för att få ideer om hur ljuden skall bli lite "bättre" och bl.a. kollat på Linus Åkessons Hardware Chiptune projekt. Han har använt en Atmega på 8MHz. Jag använder en PIC klockad på 20MHz och output sample period på 19531.25 Hz. Varje sample genererar en interrupt var 51.2 uS (Var 256:e instruktionscykel), där det enda jag gör är att steppa fram till nästa sample och mixa volym + övriga kanaler. Jag har också fixat en 8x8 multipliceringsrutin som jag kommer att behöva för mer precis volymkontroll och för LFO,VFO och Filter. Det mesta förutom mixning och steppning och andra mindre grejor går att fixa utanför interruptrutinen, i vanliga mainloopen.
Hur man fixar filter i mjukvara har jag ingen aning om, men jag kollar på det. Dessutom måste jag lösa hur jag skall pulsbreddsmodulera vågformer. Blir ju liksom pulsbredd på pulsbredd. Synd man är så kass på matte.
Jag skall kolla på Jespers exempel (tack Jesper!) för lite inspiration/ideer.
Har haft en liten paus sedan sist. Har kommit så långt iaf att jag nu kan spela upp en 64 bytes "förgenererad" sinusvåg i rätt frekvens för respektive not. Dvs ett A oktav 4 blir 440 Hz som sig bör. Har även fått till en N00b tracker som gör att jag kan spela ett antal sinus noter i X antal millisekunder som kan bilda en melodi. Dock enbart en oscillator och kanal än, även om det är enkelt att modifiera för fler. Koden för volym och mixning finns, fast används inte nu i debug stadiet.
Slutsatsen jag dragit så långt är att en sinusvåg eller sågtand eller triangel eller puls helt omodifierad låter ren "skit"

Efter lite efterforskning har jag kommit fram till att det krävs ljudtekniska finesser som LFO, VFO, Envelope, Filter, Fasmodulering, Arpeggio, Slide och PWM (förutom den PWM som används för att generera varje sample) för att få fräna synthljud. I vissa fall "mixning" mellan flera oscillatorer.
Har kollat lite på SID/chip trackers för att få ideer om hur ljuden skall bli lite "bättre" och bl.a. kollat på Linus Åkessons Hardware Chiptune projekt. Han har använt en Atmega på 8MHz. Jag använder en PIC klockad på 20MHz och output sample period på 19531.25 Hz. Varje sample genererar en interrupt var 51.2 uS (Var 256:e instruktionscykel), där det enda jag gör är att steppa fram till nästa sample och mixa volym + övriga kanaler. Jag har också fixat en 8x8 multipliceringsrutin som jag kommer att behöva för mer precis volymkontroll och för LFO,VFO och Filter. Det mesta förutom mixning och steppning och andra mindre grejor går att fixa utanför interruptrutinen, i vanliga mainloopen.
Hur man fixar filter i mjukvara har jag ingen aning om, men jag kollar på det. Dessutom måste jag lösa hur jag skall pulsbreddsmodulera vågformer. Blir ju liksom pulsbredd på pulsbredd. Synd man är så kass på matte.
Jag skall kolla på Jespers exempel (tack Jesper!) för lite inspiration/ideer.
Re: Hur spela upp noter med wavetable via mjukvara?
> Hur man fixar filter i mjukvara har jag ingen aning om, men jag kollar på det.
Kolla in IIR-filter.
Det här dokumentet har jag haft nytta av:
http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
Formeln som jag använder mig av i min FPGA-synth är:
där q är a0 och a2 utbrutet från Eq 1 i ovanstående dokument (q är alltså resonans)
y0 = q*(b0*x0 + b1*x1 + b2*x2 + a1*y1 - 2*y2) + y2
> Dessutom måste jag lösa hur jag skall pulsbreddsmodulera vågformer.
Ett intressant sett att "pulsbreddsmodulera" på är att ha två stycken oscillatorer som genererar samma vågform, men med olika offset.
Sen subtraherar eller multiplicerar du dessa. Genom att modulera offseten kan du åstadkomma något som liknar pulsbreddsmodulering.
(Idén är tagen från Reasons Subtractor, de kallar detta för Phase Offset Modulation, jag har implementerat en variant av detta i min FPGA-synth).
Kolla in IIR-filter.
Det här dokumentet har jag haft nytta av:
http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
Formeln som jag använder mig av i min FPGA-synth är:
där q är a0 och a2 utbrutet från Eq 1 i ovanstående dokument (q är alltså resonans)
y0 = q*(b0*x0 + b1*x1 + b2*x2 + a1*y1 - 2*y2) + y2
> Dessutom måste jag lösa hur jag skall pulsbreddsmodulera vågformer.
Ett intressant sett att "pulsbreddsmodulera" på är att ha två stycken oscillatorer som genererar samma vågform, men med olika offset.
Sen subtraherar eller multiplicerar du dessa. Genom att modulera offseten kan du åstadkomma något som liknar pulsbreddsmodulering.
(Idén är tagen från Reasons Subtractor, de kallar detta för Phase Offset Modulation, jag har implementerat en variant av detta i min FPGA-synth).