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

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->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->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

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->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->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 tempwave,0,TriangleSweep(MidiNoteToFrequency(n+5),SineWave(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

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

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: 1758
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

@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: 2128
Joined: Jun 21, 2005 19:04

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: 1758
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

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 ()
PlayMidi(Midi, 1)
sleep``````
You just tell me: Is it a MIDI file problem or a library problem or a SoundFont?
angros47
Posts: 2128
Joined: Jun 21, 2005 19:04

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

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: 2128
Joined: Jun 21, 2005 19:04

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: 1758
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

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: 2128
Joined: Jun 21, 2005 19:04

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: 1758
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

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: 2128
Joined: Jun 21, 2005 19:04

Ok: try to execute

Code: Select all

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

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: 2128
Joined: Jun 21, 2005 19:04

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: 1758
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

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: 2128
Joined: Jun 21, 2005 19:04