Audio library for FreeBasic - Features

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
VANYA
Posts: 1841
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

I present 2 files, one entry using sfx, the other using dosmid player. You can listen and make sure that sfx plays something incomprehensible under DOS:

Here is the dosmid entry (correctly played): https://disk.yandex.ru/d/6odNNDDQJtsorQ
Here's a sfx recording (incomprehensible music): https://disk.yandex.ru/d/lGb3XJ6LiSV0gw

Here's the original file midi: https://disk.yandex.ru/d/P7X_8HiAel5y3w

P.S. Under Linux and Windows, this file plays correctly.
P.P.S I use SoundBlaster on virtual system (freedos). Similarly, it sounds bad on a real computer with adlib.
angros47
Posts: 2339
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

I think the issue is a bad support for drums under sfx.

About the file onestop.mid, it is possible that it is a type 2 midi file?
VANYA
Posts: 1841
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Oct 23, 2022 16:17 I think the issue is a bad support for drums under sfx.
And why then on windows and linux the sound is normal? And the second question is: Can this be improved?
angros47 wrote: Oct 23, 2022 16:17About the file onestop.mid, it is possible that it is a type 2 midi file?
I don't know. I only know that this is a file that lies in a standard folder: C:\Windows\Media
I don't think microsoft would put a non-standard or bad file in the windows.
angros47
Posts: 2339
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

VANYA wrote: Oct 23, 2022 17:02
And why then on windows and linux the sound is normal? And the second question is: Can this be improved?
Because if it's the case, the issue would be in the part of code used to translate the MIDI command into OPL commands. Under Windows or Linux the library doesn't use that part of the code, but it relies on the OS-specific implementation (or, in Linux, loads a small software synthesizer)

I think it can be improved, but I am not sure how complex is the task

I don't know. I only know that this is a file that lies in a standard folder: C:\Windows\Media
I don't think microsoft would put a non-standard or bad file in the windows.
Type 2 MIDI files are standard, they are just pretty uncommon. Type 2 is a specific format, not supported in my library, that allows to have several different songs inside a single MIDI file.
VANYA
Posts: 1841
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Oct 23, 2022 18:00 Type 2 MIDI files are standard, they are just pretty uncommon. Type 2 is a specific format, not supported in my library, that allows to have several different songs inside a single MIDI file.
Then at least the library should have a check (if tracks > 16 then ....). Currently crash program if tracks > 16.
angros47
Posts: 2339
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

In LoadMidi there is the line:

Code: Select all

	if b[1]>1 then delete Midi: close F:return 0	'Not a supported format
that stops the load if the type is not 0 or 1. Is it executed or not? (if the load fails and you try to play the null audio, it would crash anyway)
VANYA
Posts: 1841
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

The error (segmentation fault) is here , if tracks = 17:

Code: Select all

Midi->Track(I)=left(input(TrackLength,F), TrackLength-4)
angros47
Posts: 2339
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

What happens if you change the value of the const?
VANYA
Posts: 1841
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Oct 24, 2022 6:29 What happens if you change the value of the const?
So try it yourself.

And by the way, here is the player for DOS: https://bisqwit.iki.fi/jutut/kuvat/prog ... iplay.html

He can play any midi files. The only downside is that it loses at a faster rate (+20%). The source code is of course solid spaghetti, but maybe this will help you figure it out.
angros47
Posts: 2339
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

I cannot try because I don't have that midi file.
VANYA
Posts: 1841
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

hhr
Posts: 244
Joined: Nov 29, 2019 10:41

Re: Audio library for FreeBasic - Features

Post by hhr »

Debian and Lubuntu have the same behaviour. The sound is audible but distorted.
With timidity -iA the sound gets good but stumbles a bit.
Lubuntu with Ubuntu Studio gives a good sound without disturbance. (And without timidity.)

I tried longint in sequencer.bi and sleep in playtomidi.bas and could not find a difference.

I am interested in the DSP feature of sfx. The following program crashes with SoundSet(44100,1,8).
It runs if screenres is commented out, or if the last allocated buffer (pWave(10)) is not used.

Code: Select all

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

dim as long buffersize,i
dim as single d=5

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

SoundSet(samplerate,channels,bits)

dim shared as WaveHeaderType ptr pWave(1 to 10)
buffersize=samplerate*d

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

print "AAA"
sleep 1000

Sound pWave(10),0,SineWave(440),d
'Sound pWave(10),0,DSPWave(pWave(5)),d

screenres (400,300)

print "End of program, any key to quit."
sleep
hhr
Posts: 244
Joined: Nov 29, 2019 10:41

Re: Audio library for FreeBasic - Features

Post by hhr »

@angros47
found the reason for the crashes. Line 568 in SoundFunction.bas should be:
for i as integer=0 to samples - 1

As the seven corresponding lines above in the two sound subroutines.
angros47
Posts: 2339
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

Or in your program you can change the line:

Code: Select all

buffersize=samplerate*d
to

Code: Select all

buffersize=samplerate*d +1
hhr
Posts: 244
Joined: Nov 29, 2019 10:41

Re: Audio library for FreeBasic - Features

Post by hhr »

I tried your proposal in my programs. It seems to work, thank you.

My functions work with gas32,gas64 and gcc64. They do not work with gcc32.
Therefore I replaced the sfx internal variable __Samplerate by the own variable samplerate:

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"

dim shared as long samplerate,channels,bits
samplerate=44100
channels=1
bits=16

SoundSet(samplerate,channels,bits)

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

type ExpADEnvelopeFunction extends SoundFunction
   Max as double
   Pow as double
   Amplitude as double
   declare function GetNext() as single
end type

function ExpADEnvelopeFunction.GetNext() as single
   dim as double arg,arg2,arg3
   arg=t/(Max*samplerate)
   arg2=(arg/Pow)^Pow ' (arg^Pow)/(Pow^Pow)
   arg3=(arg2)*exp(Pow-arg)
   if arg3 > 1 then arg3=1
   if arg3 < 0 then arg3=0
   t+=1
   
   if child=0 then
      return (2*arg3*Amplitude-1)
   else
      return arg3*Amplitude*(child->getnext)
   end if
end function

function ExpADEnvelope overload(Pow as double, Max as double, Amplitude as double=1) as ExpADEnvelopeFunction ptr
   dim w as ExpADEnvelopeFunction ptr=new ExpADEnvelopeFunction
   w->Pow=Pow
   w->Max=Max
   w->Amplitude=Amplitude
   
   return w
end function

function ExpADEnvelope overload(func as any ptr, Pow as double, Max as double, Amplitude as double=1) as ExpADEnvelopeFunction ptr
   dim w as ExpADEnvelopeFunction ptr=new ExpADEnvelopeFunction
   w->child=func
   w->Pow=Pow
   w->Max=Max
   w->Amplitude=Amplitude
   
   return w
end function

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

sub ShowWave(buffer as WaveHeaderType ptr)
   open cons for output as #1
   print #1, mkl(buffer->RiffID),"RIFF"
   print #1, buffer->RiffLength,"Riff Length = File Length - 8"
   print #1, mkl(buffer->WavID),"WAVE"
   print #1, mkl(buffer->FmtID),"fmt "
   print #1, buffer->FmtLength,"Fmt Length (16)"
   print #1, buffer->wavformattag,"Format Tag (1: PCM)"
   print #1, buffer->Channels,"Channels"
   print #1, buffer->SamplesPerSec,"Sample Rate"
   print #1, buffer->avgBytesPerSec,"Bytes/Second"
   print #1, buffer->blockalign,"Block Align"
   print #1, buffer->FmtSpecific,"Bits/Sample"
   print #1, mkl(buffer->DataID),"data"
   print #1, buffer->DataLength,"Data Length = File Length - 44"
   print #1, string (44,"=")
   close #1
   '------------------------------------------------
   dim as ulong i
   dim as long x,y
   x=400 : y=300
   screenres (x,y)
   cls
   if (buffer->Channels) = 2 then line (0,y\2)-(x,y\2),10
   
   if (buffer->Channels) = 1 then
      if (buffer->FmtSpecific) = 16 then
         'Print "1,16"
         window (0,-32768)-((buffer->DataLength)\2,32767)
         dim as short ptr SourcePtr=cast(short ptr,(cast(byte ptr,buffer)+44))
         for i=1 to (buffer->DataLength)\2
            pset (i-1,SourcePtr[i-1])
         next i
      else
         'print "1,8"
         window (0,0)-((buffer->DataLength),255)
         dim as ubyte ptr SourcePtr=cast(ubyte ptr,(cast(byte ptr,buffer)+44))
         for i=1 to (buffer->DataLength)
            pset (i-1,SourcePtr[i-1])
         next i
      end if
   else
      if (buffer->FmtSpecific) = 16 then
         'print "2,16"
         window (0,-32768)-((buffer->DataLength)\2,32767)
         dim as short ptr SourcePtr=cast(short ptr,(cast(byte ptr,buffer)+44))
         for i=1 to (buffer->DataLength)\2 step 2
            pset (i-1,SourcePtr[i-1]\2+16384)
            pset (i-1,SourcePtr[i]\2-16384)
         next i
      else
         'print "2,8"
         window (0,0)-((buffer->DataLength),255)
         dim as ubyte ptr SourcePtr=cast(ubyte ptr,(cast(byte ptr,buffer)+44))
         for i=1 to (buffer->DataLength) step 2
            pset (i-1,SourcePtr[i-1]\2+128)
            pset (i-1,SourcePtr[i]\2)
         next i
      end if
   end if
end sub

''===================================================
'sfx initialises with zeros (function CreateWave in wave.bas).
'In some cases (MixWaves) it is useful to initialise 8-bit files with 127 or 128.
'sfx uses 127.
sub Init8BitWave(buffer as WaveHeaderType ptr)
   if (buffer->FmtSpecific) <> 8 then exit sub
   dim as ulong i
   dim as ubyte ptr bptr=cast(ubyte ptr,buffer)
   for i=0 to (buffer->DataLength)-1
      bptr[i+44]=127
   next i
end sub

''===================================================
print "Please wait..." : locate 1

dim as long buffersize,i
dim as single start,duration

'' n: Midi note number, d: duration
dim as single n(1 to ...)={ 69, 69}
dim as single d(1 to ...)={1.8,9.5}

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

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

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

dim shared as WaveHeaderType ptr pWave(1 to 16),mWave(1 to 14),sWave,eWave
buffersize=samplerate*duration + 3

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

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

dim as single ed=1
eWave=CreateWave(samplerate*ed+1,samplerate,channels,bits)
Sound eWave,0,ExpADEnvelope(1,0.1,0.8),ed

start=0.2
sWave=CreateWave(samplerate*(duration+start),samplerate,channels,bits)
Init8BitWave(sWave)

dim shared as single Pow=0.4,Max=0.4
sub tone(start as single,n as single,d as single)
   Sound pWave(1),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n-12)),Pow,1,0.5),d
   '   Sound pWave(2),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n)),Pow,0.1,0.25),d
   Sound pWave(3),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n)),Pow,Max,1),d
   Sound pWave(4),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+3)),Pow,Max,0.73),d
   Sound pWave(5),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+7)),Pow,Max,0.4),d
   Sound pWave(6),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+12)),Pow,Max,0.95),d
   Sound pWave(7),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+16)),Pow,Max,0.5),d ' (!)
   Sound pWave(8),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+17)),Pow,Max,0.25),d
   Sound pWave(9),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+17+0.01)),Pow,Max,0.25),d
   Sound pWave(10),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+19)),Pow,Max,0.9),d
   Sound pWave(11),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+21)),Pow,Max,0.75),d
   Sound pWave(12),0,ExpADEnvelope(SineWave(MidiNoteToFrequency(n+24)),Pow,Max,1),d
   '   Sound pWave(13),0,...
   '   Sound pWave(14),0,...
   '   Sound pWave(15),0,...
   '   Sound pWave(16),0,...
   '---
   Sound mWave(1),0,MixWaves(DSPWave(pWave(1)),DSPWave(pWave(2))),d
   Sound mWave(2),0,MixWaves(DSPWave(pWave(3)),DSPWave(pWave(4))),d
   Sound mWave(3),0,MixWaves(DSPWave(pWave(5)),DSPWave(pWave(6))),d
   Sound mWave(4),0,MixWaves(DSPWave(pWave(7)),DSPWave(pWave(8))),d
   Sound mWave(5),0,MixWaves(DSPWave(pWave(9)),DSPWave(pWave(10))),d
   Sound mWave(6),0,MixWaves(DSPWave(pWave(11)),DSPWave(pWave(12))),d
   '   Sound mWave(7),0,MixWaves(DSPWave(pWave(13)),DSPWave(pWave(14))),d
   '   Sound mWave(8),0,MixWaves(DSPWave(pWave(15)),DSPWave(pWave(16))),d
   '---
   Sound mWave(9),0,MixWaves(DSPWave(mWave(1)),DSPWave(mWave(2))),d
   Sound mWave(10),0,MixWaves(DSPWave(mWave(3)),DSPWave(mWave(4))),d
   Sound mWave(11),0,MixWaves(DSPWave(mWave(5)),DSPWave(mWave(6))),d
   '   Sound mWave(12),0,MixWaves(DSPWave(mWave(7)),DSPWave(mWave(8))),d
   '---
   Sound mWave(13),0,MixWaves(DSPWave(mWave(9)),DSPWave(mWave(10))),d
   Sound mWave(14),0,MixWaves(DSPWave(mWave(11)),DSPWave(mWave(12))),d
   '---
   Sound sWave,start,Panning(MixWaves(DSPWave(mWave(13)),DSPWave(mWave(14))),,2.7),d ' Amplify with Panning
end sub

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

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

ShowWave(eWave)
print "Any key to continue..."
sleep
ShowWave(sWave)

'SaveWave("envelope.wav",eWave)
'SaveWave("test.wav",sWave)

PlayWave(sWave)

sleep
Last edited by hhr on Nov 05, 2022 8:23, edited 2 times in total.
Post Reply