Ich bin gerade dabei einen kleinen Synthesizer zu basteln, den man
irgendwann dann auch mal komfortabel in kleine PureBasic-Projekte
einbinden kann und ein bisschen Musik machen kann.
Den kompletten Quellcode gibt es jetzt noch nicht, weil das ganze noch
lange nicht fertig ist. Aber ein kleiner Vorgeschmack als EXE-Datei und
externer fmodex.dll gibt es hier:
- fmodex.dll
- MSS.exe
Einfach beides in einen Ordner herunterladen und starten. Es öffnet sich
ein Mausklavier, ein Debug-Fenster ohne Debug-Informationen und ein
schwarzes Fenster. Beim Drücken einer Taste sollte dann ein Ton ertönen
und im scharzen Fenster die Welle angezeigt werden. Mit der Tastatur geht
es übrigens auch.
Das Signal ist ein Rechtecksignal (Square), das mit einem Dreiecksignal
(Triangle) amplitudenmoduliert und einem Sinussignal (Sine)
frequenzmoduliert ist. Die Frequenz der Amplitudenmodulation liegt bei
einem Hertz. Die Frequenz der Frequenzmodulation liegt bei 3 Hertz und
ihre Amplitude ist abhängig von der Frequenz des Rechtecksignals mit dem
Multiplikator 0,05.
Soviel zu dem Klang. Hier noch ein Code-Ausschnitt der Main.pb des
Programmes.
Code: Alles auswählen
Procedure AmplitudeCallback(time.d, Amplitude.d, Velocity.d, *result.Double)
EndProcedure
Procedure.d FrequencyCallback(time.d, Frequency.d, inFrequency.d, Velocity.d)
EndProcedure
Procedure EnumMixerInputs(*Wave.MSS_WaveForm)
ProcedureReturn #True
EndProcedure
Macro Quote
"
EndMacro
Macro DebugVar(a)
Debug Quote#a#Quote + ": " + Str(a)
EndMacro
Macro OK(a)
Debug Quote#a#Quote
Result = a
If Result
Debug FMOD_ErrorString(Result)
End
EndIf
EndMacro
Define *IS.ImageScreen, *Keys.Keys
Define Width.l = 1000, Height.l = 400
Define Samplerate.l, Channels.l, exinfo.FMOD_CREATESOUNDEXINFO
Global Dim OnNotes.b(127), Dim NoteSample.l(127), Dim *RefreshOut(127), Dim *FMODOut(127)
Global *Wave.MSS_WaveForm, *Mix.MSS_Mixer
Samplerate = 44100
Channels = 1
Procedure KeyCallback(*Keys.Keys, Note.b, Velocity.b, UserData.l)
OnNotes(Note) = Velocity
NoteSample(Note) = 0
EndProcedure
DataSection ;{ Tasten
Tasten:
Data.l 25 ;Anzahl aufeinander folgender Noten
Data.l #VK_Y, #VK_S, #VK_X, #VK_D, #VK_C, #VK_V, #VK_G, #VK_B, #VK_H, #VK_N, #VK_J, #VK_M
Data.l #VK_Q, #VK_2, #VK_W, #VK_3, #VK_E, #VK_R, #VK_5, #VK_T, #VK_6, #VK_Z, #VK_7, #VK_U, #VK_I
Data.l -1, -1 ;eins höher/tiefer (-1 = ignorieren)
Data.l #VK_UP, #VK_DOWN ;eine oktave höher/tiefer
EndDataSection ;}
Procedure RefreshWindow(*IS.ImageScreen)
Protected x.l, Width.l = *IS\Width(), Height.l = *IS\Height(), yoff.l = Height / 2, oldy.l = yoff, y.d, a.l
Protected Input.MSS_Input
Static *Out
If *Out = 0 : *Out = *Wave\NewOut() : EndIf
*IS\StartDraw()
Box(0, 0, Width, Height, 0)
For xl = 0 To Width
y = 0
For a = 0 To 127
If OnNotes(a) > 0
Input\Velocity = OnNotes(a) / 127
Input\Frequency = 220 * Pow(2, (a - 68) / 12)
Input\sample = xl * 44100 / Width
y + *Wave\Out(@Input, *RefreshOut(a))
EndIf
Next
y * -yoff + yoff
LineXY(xl - 1, oldy, xl, y, $FFFFFF)
oldy = y
Next
*IS\StopDraw()
*IS\ShowChanges()
EndProcedure
Procedure.l Buffercallback(*Sound, *BufferPointer, length.l)
Protected float.f, *sample.Float, *sample_last, mpos.POINT, a.l
Protected Input.MSS_Input
Static *Out
Shared Channels.l, Samplerate.l
*sample_last = length + *BufferPointer
*sample = *BufferPointer
If *Out = 0 : *Out = *Wave\NewOut() : EndIf
Repeat
float = 0
For a = 0 To 127
If OnNotes(a) > 0
Input\Velocity = OnNotes(a) / 127
Input\Frequency = 440 * Pow(2, (a - 68) / 12)
Input\sample = NoteSample(a)
float + *Wave\Out(@Input, *FMODOut(a))
NoteSample(a) + 1
EndIf
Next
For offset = 1 To Channels
*sample\f = float
*sample + SizeOf(Float)
Next
Until *sample => *sample_last
ProcedureReturn #FMOD_OK
EndProcedure
;{- Wave-Fenster und Keyboard
*IS = IS_New()
*IS\Open(0, 0, Width, Height, "ModuleSoundSystem", 0, #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
*Keys = Keys_New("Mausklavier")
*Keys\Resize(300, 0, 600, 40)
*Keys\AutoAspectRatio(#True)
*Keys\Hide(#False)
*Keys\Callback(@KeyCallback(), 0)
*Keys\SyncInput(#True)
*Keys\SetKeyboard(?Tasten)
*Keys\KeyNote(60)
;}
;{- Waveformen
Define.MSS_WaveForm *Wave1a, *Wave1b, *Wave1, *Wave4
Define.MSS_Mixer *Mix1, *Mix2
*Wave1a = MSS_WaveForm_New()
*Wave1a\Type(#MSS_WaveForm_Sine) ;Sinussignal
*Wave1a\Samplerate(Samplerate)
*Wave1a\Frequency(3) ;Frequenz
*Wave1a\FrequencyType(#MSS_Frequency_Static) ;Frequenz mit Eingangsfrequenz multiplizieren
*Wave1a\Amplitude(0.05) ;Amplitudenhöhe
*Wave1a\AmplitudeType(#MSS_Amplitude_FrequencyMult) ;Amplitudenhöhe mit Eingangsfrequenz multiplizieren
DebugVar(*Wave1a)
*Wave1b = MSS_WaveForm_New()
*Wave1b\Type(#MSS_WaveForm_Triangle) ;Sinussignal
*Wave1b\Samplerate(Samplerate)
*Wave1b\Frequency(1) ;Frequenz
*Wave1b\FrequencyType(#MSS_Frequency_Static) ;Frequenz multiplizieren mit Eingangsfrequenz
*Wave1b\Amplitude(1) ;Amplitudenhöhe
*Wave1b\AmplitudeType(#MSS_Amplitude_Static) ;Amplitudenhöhe mit Eingangslautstärke multiplizieren
*Wave1b\Phase(0.75)
DebugVar(*Wave1b)
*Wave1 = MSS_WaveForm_New()
*Wave1\Type(#MSS_WaveForm_Square) ;Sinussignal
*Wave1\Samplerate(Samplerate)
*Wave1\Frequency(1) ;Frequenz
*Wave1\FrequencyType(#MSS_Frequency_WaveFormAddI) ;Waveform addieren zu Eingangsfrequenz
*Wave1\SetFrequencyCallback(*Wave1a)
*Wave1\Amplitude(0.25) ;Amplitudenhöhe
*Wave1\AmplitudeType(#MSS_Amplitude_WaveformAddVxA) ;Amplitudenhöhe mit Eingangslautstärke multiplizieren
*Wave1\SetAmplitudeCallback(*Wave1b)
DebugVar(*Wave1)
; *Mix1 = MSS_Mixer_New()
; *Mix1\Type(#MSS_Mixing_Average) ;Durchschnittswert aus allen Eingängen nehmen
; *Mix1\Voices(128)
; ;*Mix1\Volume(0.25)
; ;*Mix1\AddInput(*Wave1b, #MSS_Input_WaveForm)
; *Mix1\AddInput(*Wave2, #MSS_Input_WaveForm)
; ;*Mix1\AddInput(*Wave3, #MSS_Input_WaveForm)
; *Mix1\AddInput(*Wave4, #MSS_Input_WaveForm)
; DebugVar(*Mix1)
*Wave = *Wave1
;}
Define a.l
For a = 0 To 127
*RefreshOut(a) = *Wave\NewOut()
*FMODOut(a) = *Wave\NewOut()
Next
;{ FMOD initialisieren und Sound starten
Init_FMOD()
OK(FMOD_System_Create(@*System)) ;System erstellen
OK(FMOD_System_Init(*System, Channels, #FMOD_INIT_NORMAL, 0)) ;System initialisieren
With exinfo
\cbSize = SizeOf(FMOD_CREATESOUNDEXINFO)
\Numchannels = Channels ;Anzahl Kanäle (Stereo)
\defaultfrequency = Samplerate ;Die Samplerate (44100)
\Format = #FMOD_SOUND_FORMAT_PCMFLOAT ;Das Format (Float)
\length = Samplerate ;Die Länge des Samples (1 Sekunde)
\decodebuffersize = Samplerate / 20 ;Die Anzahl an Samples pro Callback-Aufruf (25 ms)
\pcmreadcallback = @Buffercallback() ;Der Pointer zum Callback
EndWith
;Sample erstellen (2D-Sound, Software-Rendering, Loop)
OK(FMOD_System_CreateSound(*System, 0, #FMOD_2D | #FMOD_OPENUSER | #FMOD_SOFTWARE | #FMOD_CREATESTREAM | #FMOD_LOOP_NORMAL, @exinfo, @*Sound))
;Sound abspielen
OK(FMOD_System_PlaySound(*System, #FMOD_CHANNEL_FREE, *Sound, #False, @*channel))
;}
Repeat
EventID = WindowEvent()
*Keys\Event(EventID)
Select EventID
Case #PB_Event_CloseWindow
Break
Case 0
RefreshWindow(*IS)
Delay(10)
EndSelect
FMOD_System_Update(*System)
ForEver
;{ Sound stoppen und FMOD deinitialisieren
OK(FMOD_Channel_Stop(*channel))
OK(FMOD_Sound_Release(*Sound))
OK(FMOD_System_Release(*System))
*IS\Kill()
*Wave1\Kill()
End
;}