Hallo Leute,
ich bin gerade dabei mit einem ATmega 328P ein Projekt mit Sprachausgabe
zu programmieren. Die Soundfiles werden von einer SD-Karte gelesen und
sollen über PWM an einen Lautsprecher ausgegeben werden. Es wird immer
ein Block mit 512Bytes von der SD-Karte gelesen und abwechselnd in
Puffer1 und Puffer2 geschrieben.
An den ATmega ist ein 8MHz Quarz angeschlossen.
Ich verwende OCR0A für die Ausgabe an den Lautsprecher. Dort habe ich
Fast PWM eingestellt und verwende keinen Prescaler. Die Init sieht
folgendermaßen aus:
1
TCCR0A&=~(1<<COM0A0);
2
TCCR0A|=(1<<COM0A1);
3
TCCR0A|=(1<<WGM00);
4
TCCR0A|=(1<<WGM01);
5
TCCR0A&=~(1<<WGM02);
6
7
TCCR0B|=(1<<CS00);
8
TCCR0B&=~(1<<CS01);
9
TCCR0B&=~(1<<CS02);
Mit OCR1A soll ein Interrupt im 44100Hz-Takt aufgerufen werden, in dem
dann die Bytes aus dem Puffer auf OCR0A geschrieben werden. Der Timer
soll in CTC Mode laufen mit einem Prescaler von 8. Hier die
Initialisierung dazu:
1
TCCR1A&=~(1<<COM1A1);
2
TCCR1A&=~(1<<COM1A0);
3
TCCR1A&=~(1<<WGM11);
4
TCCR1A&=~(1<<WGM10);
5
6
TCCR1B&=~(1<<WGM13);
7
TCCR1B|=(1<<WGM12);
8
TCCR1B&=~(1<<CS12);
9
TCCR1B|=(1<<CS11);
10
TCCR1B&=~(1<<CS10);
11
12
OCR1A=((8000000/8)/44100)-1;
13
14
TIMSK1|=(1<<OCIE1A);
15
TIFR1|=(1<<OCF1A);
Wenn eine Datei von der SD-Karte abgespielt werden soll, dann kommt es
mir so vor, als ob der Interrupt etwas zu schnell aufgerufen wird. Habe
ich
hier einen Denkfehler bei der Initialisierung der Timer?
Die Tonhöhe der Sprache ist korrekt, aber ich glaube, dass die einzelnen
Bytes zu schnell nacheinander ausgegeben werden. Hier die
Interrupt-Routine:
Wie kommst du zu dem Schluss, die Tonhöhe sei korrekt, die Bytes kämen
aber zu schnell? Mir ist nicht ganz klar, warum ein ATMega328P mit 8MHz
Takt eine PWM von 44.1kHz stemmt? So etwas um die 30kHz halte ich für
realistisch(er). Hast du vielleicht die Auflösung reduziert, um so hohe
Sample-Raten zu bewerkstelligen? (Ich vermute nicht, denn dann müsstest
du die Compare-Register neu/ anders/ überhaupt laden und den
Wave-Generator-Modus nutzen.)
>Wie kommst du zu dem Schluss, die Tonhöhe sei korrekt, die Bytes kämen aber zu
schnell?
Die Stimme ("Fetzen", die aus dem Lautsprecher kommen) der Sprachausgabe
klingt genau gleich, wie wenn ich die Dateien am PC abspiele. Es hört
sich einfach an, als ob das nächste Byte schon geschrieben wird, bevor
der Lautsprecher komplett darauf reagieren kann.
>Mir ist nicht ganz klar, warum ein ATMega328P mit 8MHz Takt eine PWM von 44.1kHz
stemmt?
Ich habe die Gleichung aus dem Datenblatt folgendermaßen umgestellt:
OCR1A = ((F_CPU / PRESCALER) / SOLLFREQUENZ) - 1
Wenn ich den Wert von OCR1A leicht erhöhe, wird die Ausgabe schneller
und die Stimme tiefer. Wenn ich den Wert niedriger einstelle, wird das
Ganze langsamer und die Stimme höher.
Bei dieser Einstellung:
OCR1A = ((F_CPU / PRESCALER) / SOLLFREQUENZ) - 1 -4
wird die Sprache tatsächlich in richtiger Geschwindigkeit und
vollständig ausgegeben, nur die Stimme ist deutlich zu hoch.
Es gibt auch schon AVR-Projekte, die mit 44,1kHz Sprachausgabe oder
Musik ausgeben können.
>Hast du vielleicht die Auflösung reduziert, um so hohe Sample-Raten zu
bewerkstelligen?
Welche Auflösung meinst Du? Außer den Zeilen oben, habe ich nichts
anderes eingestellt.
Mir fällt langsam nichts mehr ein, was ich noch ändern könnte.
Das Ganze soll offenbar eine Auflösung von 8 bit haben, zumindest ist so
ja der Timer0 eingestellt. Damit läuft er aber mit einer Frequenz von 8
MHz / 2^8 = 31250 Hz. Dass OCR0A mit 44100 Hz neu geladen wird, ändert
daran nichts und passt eben nicht.
Danke erstmal für Eure Hilfe.
Ja, die Soundfiles haben 8 bit, 44100 Hz Abtastrate und sind einkanalig
Mono.
Wenn ich versuche eine Datei mit 22050 Hz Abtastrate abzuspielen und die
Sollfrequenz entsrpechend anpasse, dann scheint es das Ganze noch
schlechter zu machen und die "Wortfetzen" werden noch kürzer.
Tobias S. schrieb:> Mir fällt langsam nichts mehr ein, was ich noch ändern könnte.
Du könntest erstmal die Tatsache ändern, dass du mit zwei Timern
hantierst. Das ist völlig überflüssig. Dein Timer für die PWM-Ausgabe
taktet mit 8000000Hz/256=31250Hz. Deine Eingabedaten sind (zumindest
nach deiner Aussage) mit einer Samplefrequenz von 44100Hz gespeichert
(hoffentlich wenigstens im korrekten Sampleformat, das müsste sein: 8Bit
unsigned).
Die einfachste Methode wäre, die Eingabe-Dateien einfach mit einem PC
und einem entsprechenden Programm auf die verfügbare Wiedergabefrequenz
von 31250Hz zu resampeln. Dann brauchst du dich nicht mit den den
Widrigkeiten der leicht gehobenen µC-Programmierung
auseinanderzusetzen...
Wenn du aber dieses Herausforderung annehmen willst, musst du einfach
nur eine DDS für die Ausgabe implementieren. Das ist wahrlich nicht so
schwer.
Der triviale Ansatz wäre mit fixed-point-Mathematik. Deine
TIMER1_COMPA-ISR müßte also bei jedem Aufruf nicht 1,0 Samples der
Quelle entnehmen, sondern 44100/31250=1,4112. Das kann man mit einem
1.7-FP-Format (also nur win Byte für den Faktor) schon recht gut
abbilden, der absolute Fehler beträgt dann ca. +0.2%, d.h.: die
Wiedergabefrequenz ist etwas zu hoch, für einen Normalsterblichen aber
wohl kaum erkennbar zu hoch...
Das Gelbe vom Ei ist DDS mit Bresenham statt FP-Mathematik. Vorteil:
zumindest im langfristigen Mittel stimmt die Wiedergabefrequenz exakt.
Nachteil: Rechenzeitaufwand größer (möglicherweise sogar deutlich
größer) als beim FP-Ansatz.
Ob der Bresenham in Frage kommt, ermittelt man durch eine
Primfaktorzerlegung der beiden Frequenzen:
44100=2^2 * 5^2 * 3^2 * 7^2
31250=2^1 * 5^6
Dann eleminiert man das, was gleich ist und betrachtet nur noch, was
ungleich ist:
44100=2^1 * 3^2 * 7^2 = 882
31250=5^4 = 625
Im konkreten Fall ziemlicher Mist, beides passt nicht in ein Byte, d.h:
man müßte hier mit 16Bit rechnen. Außerdem: der geringe Grad an
Übereinstimmung (also an eleminierbaren Primfaktoren) weist darauf hin,
dass Bresenham hier nicht wirklich vorteilhaft wäre, weil die meiste
Zeit auch nur derselbe Scheiss rauskommen würde, den auch ein Ansatz mit
FP-Arithmetik zu viel geringeren Kosten liefern könnte...
Also DDS mit FP... Ggf optimiert. 0.8 wäre in dieser Anwendung bei
entsprechender Programmierung ebenfalls möglich und würde den Fehler
halbieren...
Man muss einfach nur programmieren können, das ist alles...
Wenn dein ATmega keine weiteren Funktionen während des "PLAY" ausführen
muß, dann kannst du es auch ganz einfach mit einer Warteschleife machen.
Also, ein Byte an OCR0A übergeben, Warteschleife abwarten und das
nächste Byte an OCR0A übergeben und wieder warten und so weiter und so
fort...bis zum Ende.
Der Vorteil hierbei ist, das du dann auch jede beliebige Samplefrequenz
dir durch verändern der Warteschleife einfach einstellen kannst und
nicht abhängig bist von den Teilungsfaktoren eines Zählers.
Noch eine kleine Info, falls noch nicht so gemacht:
Ich gehe mal davon aus, du weist, das man am OCR0A Ausgangspin ein
Widerstand und Kondensator noch benötigt, um ein ordentliches Signal für
den Verstärker bzw. Lautsprecher zu bekommen. Besser wären 2 oder 3
Solcher RC Filter um alle störenden Frequenzen auszufiltern.
wenn nicht könnte auch das eine Erklärung sein, wenn es sich Schei...
anhört.
> Du könntest erstmal die Tatsache ändern, dass du mit zwei Timern> hantierst. Das ist völlig überflüssig. Dein Timer für die PWM-Ausgabe> taktet mit 8000000Hz/256=31250Hz. Deine Eingabedaten sind (zumindest> nach deiner Aussage) mit einer Samplefrequenz von 44100Hz gespeichert> (hoffentlich wenigstens im korrekten Sampleformat, das müsste sein: 8Bit> unsigned).
Der zweite Timer würde das Ganze flexibler machen was die Abtastrate der
Dateien angeht. Ich habe es jetzt auch mal mit einem Timer versucht und
gebe dabei immer bei Timer0 Overflow das nächste Byte raus aber das
Ergebnis bleibt leider das selbe. Die Abtastrate der Datei ist dann auf
31250 eingestellt.
Das Dateiformat ist 8Bit unsigned.
> Wenn dein ATmega keine weiteren Funktionen während des "PLAY" ausführen> muß, dann kannst du es auch ganz einfach mit einer Warteschleife machen.
Er muss eigentlich während des Abspielens nichts anderes machen aber es
müssen ständig von der SD-Karte neue Blöcke gelesen und in die
PlayBuffer geschrieben werden. Deshalb fällt glaube ich die Lösung mit
der Warteschleife raus.
> Noch eine kleine Info, falls noch nicht so gemacht:> Ich gehe mal davon aus, du weist, das man am OCR0A Ausgangspin ein> Widerstand und Kondensator noch benötigt, um ein ordentliches Signal für> den Verstärker bzw. Lautsprecher zu bekommen. Besser wären 2 oder 3> Solcher RC Filter um alle störenden Frequenzen auszufiltern.> wenn nicht könnte auch das eine Erklärung sein, wenn es sich Schei...> anhört.
Am Ausgang hängt eine Verstärkerschaltung mit LM386. Die Tonqualität der
Schaltung ist momentan nicht das Problem. Es kommen einfach die Daten
aus dem Ausgang nicht so, wie es eigentlich sein sollte.
> Es hört sich einfach an, als ob das nächste Byte schon geschrieben> wird, bevor der Lautsprecher komplett darauf reagieren kann.
Ich habe keine Erfahrung, wie sich so etwas anhört. Du denn? Lade doch
mal zur Veranschaulichung eine kurze Audio-Aufzeichnung hoch.
Stefan U. schrieb:>> Es hört sich einfach an, als ob das nächste Byte schon geschrieben>> wird, bevor der Lautsprecher komplett darauf reagieren kann.>> Ich habe keine Erfahrung, wie sich so etwas anhört. Du denn? Lade doch> mal zur Veranschaulichung eine kurze Audio-Aufzeichnung hoch.
Hier ist mal eine Aufnahme von der Ausgabe. Die Stimme sagt: "Diese
Datei ist nur ein Test, um die Sprachausgabe mit dem Mikrocontroller zu
prüfen. 1, 2, 3, 4, 5, 6, 7, 8, 9."
Würde mich mal interessieren was Ihr da raushören könnt.
Ich höre da gar nichts vernünftiges raus.
Weisst du was? Vereinfache mal das Programm, indem du den ganzen Teil
mit der SD Karte raus lässt.
Benutze stattdessen einen Dummy-Ton mit einer rechteck- oder
dreieck-Schwingung als Byte-Array. Das ist ja noch recht einfach. Wie
hört sich dessen Ausgabe an? Sieht sie auf einem Oszilloskop sauber aus?
> Hast du die Datei vorher auf Viren geprüft?
Ääääh nein. Ich hoffe doch, dass Windows das automatisch tut. Muss aber
zugeben, dass ich immer noch gewohnt bin, unter Linux zu arbeiten und
mir daher um Viren keinen Kopf gemacht habe.
> Benutze stattdessen einen Dummy-Ton mit einer rechteck- oder> dreieck-Schwingung als Byte-Array. Das ist ja noch recht einfach. Wie> hört sich dessen Ausgabe an? Sieht sie auf einem Oszilloskop sauber aus?
Ich hab hier leider kein Oszi zur Hand und könnte nicht prüfen, wie das
Signal aussieht, das der µC rausgibt.
> Hier mal die Hörprobe vom Recktecksignal.
Klingt gleichmäßig (was ein wichtiger Test war), aber
> Der Interrupt sollte mit 44,1 kHz aufgerufen werden> ich schreibe immer abwechselnd 0 oder 128 auf den Ausgang.
Dann hätte die Tonfrequenz 22kHz sein müssen und das kann ich nicht
hören. Deinen Ton konnte ich jedoch sehr deutlich hören. Ich schätze
2kHz.
Des weiteren solltest du den Ton in den Puffer ablegen, den du sonst für
den Lesevorgang von der SD Karte genommen hättest, um zu sehen, ob der
Zugriff auf das Array richtig abläuft.
Tobias S. schrieb:> Er muss eigentlich während des Abspielens nichts anderes machen aber es> müssen ständig von der SD-Karte neue Blöcke gelesen und in die> PlayBuffer geschrieben werden. Deshalb fällt glaube ich die Lösung mit> der Warteschleife raus.
Ok. Wenn es 44.100 kHz Samplefrequenz sein sollen dann könnte man auch
mit dem Hauptoszillator das Ganze darauf "biegen".
44.100 kHz * 256 = 11.289MHz. Also entweder einen 11.0592MHz nehmen dann
erhält man 43.200kHz, oder einen 12MHz nehmen, dann erhält man 46.875kHz
bei entsprechendem Teilungsfaktor für den Zähler der den Interrupt
auslöst. 11.0592 liegt dann näher dran. Den Unterschied zu den fehlenden
900Hz hören dann nur noch die Toningenieure. :-)
Bei meinem Projekt habe ich einen 12MHz Quarz genommen und da ist der
Unterschied bei Sprachausgaben die dann nur minimal schneller
wiedergegeben werden kaum zu bemerken.
Somit wäre die Samplefrequenz erst einmal geregelt und der ATmega dann
auch zum Einlesen weiterer Daten von der SD-Karte noch in Aktion.
Habe mal aus dem Schaltplan meines Projektes die Audioausgabe hier
angehangen. Da ist zu sehen wie man das Audiosignal gewinnen kann.
Nur zum Verständnis...
> Ok. Wenn es 44.100 kHz Samplefrequenz sein sollen dann könnte man auch> mit dem Hauptoszillator das Ganze darauf "biegen".
Es müssen nicht unbedingt 44,1 kHz sein. Morgen werde ich das Ganze mal
mit 22050 Hz Samples versuchen.
Hast du bei deinem Projekt eine SD-Karte verwendet?
Bzw würdest du mir mal deinen Code schicken, damit ich das mal mit
meinem verlgeichen kann? Bin offen für alles :)
Kann man nicht jetzt erstmal mit der halben Auflösung arbeiten, d.h. 7
bit?
Den Timer0 auf Modus 7, fast PWM mit top=OCR0A=180 für die 44.1 kHz,
stellen und die halbierten Eingangswerte auf OCR0B geben; das
Ausgangssignal liegt dann auf OC0B=PD5.
Tobias S. schrieb:> Der zweite Timer würde das Ganze flexibler machen was die Abtastrate der> Dateien angeht.
Nein. Genau darum, wie man das auch ohne zweiten Timer löst, drehte sich
mein Posting.
Tobias S. schrieb:> Hast du bei deinem Projekt eine SD-Karte verwendet?> Bzw würdest du mir mal deinen Code schicken, damit ich das mal mit> meinem verlgeichen kann? Bin offen für alles :)
Mein Projekt arbeitet mit einem externen Flash Speicher der per SPI
angesteuert wird und eine Größe von 2 Mbyte (16MBit) hat.
Ich würde dir auch den Code zur Verfügung stellen. Das Problem ist nur,
ich programmiere nicht in C sondern nur in Assembler.
Aber ich versuche mal das Ganze in Worte zu fassen.
Hier erst mal die Initialisierung des verwendeten PWM Pins OC2A.
in Register TCCR2A kommt der Wert 10000011
in Register TCCR2B kommt der Wert 00000001 (Bedeutung der einzelnen Bits
sind im Datenblatt z.B. ATmega1284 zu finden)
Dann lade ich den Mittelwert der PWM, also 127 und schreibe diesen erst
einmal auf die PWM (Register OCR2A). Somit bekomme ich eine
Mittenspannung als Analogausgang. Man möchte ja Sinus erzeugen und
negative Sinuswellen gehen halt sonnst nicht. Deshalb erst einmal auf
Halbe Ausgangsspannung setzen um eine negative Sinuswelle in den Bereich
unterhalb der Mittenspannung bis zur 0 generiert zu bekommen.
Ab hier läuft die PWM ständig im Hintergrund. Also es wird ab hier
ständig die halbe Analogspannung erzeugt. Wenn der ATmega nun einen
Befehl erhält (bei mir über TWI), ein Sample aus dem externen Flash
abzuspielen, dann wird ein Byte des abzuspielenden Samples über die SPI
aus dem Externen Flash ausgelesen und in OCR2A gespeichert. Dadurch
ändert sich nun die PWM- analog Spannung von der Mittelspannung gesehen
entweder nach unten, oder oben. Die Größe der Änderung ist dann
natürlich abhängig von dem Wert den ich in OCR2A gespeichert habe.
Jetzt kann man eine Warteschleife, oder ein Interrupt ausgelöst durch
einen weiteren Zähler (Timer Overflow) programmieren, der die
"Haltezeit" dieses einzelnen PWM Bytes beinhaltet. Die Wartezeit einer
Warteschleife, bzw. die Timer Overflow Zeit muß dann der verwendeten
Samplefrequenz bzw. der Periodenzeit der verwendeten Samplefrequenz
entsprechen.
Wenn man einen IRQ nimmt, könnte man dann auch während dieser
"Haltezeit" weitere Daten von der SD-Karte ins SRAM lesen.
Ist die Periodenzeit bzw. die "Haltezeit" des PWM mit einem bestimmten
Wert abgelaufen (Warteschleife zu ende, oder IRQ ausgelöst), wird das
nächste Byte in OCR2A geschrieben, bis der Bytezähler des Samples die 0
erreicht hat. Zum Schluss, wenn das Sample komplett ausgegeben wurde,
wird dann wieder die 127 in OCR2A geschrieben um eine Halbe
Analogspannung wieder auf der PWM (analog) Leitung zu haben.
Das ist das Ganze Prinzip wie ich es realisiert habe.
Die Samples kommen bei mir über die UART vom PC in den Externen Flash
Speicher. Bei dir wäre dann halt die SD-Karte der Samplespeicher.
Da ich nur Sprachausgabe und keine Musikausgabe mache, reicht es mir
auch vollkommen aus, wenn die Samples 8-Bit Auflösung haben.
Hoffe das hilft dir weiter. Denn es ist ganz toll, wenn ein ATmega
anfängt zu sprechen... :-)
Noch eine Idee von mir zur Sprachausgabe per ATmega. Habe aber keine
Ahnung ob das überhaupt so geht! Aber bitte entscheidet selber...
Wenn man eine Hardware hat, die ein bisschen mehr Speicherplatz zur
Verfügung stellt, könnten dann auch solche Sachen wie z.B. das Vorlesen
eines Textes, der über eine RS232 eines PC´s auf die UART des ATmega
geleitet wird, machen. Man müßte halt 26 Buchstaben + Ö,Ä,Ü usw. als
Sample im Speicher ablegen. Wenn dann in ASCII das "A" gesendet wird,
würde das Sample für "A" abgespielt werden, bei "B" das Sample für "B"
usw. Würde sich bestimmt ganz ulkig anhören...
> Aber ich versuche mal das Ganze in Worte zu fassen.> Hier erst mal die Initialisierung des verwendeten PWM Pins OC2A.> in Register TCCR2A kommt der Wert 10000011> in Register TCCR2B kommt der Wert 00000001 (Bedeutung der einzelnen Bits> sind im Datenblatt z.B. ATmega1284 zu finden)> Dann lade ich den Mittelwert der PWM, also 127 und schreibe diesen erst> einmal auf die PWM (Register OCR2A). Somit bekomme ich eine> Mittenspannung als Analogausgang. Man möchte ja Sinus erzeugen und> negative Sinuswellen gehen halt sonnst nicht. Deshalb erst einmal auf> Halbe Ausgangsspannung setzen um eine negative Sinuswelle in den Bereich> unterhalb der Mittenspannung bis zur 0 generiert zu bekommen.> Ab hier läuft die PWM ständig im Hintergrund. Also es wird ab hier> ständig die halbe Analogspannung erzeugt. Wenn der ATmega nun einen> Befehl erhält (bei mir über TWI), ein Sample aus dem externen Flash> abzuspielen, dann wird ein Byte des abzuspielenden Samples über die SPI> aus dem Externen Flash ausgelesen und in OCR2A gespeichert. Dadurch> ändert sich nun die PWM- analog Spannung von der Mittelspannung gesehen> entweder nach unten, oder oben. Die Größe der Änderung ist dann> natürlich abhängig von dem Wert den ich in OCR2A gespeichert habe.> Jetzt kann man eine Warteschleife, oder ein Interrupt ausgelöst durch> einen weiteren Zähler (Timer Overflow) programmieren, der die> "Haltezeit" dieses einzelnen PWM Bytes beinhaltet. Die Wartezeit einer> Warteschleife, bzw. die Timer Overflow Zeit muß dann der verwendeten> Samplefrequenz bzw. der Periodenzeit der verwendeten Samplefrequenz> entsprechen.
Danke für die ausführliche Antwort.
Ich habe mein Programm jetzt auch zum Laufen gebracht. Das Problem lag
nicht an den Timern, sondern an den Lesepuffern. Es wurde zum Teil der
Puffer2 nicht mit Daten befüllt und deshalb hat die Hälfte der Bytes
gefehlt. Die Soundqualität werde ich mal mit Hilfe Deiner Vorschläge
versuchen zu verbessern. Ab und zu habe ich auch noch ein leichtes
knacken in der Ausgabe.
> Noch eine Idee von mir zur Sprachausgabe per ATmega. Habe aber keine> Ahnung ob das überhaupt so geht! Aber bitte entscheidet selber...> Wenn man eine Hardware hat, die ein bisschen mehr Speicherplatz zur> Verfügung stellt, könnten dann auch solche Sachen wie z.B. das Vorlesen> eines Textes, der über eine RS232 eines PC´s auf die UART des ATmega> geleitet wird, machen. Man müßte halt 26 Buchstaben + Ö,Ä,Ü usw. als> Sample im Speicher ablegen. Wenn dann in ASCII das "A" gesendet wird,> würde das Sample für "A" abgespielt werden, bei "B" das Sample für "B"> usw. Würde sich bestimmt ganz ulkig anhören...
Ich glaube nicht, dass man da etwas verstehen würde. Die Stimme würde
mehr oder weniger das buchstabieren, was eingetippt wird.
Ich habe meine Samples in Linux mit dem Befehlszeilenprogramm pico2wave
erstellt und anschließend mit sox in das passende Format gewandelt. Die
Qualität der Sprache ist erstaunlich gut. Und wenn man die Befehle in
ein Skript packt, werden gleich alle Samples auf einmal erstellt und
gewandelt.
Tobias S. schrieb:> Ich habe meine Samples in Linux mit dem Befehlszeilenprogramm pico2wave> erstellt und anschließend mit sox in das passende Format gewandelt. Die> Qualität der Sprache ist erstaunlich gut. Und wenn man die Befehle in> ein Skript packt, werden gleich alle Samples auf einmal erstellt und> gewandelt.
Für die Erstellung der Samples verwende ich Audacity. Da ist alles drin
was man so zur Soundbearbeitung benötigt.
Da ich auch nur die Sampledaten selbst und nicht die Filekennung, Größe
usw. die ja auch sonnst mit in das File abgespeichert wird (z.B. WAV),
speichere ich das Endprodukt als RAW File. Dann habe ich wirklich nur
die reinen Sounddaten und kann dann einfach die erstellte RAW-Datei Byte
für Byte in den Flash Speicher schieben.
Freut mich wenn es jetzt funzt. :-)