Audio library for FreeBasic - Features

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
hhr
Posts: 74
Joined: Nov 29, 2019 10:41

Re: Audio library for FreeBasic - Features

Post by hhr »

Thank you for the explanation.

SoundFunction.bas contains the function ADSREnvelope. For test purposes I copied it as ADSREnvelope2 into my program.
In function EnvelopeFunction.GetNext Amplitude is incremented or decremented very often and Amplitude can get greater than 1 or lesser than 0.
That is a problem of adding rounded floating-point numbers.
With this program I tried to avoid this problem.
There are three additional lines in EnvelopeFunction2.GetNext and a variable SilenceAdjust, which can be useful in the case sustain=0.

Code: Select all

#cmdline "-mt -exx"
#include "sfx.bi"
#inclib "fbsfx"

common shared __Samplerate as integer
''===================================================
type EnvelopeFunction2 extends SoundFunction
   A as integer
   D as integer
   S as integer
   R as integer
   Incr as single
   DecD as single
   DecR as single
   
   Amplitude as single
   
   declare function GetNext() as single 
end type

function EnvelopeFunction2.GetNext() as single
   IF t <= A THEN
      Amplitude+=Incr
      if Amplitude > 1 then Amplitude = 1
   ELSEIF t < D THEN
      Amplitude-=DecD
      if Amplitude < 0 then Amplitude = 0
   ELSEIF t < S THEN
   ELSEIF t < R THEN
      Amplitude-=DecR
      if Amplitude < 0 then Amplitude = 0
   END IF
   t+=1
   
   if child=0 then
      return Amplitude*2-1
   else
      return Amplitude*child->getnext
   end if
end function


function ADSREnvelope2 Overload(Attack as single, Decay as single, Sustain as single, Release as Single, Dur as single, SilenceAdjust as integer=0) as EnvelopeFunction2 ptr
   dim w as EnvelopeFunction2 ptr=new EnvelopeFunction2
   w->Duration=dur
   
   dim as single S = 1 - Attack - Decay - Release
   
   w->Incr = 1 / (Dur*__Samplerate * Attack + 1)
   w->DecD = (1 - Sustain) / (Dur*__Samplerate * Decay + 1)
   w->DecR = Sustain / (Dur*__Samplerate * Release + 1)
   
   w->A = Attack*Dur*__Samplerate - SilenceAdjust
   w->D = Decay*Dur*__Samplerate + w->A
   w->S = S*Dur*__Samplerate + w->D
   w->R = Release*Dur*__Samplerate + w->S
   
   return w
end function

function ADSREnvelope2 Overload(func as any ptr, Attack as single, Decay as single, Sustain as single, Release as Single, Dur as single, SilenceAdjust as integer=0) as EnvelopeFunction2 ptr
   dim w as EnvelopeFunction2 ptr=new EnvelopeFunction2
   w->child=func
   w->Duration=dur
   
   dim as single S = 1 - Attack - Decay - Release
   
   w->Incr = 1 / (Dur*__Samplerate * Attack + 1)
   w->DecD = (1 - Sustain) / (Dur*__Samplerate * Decay + 1)
   w->DecR = Sustain / (Dur*__Samplerate * Release + 1)
   
   w->A = Attack*Dur*__Samplerate - SilenceAdjust
   w->D = Decay*Dur*__Samplerate + w->A
   w->S = S*Dur*__Samplerate + w->D
   w->R = Release*Dur*__Samplerate + w->S
   
   return w
end function

''===================================================

dim as integer samplerate,channels,bits
samplerate=44100
channels=1
bits=16

SoundSet(samplerate,channels,bits)

''===================================================

dim as single f,duration
f=440
duration=1

dim shared as WaveHeaderType ptr wave(1 to 6),tempwave
dim as long buffersize,i
buffersize=samplerate*duration

for i=lbound(wave) to ubound(wave)
   wave(i)=CreateWave(buffersize,samplerate,channels,bits)
next i

tempwave=CreateWave(buffersize,samplerate,channels,bits)

dim as single a,d,s,r
dim as integer j
a=0.04 : d= 0.49 : s=0 : r=0.22 : j=1

Sound tempwave,0,SineWave(f),duration

Sound wave(1),0,ADSREnvelope(DSPWave(tempwave),a,d,s,r,duration),duration
Sound wave(2),0,AmplitudeModulate(DSPWave(tempwave),ADSREnvelope(a,d,s,r,duration)),duration
Sound wave(3),0,ADSREnvelope(a,d,s,r,duration),duration

Sound wave(4),0,ADSREnvelope2(DSPWave(tempwave),a,d,s,r,duration,j),duration
Sound wave(5),0,AmplitudeModulate(DSPWave(tempwave),ADSREnvelope2(a,d,s,r,duration,j)),duration
Sound wave(6),0,ADSREnvelope2(a,d,s,r,duration,j),duration

for i=lbound(wave) to ubound(wave)
   SaveWave("test" & str(i) & ".wav",wave(i))
next i

PlayWave(wave(1))
PlayWave(wave(2))
PlayWave(wave(4))
PlayWave(wave(5))

sleep

Code: Select all

'' https://www.freebasic.net/wiki/ExtLibsfx
'' https://en.wikipedia.org/wiki/Piano_key_frequencies

#cmdline "-mt -exx"
#include "sfx.bi"
#inclib "fbsfx"

common shared __Samplerate as integer
''===================================================

type EnvelopeFunction2 extends SoundFunction
   A as integer
   D as integer
   S as integer
   R as integer
   Incr as single
   DecD as single
   DecR as single
   
   Amplitude as single
   
   declare function GetNext() as single 
end type

function EnvelopeFunction2.GetNext() as single
   IF t <= A THEN
      Amplitude+=Incr
      if Amplitude > 1 then Amplitude = 1
   ELSEIF t < D THEN
      Amplitude-=DecD
      if Amplitude < 0 then Amplitude = 0
   ELSEIF t < S THEN
   ELSEIF t < R THEN
      Amplitude-=DecR
      if Amplitude < 0 then Amplitude = 0
   END IF
   t+=1
   
   if child=0 then
      return Amplitude*2-1
   else
      return Amplitude*child->getnext
   end if
end function


function ADSREnvelope2 Overload(Attack as single, Decay as single, Sustain as single, Release as Single, Dur as single, SilenceAdjust as integer=0) as EnvelopeFunction2 ptr
   dim w as EnvelopeFunction2 ptr=new EnvelopeFunction2
   w->Duration=dur
   
   dim as single S = 1 - Attack - Decay - Release
   
   w->Incr = 1 / (Dur*__Samplerate * Attack + 1)
   w->DecD = (1 - Sustain) / (Dur*__Samplerate * Decay + 1)
   w->DecR = Sustain / (Dur*__Samplerate * Release + 1)
   
   w->A = Attack*Dur*__Samplerate - SilenceAdjust
   w->D = Decay*Dur*__Samplerate + w->A
   w->S = S*Dur*__Samplerate + w->D
   w->R = Release*Dur*__Samplerate + w->S
   
   return w
end function

function ADSREnvelope2 Overload(func as any ptr, Attack as single, Decay as single, Sustain as single, Release as Single, Dur as single, SilenceAdjust as integer=0) as EnvelopeFunction2 ptr
   dim w as EnvelopeFunction2 ptr=new EnvelopeFunction2
   w->child=func
   w->Duration=dur
   
   dim as single S = 1 - Attack - Decay - Release
   
   w->Incr = 1 / (Dur*__Samplerate * Attack + 1)
   w->DecD = (1 - Sustain) / (Dur*__Samplerate * Decay + 1)
   w->DecR = Sustain / (Dur*__Samplerate * Release + 1)
   
   w->A = Attack*Dur*__Samplerate - SilenceAdjust
   w->D = Decay*Dur*__Samplerate + w->A
   w->S = S*Dur*__Samplerate + w->D
   w->R = Release*Dur*__Samplerate + w->S
   
   return w
end function

''===================================================

type TriangleSweepFunction extends SoundFunction
   Freq as single
   DutyCycle as single
   declare function GetNext() as single 
end type

function TriangleSweepFunction.GetNext() as single
   t+=1
   if child<>0 then DutyCycle=child->GetNext/2+.5
   dim as single r,dc
   r=1.0/__Samplerate*Freq*t+fm
   dc=((4*DutyCycle-1)*8*DutyCycle+1)/5
   return (abs(((r-int(r))^(dc))*4-2)-1)
end function

function TriangleSweep overload(Freq as single, DutyCycle as single=.5) as TriangleSweepFunction ptr
   dim w as TriangleSweepFunction ptr=new TriangleSweepFunction 
   w->Freq=Freq
   w->DutyCycle=DutyCycle
   
   return w
end function

function TriangleSweep overload(Freq as single, DutyCycle as any ptr) as TriangleSweepFunction ptr
   dim w as TriangleSweepFunction ptr=new TriangleSweepFunction 
   w->Freq=Freq
   w->child=DutyCycle
   
   return w
end function

''===================================================

dim as long numberoftones=20 ' number is chosen freely
dim as long buffersize,i,n(1 to numberoftones) ' n: MIDI note number
dim as single start,duration,d(1 to numberoftones) ' d: duration

n(1)=64      : d(1)=5
n(2)=n(1)-2  : d(2)=3
n(3)=n(1)    : d(3)=3
n(4)=n(1)+5  : d(4)=6

function MidiNoteToFrequency(n as long) as single
   return (2^((n-69)/12))*440
end function

'change duration of tones
for i=lbound(n) to ubound(n)
   d(i)/=6
next i

dim as integer samplerate,channels,bits
samplerate=44100
channels=1
bits=16

SoundSet(samplerate,channels,bits)

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

'calculate duration of full sound
duration=0
for i=lbound(d) to ubound(d)
   duration+=d(i)
next i

dim shared as WaveHeaderType ptr wave(1 to 2),tempwave
buffersize=samplerate*duration

for i=lbound(wave) to ubound(wave)
   wave(i)=CreateWave(buffersize,samplerate,channels,bits)
next i

tempwave=CreateWave(buffersize,samplerate,channels,bits)

sub tone(start as single,n as long,d as single)
   Sound tempwave,0,TriangleSweep(MidiNoteToFrequency(n),SineWave(1/d)),d
   Sound wave(1),start,ADSREnvelope2(DSPWave(tempwave),0.15,0.25,0.9,0.1,d),d
   
   Sound tempwave,0,TriangleSweep(MidiNoteToFrequency(n+5),SineWave(1/d)),d
   Sound wave(2),start,ADSREnvelope2(DSPWave(tempwave),0.1,0.2,0.8,0.1,d),d
end sub

'fill buffers
start=0
for i=lbound(n) to ubound(n)
   tone(start,n(i),d(i))
   start+=d(i)
next i

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

dim as WaveHeaderType ptr mixwave
mixwave=CreateWave(samplerate*(duration+0.1),samplerate,channels,bits)
Sound mixwave,0.03,MixWaves(DSPWave(wave(1)),DSPWave(wave(2))),duration

''SaveWave("test.wav",mixwave)

PlayWave(mixwave)

sleep
VANYA
Posts: 1753
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

@angros47

I tried to play the midi file using the sfx library: https://disk.yandex.ru/d/7W9yMtYUwZvpZQ , but all the notes are confused (not like the original music). I tried on windows and dos. On dos the result is generally bad, although the dosmid program plays it correctly. Do not tell me what could be the problem?
angros47
Posts: 2123
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

Good question.
On Linux it more or less worked for a while, then crashed: it usually means that the midi contains some illegal instructions (like, playing a percussion instrument that doesn't exist)

In Dos, I tried it in the adlib mode, it sounded awful. How does it sound under Windows? It makes me think it uses a sound font not very suitable for that specific music.

Also, in DOS did you use the adlib mode (enabled with SoundMidiControl 103,"adlib" ) or the default mode? (adlib mode works only on dosbox or on hardware that has am OPL chip, like an old SoundBlaster). Default mode works under the Windows dos prompt (using the soundfont provided by windows), or if you have external MIDI hardware connected to the MPU-401 port
VANYA
Posts: 1753
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

I am using 86box computer emulator (old computer with soundblaster card with emulation MPU-401). I installed freedos there and tested it. The dosmid program runs this file correctly. Yes, there are small differences, but they are not significant. On Windows (libsfx), the notes are mixed up, although the sound is not annoying. Although if you play the file in Windows Media Player, then everything sounds right and is almost the same as dosmid. In DOS (libsfx), it's impossible to listen at all, there, in my opinion, the notes are even more confused. In Linux (libsfx) also sounds disgusting. In Linux timidity plays the file and it is already partially similar to the sound in Windows Media Player and DosMid.

I have a simple example to use:

Code: Select all

#include "sfx.bi"
#inclib "fbsfx"

SoundmidiSet ()
dim as any ptr Midi=LoadMidi("music.mid")
PlayMidi(Midi, 1)
sleep
You just tell me: Is it a MIDI file problem or a library problem or a SoundFont?
angros47
Posts: 2123
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

Under Linux, do you use the soft synthesizer embedded in the library, or you use Timidity as MIDI output device for ALSA?
VANYA
Posts: 1753
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Sep 24, 2022 16:27 Under Linux, do you use the soft synthesizer embedded in the library, or you use Timidity as MIDI output device for ALSA?
I try both your library and Timidity.
Timidity plays close to the original, and the sfx library very often just wheezes. For example, this midi file plays fine with Timidity , but terribly with sfx library: https://midistock.ru/files/h/handel_geo ... -1-0-30615
angros47
Posts: 2123
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

My question is: my library can use Timidity as output. Timidity, in fact, can be configured as a midi output device (usually on port 128), and my library can use it as audio output. Do you use it? Or do you use the internal FM synthesizer, when you use my library?
VANYA
Posts: 1753
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Sep 24, 2022 17:01 My question is: my library can use Timidity as output. Timidity, in fact, can be configured as a midi output device (usually on port 128), and my library can use it as audio output. Do you use it? Or do you use the internal FM synthesizer, when you use my library?
I don't even know how to set up your library to work with Timidity!
I use everything by default (I don't change anything in your library or settings).
So I probably use your library with a synthesizer that is built into your library.

I was thinking of using your library to make a simple midi player (for myself), but unfortunately many midi files sound bad with libsfx.
angros47
Posts: 2123
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

With the command "amidi -l" on linux, what do you get?

It is possible, with the command "timidity -iA", to make it a target for the output of my library
Last edited by angros47 on Sep 24, 2022 17:41, edited 1 time in total.
VANYA
Posts: 1753
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Sep 24, 2022 17:31 With the command "amidi -l" on linux, what do you get?
> amidi -l

Dir Device Name
angros47
Posts: 2123
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

Ok: try to execute

Code: Select all

timidity -iA
and to check again, if any device has appeared in the list
VANYA
Posts: 1753
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

timidity -iA
Requested buffer size 32768, fragment size 8192
ALSA pcm 'default' set buffer size 32768, period size 8192 bytes
TiMidity starting in ALSA server mode
Opening sequencer port: 128:0 128:1 128:2 128:3
And the program doesn't end in the terminal , I can't execute after that amidi -l
If I force close with CTRL+C , the output will be empty again: Dir Device Name
angros47
Posts: 2123
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

Try this: start timidity -iA in a terminal, then open a new terminal (or a new tab in the same terminal) and use amidi -l again
VANYA
Posts: 1753
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Sep 24, 2022 17:53 Try this: start timidity -iA in a terminal, then open a new terminal (or a new tab in the same terminal) and use amidi -l again
I tried , the output is empty.

But I'm more interested in why the library itself handles midi files so badly? Some notes sound, and some seem to be empty (they sound like a stick on a piece of wood). The whole point was not to install timidity or some other program, but simply launch your player based on your sfx library, but it turns out it doesn’t work well.
angros47
Posts: 2123
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

I am trying to understand what happened, as well. The library has a sequencer (that is supposed to work both for the events created by the PLAY command, and for midi files), and then sends the events to the default midi output device.

On windows, the midi output device is often a pretty poor wavetable synthesizer, with low quality (most midi players nowadays avoid it, and use their own synthesizer)

On linux, by default there is NO midi output device, unless you have a midi dedicated hardware. So, the solution is to configure timidity as midi output device, otherwise midi doesn't work. Most media players (like totem) don't even try to use the midi output anymore, and they use instead a sort of reduced version of wildmidi (a software similar to timidity)

Including such a software in my library was not possible, because it would have needed the sound fonts (that are usually 50 Mb downloads, for a good quality). So, I used the cheaper solution to emulate an old hardware used in DOS systems, as a fallback solution
Post Reply