Libraries 1: Sound mit fmod


Einleitung
Sound: Warum überhaupt eine Bibliothek?
Sounderzeugung auf einem modernen PC
Welche Lib?
Erste Schritte
Woher Sounds nehmen?
Ritterspiel mit Sound
Weitere Anregungen
Sound-Synthese

Einleitung

Es gibt beim Programmierenlernen drei Schritte:

  1. Erlernen der Programmiertools (Editor, Debugger, Compiler)
  2. Erlernen der Sprache
  3. Erlernen der Libs

Notiz am Rande: Diese drei Schritte durchläuft man keineswegs nur einmal in seinem Programmiererleben. Steigen Sie z.B. jetzt in die .NET oder Java-Programmierung ein, dann geht's erstmal wieder bei Schritt 1. los... Immerhin gibts immer mehr Bemühungen, gemeinsame Entwicklungsumgebungen (IDE's) für ein grosses Spektrum an Sprachen und Libs zu schaffen. Das gilt sowohl für das Visual-Tool von Microsoft (für C#, C++, J#, Visual Basic und viele andere Sprachen) als auch für Eclipse (das inzwischen für mehrere Dutzende Sprachen anwendbar ist) oder KDevelop. Und für Emacs und gdb galt es sowieso schon immer.

Wie dem auch sei: Bei Freebasic sind wir jetzt mit Schritt 2. weithingehend durch. Kommen wir zu Schritt 3.

Freebasic hat Bindings zu sehr vielen Libs. Ein solches Binding zu erstellen, kostet nicht viel Arbeit und kann von jedem fortgeschrittenen Programmierer selbst erstellt werden, so dass man prinzipiell aus Freebasic heraus alles nutzen kann, was in Form einer dynamischen Bibliothek für Windows (dll) oder Linux (so) verfügbar ist.

Eine andere Frage ist allerdings, wie man es benutzt. Gerade weil es soviele zugängliche Libs gibt, ist oft nur sehr wenig Dokumentation zur jeweiligen Libs speziell für Freebasic zu finden.

Wenn ich also etwas haben möchte, spezielle Mathefunktionen, spezielle Spielefunktionen, ein Windows-GUI, Sound etc.., dann habe ich die folgenden Fragen:

  1. Welche Lib deckt meinen Bedarf ab?
  2. Wo bekomme ich sie her und kostet sie etwas?
  3. Gibt es schon ein Binding dafür?
  4. Gibt es Dokumentation?
  5. Gibt es Freebasic-spezifische Dokumentation?

Die letzte Frage lautet meist "Nein". So läuft unser Vorhaben hier darauf hinaus, zu lernen, wie man die allgemeine (und meist sehr gute) Dokumentation zu einer Lib, deren Beispiele und Syntax meistens auf der Sprache C++ beruht, für unsere Zwecke auswerten kann.

Bei Punkt 2. gilt übrigens in den meisten Fällen: Es kostet nichts, solange der Gebrauch auf das Private beschränkt bleibt. Oft kostet es auch dann nichts, wenn man selbst seine Programme unter die gleiche Lizenz stellt wie die Lib. Schwierig wird es meist bei kommerziellem Gebrauch. Manche Libs lassen dies zu, die meisten jedoch nicht. Aber das dürfte hier auch nicht das Thema sein.

Die nächsten Kapitel wollen also nicht einfach eine Kurzeinführung in die jeweilige Lib geben. Sondern sie wollen mehr vermitteln, wie man sich selbst in neue Libs einarbeitet.

Sound: Warum überhaupt eine Bibliothek?

Ich habe gleich am Anfang den Sound gewählt, weil Sie ihn sicher schon vermissen. Grafik haben wir ja schon zur Genüge (zumindest in 2D). Und in QBASIC hatten wir ja mit dem PLAY-Befehl auch schon Sound. Warum verflixt nochmal gibt's den PLAY-Befehl nicht in der gfx-Lib?

Dazu braucht es einen kleinen (aber wirklich nur sehr kleinen!) Rückblick in die Geschichte. QBASIC stammt aus dem Ende der 80er-Jahre. Damals hatten PC's keine s.g. Soundkarte. Sondern nur den PC-Speaker (den heutige PC's oft gar nicht mehr haben.) Dieser kann lediglich einzelne Piepstöne in verschiedenen Höhen von sich geben. Da damals PC's ausschliesslich für Büros gedacht waren, reichte das auch. Und der PLAY-Befehl war völlig hinreichend, diesen kläglichen Piepser anzusteuern.

Ungefähr um 1990 herum wurden PC's so billig, dass sie auch für Privatzwecke interessant wurden - trotz ihrer vergleichsweise jämmerlichen Grafik- (siehe CGA-Grafik aus dem Vorkapitel!) und "Sound"-Fähigkeiten. Was Spieleprogrammierer sich einen abbrachen, aus dem Speaker so etwas wie Musik herauszuquetschen war grausam, was dann allerdings dabei herauskam, war oft noch viel grausamer und grenzte an zeitgenössische experimentelle Musik im atonalen Raum. Während sich die Grafik über EGA zu VGA allerdings sehr schnell verbesserte, blieb der Sound im Jammerzustand und daher gab es auch bei QBASIC keine Änderung am PLAY-Befehl.

Erst um 1993 herum änderte sich die Situation langsam, als s.g. Soundkarten auf den Markt kamen und langsam auch erschwinglich wurden. Relativ schnell wurden sie sogar serienmässig auf der Hauptplatine integriert. Zu der Zeit wurde QBASIC aber schon nicht mehr weiterentwickelt.

Im Endergebnis machte es für Freebasic viel Sinn, an die VGA-Grafikfähigkeiten von QBASIC anzuknüpfen, aber mit dem PLAY-Befehl kann man einfach keinen Staat machen und auf Freebasic übertragene Altspiele sollte man tunlichst ohne Speaker-Sound spielen.

Sounderzeugung auf einem modernen PC

Man muss denkbar wenig wissen, um guten Sound auf einem PC zu erzeugen. Der Weg dazu ist allerdings auch ein ganz anderer als früher. Heute nimmt man sich einfach fertige Sounds. Die kann man sich aus dem Internet runterladen oder man nimmt sie sich einfach selbst mit einem Aufnahmegerät und Mikro auf. Meist muss man sie für seinen Zweck noch bearbeiten, daher ist ein Audioeditor sehr empfehlenswert.

Die Sounderzeugung besteht dann nur darin, an der passenden Stelle eine fertige Aufnahme wieder abzuspielen. Wie dieser Sound erzeugt wurde, kümmert den Programmierer meist wenig.

Wichtige Sounddatei-Formate:

Daneben gibt es noch s.g. mod's, diese sind eigentlich kein Soundformat, sondern das Ergebnis von s.g. Sequencern oder "Sound-Trakkern", mit denen Sound synthetisch am PC hergestellt werden kann. Aber auch sowas kann man sich im Internet runterladen und als Sound benutzen. In der Programmiererszene sind hauptsächlich wav-Dateien (oder mod's) üblich, für längere Musikpassagen auch einmal MP3.

Was wir also in erster Linie brauchen, ist also eine Lib, die uns genau einen Befehl zur Verfügung stellt: "Play diese Sounddatei!".

Daneben wollen wir allerdings weiter hinten auch mal in die Sounderzeugung reinschnuppern, die mit dem heutigen Soundchip-Standard (s.g. Soundblaster-Standard) möglich ist.

Welche Lib?

  1. Wikipedia: Als Ausgangspunkt nehmen wir uns einmal das wav-Format. Das wollen wir ja abspielen. Und wir gehen mal wieder in die allgemeine wikipedia und schauen unter "wav" nach. Ein langer technischer Artikel, den wir nicht durchlesen müssen. Aber unter den Weblinks kommt ein Link zur "Audioprogrammierung". Wow! Das ist das Stichwort, nach dem wir googeln!
  2. Google: Es gibt massenhaft Funde. Gleich einer der ersten führt uns in ein pdf "Audioprogammierung unter Windows mit C++ für Computerspiele". Dem Link http://turing.fh-landshut.de/~jamann nach zu urteilen von einem Herrn Jamann.
  3. Schmökern: Schon auf der ersten Seite erfahren wir viele wichtige Dinge, z.B. dass es drei Wege gibt, zum Sound zu kommen: Der Artikel selbst wendet sich DirectSound zu. So liegt der Gedanke nahe, sich das näher anzuschauen.
  4. Examples: So schnell schiessen wir allerdings nicht. Wir haben jetzt drei Möglichkeiten. Nun sollten wir erstmal im Freebasic-Ordner nachschauen, zu welchen dieser Libs Beispielcode dort existiert. Gibt es nämlich Beispielcode, ist das schon mehr als die halbe Miete.
    In examples/sound finden wir:
    bassmod_test.bas
    bass_test.bas
    dne_trtn.mod
    fmodtest.bas
    openaltest.bas
    playmp3.bas
    
  5. Lib's herunterladen: Das ist ne Menge. Nun liegt es nahe, die Testprogramme gleich zu kompilieren und auszuführen. Kompilieren wird gehen. Aber Ausführen vermutlich nicht. Grund: Wir haben die Lib selbst, entsprechende DLL ja noch nicht! Nur die Bindung und das Beispielprogramm. Prinzipiell und praktisch läuft es jetzt auf folgendes hinaus: Man muss beim Download nur Aufpassen, eine Version runterzuladen, die zum Freebasic-Bindung passt. Bei Fmod z.B. passen nur die 3.x-Versionen, nicht aber die 4.x!
  6. So, ein Ergebnis haben wir schon mal: Kein Beispielcode für Directsound, also lassen wir das mal lieber vorerst.
  7. Weiter googeln Es schadet allerdings nicht, noch ein bisschen unsere Funde weiter anzuschauen. Da wäre bei mir z.B. der dritte Link. Der führt direkt ins Freebasic-Forum. Dort ist viel von Fmod die Rede. Fmod und Freebasic scheinen ein recht vertrautes Pärchen zu sein.
  8. Foren anschauen Wenn man freebasic.net durchwühlt, erhält man schnell noch einen anderen Fund und Link auf das etwas unbekanntere Freebasic-Forum vom D.J.Peters. Letzterer hat selbst eine Soundlib namens FBSound programmiert. Habe sie bisher nicht ausprobiert, aber dies wird wahrscheinlich sogar die einfachste Möglichkeit sein. Ich verfolge sie aus didaktischen Gründen hier nicht weiter: Wir wollen ja lernen, wie man sich in eine "grosse" Lib einarbeitet, die nicht extra für Freebasic erstellt wurde.

Es führen also viele Wege nach Rom. Ich nahm dann Fmod hauptsächlich deswegen, weil ich hier auch relativ einfach den zweiten Teil unseres Vorhabens abdecken konnte: Die synthetische Sounderzeugung.

Erste Schritte

Das Testprogramm

Der erste Schritt war ja das Testprogramm fmodtest.bas. Schauen wir uns dieses mal an:


'Simple FMOD test for FB
'by Plasma  [11-16-2004]

option EXPLICIT
#INCLUDE ONCE "fmod.bi"

DECLARE SUB ErrorQuit( BYVAL Message AS STRING )

CONST FALSE = 0
CONST MusicFile = "dne_trtn.mod"
	
	DIM song AS FMUSIC_MODULE ptr 

	IF( FSOUND_GetVersion() < FMOD_VERSION ) THEN
  		ErrorQuit( "FMOD version " & FMOD_VERSION & " or greater required" )
	END IF

	IF( FSOUND_Init(44100, 32, 0) = FALSE ) THEN
  		ErrorQuit( "Can't initialize FMOD" )
	END IF

	song = FMUSIC_LoadSong( MusicFile )
	IF song = 0 THEN
  		ErrorQuit( "Can't load music file """ + MusicFile + """" )
	END IF

	FMUSIC_PlaySong( song )

	PRINT "Press any key to exit..."
	SLEEP

	FMUSIC_FreeSong( song )
	FSOUND_Close

	END


SUB ErrorQuit( BYVAL Message AS STRING )

  PRINT "ERROR: "; Message
  FSOUND_Close
  END 1

END SUB

Da wird zunächst ein spezieller Zeiger deklariert. Einfach zu verstehen: Der Zeiger auf den Speicherplatz der Sounddaten. Macht Sinn.

Dann kommt eine Versionsprüfung. Klar. Dann eine Initialisierung. Die Parameter 44100,32,0 verstehen wir zwar nicht, aber das stört uns nicht. Nehmen wir einfach so.

Nun geht's zu Sache: Lade die Daten in den Speicher: FMUSIC_LoadSong(filename). Wirklich nicht schwer zu verstehen.

Und noch einfacher: Bei FMUSIC_PlaySong(song) wird der Klang abgespielt. Einfacher geht's nicht. Anschliessend wird der Speicher wieder freigegeben und die Lib geschlossen.

Einziger Haken: FMUSIC_PlaySong() spielt eine mod-Datei ab. Kann fmod nur mod? Ein kurzer Blick auf die Homepage von fmod zeigt: Fmod kann so gut wie alles: wav, mp3, mod und und und.... (aber kein wma!) Also probieren wir doch einfach mal in FMUSIC_LoadSong() eine wav-Datei aus! Falls Sie keine ad hoc im Internet finden, dann holen Sie sich einfach eine der Windows-Klangdateien aus C:\windows\media!

Ein Blick in die API-Dokumentation...

Tja, Pech gehabt. "Can't load music file...". So einfach war's auch wieder nicht. Also googeln wir mal nach Fmod und Freebasic. Relativ schnell werden wir da auf eine Seite auf www.gpwiki.org stossen, dem Spielerprogrammierer-Wiki. Die benutzen dort eine andere Befehlsfamilie namens FSOUND. Was ist der Unterschied zwischen beiden? Hier hilft es nun, mal in die Original-Doku von Fmod zu schauen, die als Windows-Hilfe (CHM) bei der Installation von fmod auf Platte kopiert wird. (Fmod-Verzeichnis ==> documentation). Unter Tutorials/The basics finden wir im unteren Teil: "Samples, Streams and Songs". Und dort wird genau erklärt, was man für was braucht. FMUSIC ist für "sequenced" Files - das ist wav auf keinen Fall. Falscher Eingang. Also brauchen wir FSOUND. Und da wird auch gleich gesagt, welche Abspiel-Routinen wir benutzen können: FSOUND_PlaySound() sieht gut aus. Fast so unkompliziert wie FMUSIC_PlaySong. Geladen wird der Sound dieses Mal mit FSOUND_Sample_Load(). Das sieht leider etwas komplizierter aus. Macht aber nichts! Denn hier haben wir ja ein Beispiel auf der gpwiki-Seite!

Umschreiben des Testprogramms auf FSOUND

  1. Versionskontrolle und Initialisierung: Offenbar nichts zu ändern.
  2. Zeigertyp: Hier brauchen wir was anderes. Nehmen wir auch aus dem gpwiki-Beispiel: DIM sound AS FSOUND_SAMPLE ptr.
    Kleine Anmerkung: Da stand früher ein Ptr auf einen Integer. Als ich das ausprobierte, hat's nicht funktioniert. Daraufhin habe ich in der API nachgeschaut, was gebraucht wird - und siehe da, jetzt hat's geklappt! Anschliessend habe ich den Fehler auf der Wiki-Seite korrigiert..
  3. Laden des Sounds auch aus dem gpwiki: sound = FSOUND_Sample_Load(FSOUND_FREE,"sound.wav", FSOUND_HW3D, 0, 0). Filename ist klar, den Rest verstehen wir nicht, aber was soll's. Erstmal Testen.
  4. So, jetzt kommt eine Differenz: Im gpwiki verwenden sie zum Abspielen FSOUND_PlaySoundEx(). In der API lesen wir, dass das die erweiterte Version von FSOUND_PlaySound() wäre. Pfui, weg damit, das brauchen wir nicht. Also nehmen wir hier FSOUND_PlaySound(). Der Unterschied: Wir können auf die letzten beiden Parameter verzichten.

Well, neues Testprogramm:


'Simple FMOD test for FB
'by Plasma  [11-16-2004]

option EXPLICIT
#INCLUDE ONCE "fmod.bi"

DECLARE SUB ErrorQuit( BYVAL Message AS STRING )

CONST FALSE = 0
CONST SoundFile = "intro2.wav"
	
	DIM sound AS FSOUND_SAMPLE PTR

	IF( FSOUND_GetVersion() < FMOD_VERSION ) THEN
  		ErrorQuit( "FMOD version " & FMOD_VERSION & " or greater required" )
	END IF

	IF( FSOUND_Init(44100, 32, 0) = FALSE ) THEN
  		ErrorQuit( "Can't initialize FMOD" )
	END IF

	sound = FSOUND_Sample_Load(FSOUND_FREE,SoundFile, FSOUND_HW3D, 0, 0)
	IF sound = 0 THEN
  		ErrorQuit( "Can't load sound file """ + SoundFile + """" )
	END IF

	FSOUND_PlaySound(FSOUND_FREE,sound)

	PRINT "Press any key to exit..."
	SLEEP

	FSOUND_Close

	END


SUB ErrorQuit( BYVAL Message AS STRING )

  PRINT "ERROR: "; Message
  FSOUND_Close
  END 1

END SUB


Kein Erfolg? Möglich. Sehr möglich. Wir lernen etwas draus: Auch Beispiele im Internet sind nicht immer die besten Beispiele. Es wird von Ihrer wav-Datei abhängen, ob es klappt. Als ich das jetzt für's Schreiben durchexerzierte, hat es mit manchen wav-Dateien funktioniert und mit manchen nicht. Ich bin so vorgegangen, dass ich mir nochmal alle Parameter beim Einladen des Files anschaute. Es gibt neben dem Filenamen eigentlich nur noch einen relevanten: FSOUND_HW3D. In der API steht dazu, dass wir Fmod anweisen, zu versuchen, hier eine 3D-Audio-Beschleunigung zu realisieren. Na, so ein Quatsch. Das kann mit so einem einfachen wav kaum hinhauen. Weiter oben steht: "FSOUND_STEREO" für Stereo-Samples. Na bitte, das klingt schon viel vernünftiger! Rein damit - und siehe da, jetzt klappt's immer!

Weitere Funktionen, z.B. Sound-Loop

Mit diesen "Basics" haben wir sicher schon viel an Bedarf abgedeckt. Aber es mag durchaus sein, dass plötzlich eine weitere Funktion gewünscht wird. Wir wollen z.B., dass ein kurzer Musikclip immer wieder abgespielt wird, in einer Schleife. Mit einer Freebasic-Schleife kommen wir hier nicht weit. Das FB-Programm gibt ja nur den "Startschuss" für den Sound, ab da wird er parallel und unabhängig vom Programm abgespielt und das Programm bekommt auch keine Information, wann der Sound zuende ist. Meist sehr praktisch, da Sound nicht zu einer Verzögerung im Programmablauf führt. Aber eine Soundloop können wir so natürlich nicht bauen!

In solchen Fällen hilft das Durchstöbern der Fmod-API. Fmod ist eine riesige Fundgrube - was immer man mit Sound anstellen möchte - bis hin zur Spektralanalyse - Fmod hat etwas dafür. Als ich nach der Loop suchte, bin ich erstmal zum Eintrag "FSOUND_PlaySound" gegangen. Nicht weit unterhalb fand ich den Eintrag "FSOUND_SetLoopMode" - et voila! Noch ein bisschen Rumprobieren - aha, dieser Befehl muss nach dem Start des Sounds abgeschickt werden und es empfiehlt sich, als Channel-Angabe FSOUND_ALL zu verwenden. So kommt man auch mit vielen anderen Fragen schnell weiter, z.B. wie man die Lautstärke eines Sounds verändert etc.

Woher Sounds nehmen?

So einfach der Einbau von Sound nun in unsere Programme sein mag - woher bekommen wir sie? Nun, im Internet trifft man schnell auf umfangreiche Soundsammlungen. Allerdings habe ich festgestellt, dass sie für die meisten Zwecke zu lang sind. Für unser Ritterspiel allemal. Es ist also notwendig, sie anzupassen. Dies macht man am Besten mit einem Audioeditor. Der Standard bei den kostenlosen ist hier Audacity. Ich benutze Diamond Cut Magic Audio, das es mal auf einer CD kostenlos gab.

Mit so einem Editor kann man sich eine beliebige wav-Datei in die Zange nehmen. Auf der Suche nach Schwertkampf-Sound wurde ich übrigens nicht im Internet, sondern in den Sounddateien des Spiels "Civilization 2" von Microprose fündig, das ich noch aus früheren Zeiten auf der Platte hatte. (Gibt's inzwischen als freien Download.)

Liegt ein Sound im MP3-Format vor, konvertierte ich unter Windows mit CDEx von .mp3 nach .wav.

Es gibt nur noch eins zu sagen: Beim Testen von Sounds immer auf die Lautstärkeregler achten! Vor allem, wenn man mit Kopfhörer arbeitet. Man kann sich sein Gehör schnell mal schädigen, wenn man Sounds unbedacht bei voller Lautstärke durch den Ausgang pustet.


Ritterspiel mit Sound

oma_rit2.zip (1,6 MB) runterladen und ins gleiche Verzeichnis wie oma_rit1 expandieren. Die kleineren wav-Files waren ja schon in der ersten Version mit dabei. oma_rit2.exe starten.

Und? Man wird schon viel nervöser mit dem Pferdegetrappel...Übrigens hab ich' selbst bisher nicht geschafft, den Magenta-Jungs zu entkommen...


Weitere Anregungen

Übung 2: Bauen Sie einen Highscore ein.

Übung 2:Bauen Sie einen Flipperautomat, bei dem jeder Kollisionspunkt einen anderen Sound erzeugt!

Übung 3:Programmieren Sie sich Ihren eigenen MP3-Player!


Sound-Synthese

Grundlagen: Einfache Sinuswellen

Unser Ohr ist so gebaut, dass es zwischen "Geräuschen" und "Klängen" unterscheidet. Ein "Klang" ist eine Schallwelle, die - etwas vereinfacht gesagt - aus Sinuswellen aufgebaut ist. Typischerweise ist ein Klang aus mehreren Tönen zusammengesetzt und ein Ton aus ganz bestimmten Sinuswellen. Daher ist der "Urvater" eines jeden Klangs die Sinuswelle - die wir ja von unserem guten alten PC-Speaker schon kennen.

Grundlagen: Überlagerung von Sinuswellen

In diesem Diagramm sehen wir das Ergebnis, wenn wir zwei Sinuswellen verschiedener Frequenz addieren. Man sagt dazu auch "Überlagern". Wenn unser Ton also nicht nur so ein unangenehmer öder Sinuston sein, sondern eher etwas von einer Flöte, Geige oder einer Trompete annehmen soll, dann müssen wir Sinuswellen höherer Frequenz auf die Grundkurve draufaddieren. Allerdings nicht beliebiger Frequenz: Damit es ein Klang bleibt, müssen diese zusätzlichen Wellen ganzzahlig vielfache Frequenzen der Grundwelle haben, also sin(2x), sin(3x) usw... Man nennt das "Oberschwingungen". Dabei muss die "Höhe" der jeweiligen zusätzlichen Oberschwingung , die s.g. Amplitude., nicht zwangweise gleich der Grundwelle sein - im Gegenteil. Die Wahl der Amplituden der Oberschwingungen macht den Charakter unseres Klangs aus.

Und was ist, wenn wir Schwingungen überlagern, die nicht ganzzahlig Vielfache zueinander sein? Bien, dann nimmt das das Ohr als verschiedene Klänge wahr. Auf diese Art und Weise können wir durch Überlagerung mehrstimmige Musik erzeugen.

Grundlagen: PCM-Kodierung

Das "Problem" mit moderner Soundsynthese ist, dass man nicht wie bei alten Computern der 80er-Jahre nur ein paar verschiedene, fest durchnumerierte Klangfarben einstellen kann. Sondern man muss sich seinen Sound eben selbst aus den Klängen und jeden einzelnen Klang aus den einzelnen Oberschwingungen zusammensetzen. Das macht man am einfachsten mit der s.g. PCM-Kodierung, "Pulse Code Modulation" - ich nehme an, dass an dieser Stelle ein gewisses tieferes Verständnis dafür eintritt, warum es keine gute Idee ist, für Programmierzwecke Sounds so immer neu zusammenzusetzen, sondern die Wahl eines wav-Files etwas praktikabler wirkt...

Bei PCM wird die Schwingung in einzelne Zeitscheibchen zerlegt. Und die mittlere Amplitude jedes Scheibchens wird als Zahl gespeichert. Dabei ist natürlich wichtig,

Aus diesen Parametern bestimmt sich die effektive Frequenz meines Klangs. Will ich z.B. 1 sec. des Klangs kodieren und der Ton soll 400 Hz haben, muss ich 400 Grundschwingungen kodieren. Bei 4000 Bytes Buffergrösse und 1 Byte pro Scheibchen (8-Bit-Sound) kann ich jede Schwingung in 10 Scheibchen teilen. Verdopple ich nun Samplingrate pro Schwingung bei gleichbleibendem Buffer auf 20, kann die dargestellte Schwingung natürlich nur noch eine halb so lange Zeitdauer darstellen, nämlich eine halbe Sekunde. Oder die Grundfrequenz beträgt nur noch 220 Hz.

Damit haben wir die Kodierungsformel für eine einfache Sinusschwingung beieinander:

buf[i]=int(sin(i/samplingrate*freq*2*PI)*127)

Umsetzung in fmod

Es war relativ leicht, herauszufinden, wie man in fmod wav's abspielt. Aber es hat mich ziemlich viel Zeit gekostet, herauszufinden, wie man damit selbst Klänge synthetisiert. Siehe die entsprechende Anfrage im Freebasic.de-Forum und der dort verlinkte freebasic.net-Thread.

Das Prinzip ist schliesslich gar nicht so schwer. Man muss sich einen s.g. stream definieren. Dessen Struktur ist wahrscheinlich recht kompliziert, aber das übernimmt auch fmod selbst, man muss der Lib nur eine "Stelle" übergeben, an der sie die Kodierung vornehmen kann. Und solche "Stellen" übergeben wir mittels Callback-Funktionen, also Zeiger auf selbst definierte Funktionen, die die Lib dann verwenden kann.

Innerhalb dieser callback-Funktion beschreiben wir den Speicherplatz an einer Adresse, die wir von der Lib gesagt bekommen, mit unserer PCM-Kodierung. Die Lib liest das auch und erzeugt den gewünschten Stream, den wir dann mittels FSOUND_Stream_Play() starten und mittels FSOUND_Stream_Stop() stoppen.

Erster Sinuston


'-------------------------------------------------------

FUNCTION streamcallback(BYVAL stream AS FSOUND_STREAM PTR, BYVAL buff AS ANY PTR, BYVAL LEN0 AS INTEGER, BYVAL param AS ANY PTR) AS BYTE

  DIM buf2 AS SHORT PTR = buff  'Short = 16 bit integer representing 16 bit amplitude resolution
  DIM i AS INTEGER
  DIM freq AS DOUBLE=440.0
  DIM samplingrate AS INTEGER=4096
  DIM buflength AS INTEGER=INT(LEN0/2)            'Length of buff in sample points

  FOR i=0 TO buflength-1
    buf2[i]=INT(SIN(i/samplingrate*freq*2*PI)*32000)
  NEXT i

  streamcallback=1

END FUNCTION


'-------------------------------------------------------



DIM sound AS FSOUND_SAMPLE PTR
DIM mystream AS FSOUND_STREAM PTR
DIM userdata AS INTEGER PTR

IF FSOUND_GetVersion <= FMOD_VERSION THEN
  PRINT "FMOD version " + STR$(FMOD_VERSION) + " or greater required"
  INPUT a
  END
END IF

IF FSOUND_Init(44100, 32, 0) = FALSE THEN
  PRINT "Can't initialize FMOD"
  INPUT a
  END
END IF

mystream=FSOUND_Stream_Create(@streamcallback,4096,FSOUND_16BITS OR FSOUND_MONO OR FSOUND_SIGNED,4096,userdata)

FSOUND_Stream_Play(FSOUND_FREE, mystream)
SLEEP
FSOUND_Stream_Stop(mystream)

FSOUND_Stream_Close(mystream)
FSOUND_Close()

Übung: Eigener Synthesizer

Versucnen Sie mal folgende Callback-Funktion:


'-------------------------------------------------------

FUNCTION streamcallback(BYVAL stream AS FSOUND_STREAM PTR, BYVAL buff AS ANY PTR, BYVAL LEN0 AS INTEGER, BYVAL param AS ANY PTR) AS BYTE

  DIM buf2 AS SHORT PTR = buff  'Short = 16 bit integer representing 16 bit amplitude resolution
  DIM i AS INTEGER
  DIM freq AS DOUBLE=220.0
  DIM samplingrate AS INTEGER=4096
  DIM buflength AS INTEGER=INT(LEN0/2)            'Length of buff in sample points

  FOR i=0 TO buflength-1
    buf2[i]=INT(( SIN(1*i/samplingrate*freq*2*PI) _
                 +SIN(2*i/samplingrate*freq*2*PI) _
                 +SIN(3*i/samplingrate*freq*2*PI) _
                 +SIN(5*i/samplingrate*freq*2*PI) _
                 +SIN(7*i/samplingrate*freq*2*PI) _
                 +SIN(9*i/samplingrate*freq*2*PI) _
                 +2*SIN(10*i/samplingrate*freq*2*PI) _
                 +2*SIN(1.5*i/samplingrate*freq*2*PI) _
                 +2*SIN(1.25*i/samplingrate*freq*2*PI) _
                 +2*SIN(2.5*i/samplingrate*freq*2*PI) _
                 )*3000)
  NEXT i

  streamcallback=1

END FUNCTION

Das ist nun der allererste Anfang, sich ein Programm zu schreiben, in das man das s.g. Spektrum, das heisst, die Amplituden der einzelnen Oberschwingungen eingibt und das einem dann mit diesem Klang z.B. ein Lied vorspielt. Die Aufgabe ist allerdings nicht so ganz einfach, da man schon bei den ersten Experimenten bemerkt, dass es nicht damit getan ist, einfach nur die Oberschwingungen aufzuaddieren. Ausserdem fehlt für eine gute Klangsynthese noch die Hüllkurve, damit der Klang eine Dynamik bekommt (z.B. wie das Zupfen einer Saite). Genug zu tun, aber prinzipiell und praktisch mit fmod lösbar.