Audio library for FreeBasic - Features

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
keenmaster486
Posts: 7
Joined: Feb 20, 2019 21:28

Re: Audio library for FreeBasic - Features

Post by keenmaster486 »

Hi angros47,

I'm doing some testing with your audio library in DOS. Thank you so much for working on this; I've been needing a comprehensive DOS audio library for FB for a long time and there hasn't been one until now.

So far:

MIDI files do not play correctly in DOS. I've tried a few that all play correctly in Windows and under various other DOS MIDI players that use the OPL3 chip, but the sfx library plays them extremely slowly and with many wrong notes. Using the adlib mode in DOSBox.

WAV files I have better luck with, however there are two problems:
1. The library does not like it when you try to run your test program again after stopping it. Says "DSP could not be reset"
2. It only works in DOSBox. On real hardware (Pentium MMX, genuine Sound Blaster 16), it plays about a half second snippet of the file before going completely silent.

I don't have any weird settings or anything on my DOSBox or my real hardware. It's all the usual Sound Blaster addresses (A220 I5 D1 H5 T6). All other known working programs and games play FM music and digital sound correctly on both.

One more thing: I have some code I've been working on that plays IMF and DRO files (raw Adlib output) in DOS. I wonder if you are interested in incorporating this code or a version of it as an ISR-based routine in your library to play such files. It would be very useful for game programming, and would be a good alternative to MIDI for DOS music since playing MIDI files through the OPL3 chip is always pretty janky no matter which way you cut it.

Below see the code for my test program:

Code: Select all

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

'SoundSet(44100, 2, 16)
'SoundMidiControl(103, "ADLIB")
'SoundMidiSet()
'dim midi as any ptr
'midi = LoadMidi("aliens.mid")
'PlayMidi(midi, 1)

SoundSet(44100, 2, 16)
dim wave as any ptr
wave = LoadWave(COMMAND$)
PlayWave(wave)


do until inkey$ <> ""
loop
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

I understand that some MIDI files can play in wrong way (the integrated player in the library is pretty basic, the sequencer is minimalist), but without knowing what are the notes that cause issues (are they on a specific channel? Or on a specific instrument?) there is no way to figure what could be the issue.

Instead, I might have an idea of what went wrong for your real sound blaster maybe the IRQ setting is wrong? Not every program that uses a soundcard in dos uses it (for example, all the examples for the sound blaster written in QBASIC ignored that setting), so a wrong setting might go unnoticed. My library uses that setting to refill the soundcard buffer when it has been played completely, so if the wrong interrupt is called, no buffer refill happens, and the audio halts.Try changing the I parameter with different values.

The library is not supposed to be initialized more than once, unfortunately. So, I didn't plan a specific way to reset the sound settings. I tested it on DosBox and on the dos prompt of Windows XP, not on a real dos machine (that I don't have)

About playing raw Adlib sounds: thank you, but the code you talk about could not be integrated in the library because it wouldn't be multiplatform: how would you get it to work on Windows or Linux? (yes, I know that there is AdlibEmu, in the past I have even integrated it in FreeBasic and considered it as a possible way to implement the PLAY command, but I discarded the idea). Also, not all DOS platforms use the OPL chip: the Windows Sound System (the emulated soundcard that can be used from the DOS prompt under Windows) doesn't emulate OPL, but emulates the MPU port (that is normally used for an external MIDI synthesizer, but under Windows is just redirect to the MIDI synthesis provided by windows).

My solution to use MIDI commands doesn't allow to customize the sounds as well as it would be possible by sending direct commands to the OPL chip, but allows to play the same notes both on an OPL chip, or an external device like the Roland MT 32 (I think, at least)
keenmaster486
Posts: 7
Joined: Feb 20, 2019 21:28

Re: Audio library for FreeBasic - Features

Post by keenmaster486 »

I tried different IRQ values but they do not work at all, it says DSP failed to reset.

Thought it might have been the memory manager, tried HIMEM only and still the same result.

Checked with HWINFO that my SB IRQ is indeed 5. It's set with a jumper on the card so it's kind of hard to get it wrong.

As for OPL not being cross-platform, of course it isn't unless you use an emulator. The issue here is that music in DOS is simply a different animal than on Windows. MIDI just does not play well with DOS unless you have an actual wavetable or MIDI box plugged in to your sound card -- even when you do get MIDI playing through OPL, it never sounds all that great in my experience. DOS is special, so it might make sense to add that functionality for DOS, and bring it forwards to modern platforms by using an OPL3 emulator (like what Adlib Tracker II does). Just a thought.


Fwiw I have experienced the buffer not refilling problem before, with other QB-based WAV players that claim to be able to play WAVs on an SB16. I vaguely remember it being some quirk of genuine SB16s in which they're not fully backwards compatible with the other Sound Blasters, so you have to account for that. It might work correctly on an AWE64.

More accurate PC emulators (86Box, PcEm, etc) might emulate the SB16 closer to the actual hardware. Might be worth a shot to try those.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

Actually, the file midisynth.bas included in my library is a sort of emulator of the OPL chip: the algorithm used is the same (4 operators FM synthesis), the main difference is that it doesn't emulate the limits of the original chip (so, it's possible to play as many notes as you want at the same time, while the OPL2 chip allowed a maximum to 9 channels, for example)

Currently, that emulator is used only in the linux version of the library, as a fallback if no other FM synthesizer is detected.

About DOS: I remind you that for years (and I guess it's still true today) for many people the only DOS available was the dos prompt of windows, and that DOS provided only the emulated MPU midi port, not the OPL. So, when I designed the library, I prioritized a solution that could work indifferently on a sound blaster and on that, with no need to change the program

By the way, if you want to control the OPL chip directly, you don't need my library, since you can access it easily with the OUT command (there are several tutorial for QB45 that work almost unchanged in FreeBasic). It is not as easy to control as a MIDI device (since you need to define the parameters for every instruments, while a MIDI device just requires you to select the preset instrument from a list), but it's doable. And emulators, too, are controlled in the exact same way of the original chip.

In the file DMA.BAS, try adding to the function "isr_dsp_callback" a line, like a simple PRINT "CallBack", to see if the function is ever called or not. That function is supposed to be called when the buffer is empty to refill it.
hhr
Posts: 206
Joined: Nov 29, 2019 10:41

Re: Audio library for FreeBasic - Features

Post by hhr »

In the last example (Example LoadWave, PlayWave) of
https://www.freebasic.net/wiki/ExtLibsfx
the line
SoundSet (44100,2,16)
seems to be needless. The function LoadWave reads the values.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

It is not useless, because SoundSet configures the audio output for all commands, while LoadWave can load several files, that could have different parameters. The library converts them all to the same format, and in that way it can play at the same time audio files that are stored in different formats (stereo and mono, for example)

Look at the examples on the page https://www.freebasic.net/wiki/KeyPgBload: as you can see, the command "Screen" or "ScreenRes" is always used, even if the command BLoad is able to read the parameters from the bitmap file. In the sound library, the concept is the same: loading a file is not the same thing as setting the video, or audio format.
hhr
Posts: 206
Joined: Nov 29, 2019 10:41

Re: Audio library for FreeBasic - Features

Post by hhr »

I do not know the values for SoundSet when I wish to play a wavefile. The program must read them from the file.
The following example is a test. There must be a better way to fill SoundSet.

Code: Select all

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

Dim As String s="1.wav"
Dim As WaveHeaderType Ptr pWave
pWave = LoadWave(s)
If pWave = 0 Then
   Print "pWave = 0" : End
End If
Dim As Ulong Ptr lptr=Cast(Ulong Ptr,pWave)
Dim As Ulong SampleRate=lptr[6]
Dim As Ulong Channels=Hiword(lptr[5])
Dim As Ulong BitsPerSample=Hiword(lptr[8])
Print SampleRate,Channels,BitsPerSample
SoundSet (SampleRate,Channels,BitsPerSample)
PlayWave(pWave)
Sleep
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

When you want to load a bitmap, you have to set up the graphic page before loading it. And you don't use the resolution of the bitmap to set the graphic page, do you? You set up the graphic page according to the resolution of the screen, not of the bitmap.

For the audio, it's the same. You set the parameters according to your hardware, not to the wav file.

If you don't know the values, under Windows or Linux just use "SoundSet (44100,2,16) " that usually works fine.
hhr
Posts: 206
Joined: Nov 29, 2019 10:41

Re: Audio library for FreeBasic - Features

Post by hhr »

Sfx has it. Please see wave.bas and wave.bi. Now the files are played in the right pitch.

Code: Select all

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

Dim As WaveHeaderType Ptr pWave
pWave = LoadWave("1.wav")
If pWave = 0 Then
   Print "pWave = 0" : End
End If
SoundSet (pWave->SamplesPerSec,pWave->Channels,pWave->FmtSpecific)
PlayWave (pWave)
Sleep
ADSR with exponential release. Silence adjusting can be constructed with ADSREnvelope, if necessary:

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 ExpADSREnvelopeFunction 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
   timeconstant as single

   declare function GetNext() as single 
end type

function ExpADSREnvelopeFunction.GetNext() as single
   IF t <= A THEN
      Amplitude+=Incr ' Amplitude+=(1-Amplitude)*Incr/timeconstant
      if Amplitude > 1 then Amplitude = 1
   ELSEIF t < D THEN
      Amplitude-=DecD ' Amplitude-=Amplitude*DecD/timeconstant
      if Amplitude < 0 then Amplitude = 0
   ELSEIF t < S THEN
   ELSEIF t < R THEN
      Amplitude-=Amplitude*DecR/timeconstant ' Difference equation for exponential function: Amplitude=Amplitude*(1-DecR/timeconstant)
      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 ExpADSREnvelope Overload(Attack as single, Decay as single, Sustain as single, Release as Single, Dur as single, timeconstant as single) as ExpADSREnvelopeFunction ptr
   dim w as ExpADSREnvelopeFunction ptr=new ExpADSREnvelopeFunction
   w->timeconstant=timeconstant

   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
   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 ExpADSREnvelope Overload(func as any ptr, Attack as single, Decay as single, Sustain as single, Release as Single, Dur as single, timeconstant as single) as ExpADSREnvelopeFunction ptr
   dim w as ExpADSREnvelopeFunction ptr=new ExpADSREnvelopeFunction 
   w->child=func
   w->timeconstant=timeconstant

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

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

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-1)-(x,y\2-1),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

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

dim as long buffersize,i
dim as single d,n ' d: duration, n: Midi note number

n=48
d=5

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

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

SoundSet(samplerate,channels,bits)

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

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

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

sub tone(n as single,d as single)
   Sound pWave(1),0,ADSREnvelope(0.1,0.1,0.7,0.5,d),d
   Sound pWave(2),0,ExpADSREnvelope(0.1,0.1,0.7,0.5,d,0.1),d
   if (pWave(3)->channels) = 2 then
      Sound pWave(3),0,Stereo(DSPWave(pWave(1)),DSPWave(pWave(2))),d
   else
      Sound pWave(3),0,DSPWave(pWave(2)),d
   end if
   ''------------------------------------------
   Sound pWave(4),0,FrequencyModulate(HarmonicWave(MidiNoteToFrequency(n),1,1,0,0.5,0,0.5),HarmonicWave(MidiNoteToFrequency(n+6.99),1,1,0,0.5,0,0.5),2),d
   Sound pWave(5),0,FrequencyModulate(HarmonicWave(MidiNoteToFrequency(n+12),1,1,0,0.5,0,0.5),HarmonicWave(MidiNoteToFrequency(n+12+7.01),1,1,0,0.5,0,0.5),2),d
   Sound pWave(6),0,Mixwaves(DSPWave(pWave(4)),DSPWave(pWave(5))),d
   Sound pWave(7),0,DspFilter(DSPWave(pWave(6)),3000,1 ,0),d
   Sound pWave(8),0,ADSREnvelope(DSPWave(pWave(7)),0.1,0.1,0.7,0.5,d),d
   Sound pWave(9),0,ExpADSREnvelope(DSPWave(pWave(7)),0.1,0.1,0.7,0.5,d,0.1),d
'   Sound pWave(9),0,ADSREnvelope(ExpADSREnvelope(DSPWave(pWave(7)),0.1,0.1,0.7,0.5,d,0.1),0.01,0,1,0.2,d-0.11),d ' ExpADSREnvelope with silence adjust
   if (pWave(10)->channels) = 2 then
      Sound pWave(10),0,Stereo(DSPWave(pWave(8)),DSPWave(pWave(9))),d
   else
      Sound pWave(10),0,DSPWave(pWave(9)),d
   end if
end sub

tone(n,d) ' fill buffers

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

ShowWave(pWave(3))
sleep
ShowWave(pWave(10))

'SaveWave("test.wav",pWave(10))

PlayWave(pWave(10))

' Show ADSREnvelope for silence adjusting ExpADSREnvelope:
sleep
Sound pWave(1),0,ADSREnvelope(0.01,0,1,0.2,d-0.11),d ' overwrites pWave(1), which is no longer needed.
ShowWave(pWave(1))
open cons for output as #1
print #1,"ADSREnvelope for silence adjusting ExpADSREnvelope"
close #1

sleep
Last edited by hhr on Oct 26, 2022 19:47, edited 4 times in total.
VANYA
Posts: 1834
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

hi angros47!

Everything that I will describe concerns only midi. In DOS I can only test midi on real hardware (adlib).

1) The problem of mixing up notes in windows is only on the win32 platform! On the win64 platform, everything is fine.
2) In Linux, the sound freezes due to a small delay in the playtomidi.bas file line 335 (instead of sleep(1) you need sleep(17) ). But there remains a small problem with the sound at the beginning (the sound initially contains garbage for about 1-2 seconds, and then everything is fine). It looks like you have an error somewhere.
3) The sound under DOS (freedos) is wrong, as if in a hurry. You need to test this properly on real hardware or at least in the 86box emulator.

P.S. Test file for testing: https://midistock.ru/files/h/handel_geo ... -1-0-30615
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

1) Under windows (32 or 64) the midi command are just remapped to midiOutShortMsg , so try to see on Win32 if you can reproduce the problem by just using it. If you can, the issue is in the specific installed driver

2)Have you tested it on many installations, or just one? The optimal pauses can change depending on the version of Alsa, on the CPU speed, and so on.

3) You mean the DSP sound, or the midi sound? Also, do you use it in the text or graphic mode?
VANYA
Posts: 1834
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Oct 18, 2022 20:38 1) Under windows (32 or 64) the midi command are just remapped to midiOutShortMsg , so try to see on Win32 if you can reproduce the problem by just using it. If you can, the issue is in the specific installed driver
I'm still trying to figure out what the problem is. But in any other player, the music plays correctly, that is, it's not the driver
angros47 wrote: Oct 18, 2022 20:38 2)Have you tested it on many installations, or just one? The optimal pauses can change depending on the version of Alsa, on the CPU speed, and so on.
I only tested on one computer. But if it's so critical, why not add a delay parameter to the SoundmidiSet, for example?
angros47 wrote: Oct 18, 2022 20:38 3) You mean the DSP sound, or the midi sound? Also, do you use it in the text or graphic mode?
MIDI (SoundMidiControl 103,"adlib")

The sound does not depend on text or graphics mode (bad in any mode).

---------

angros47! Your sequencer.bi file writed:

Code: Select all

const MaxTracks=16
I am not an expert in MIDI, but one person who knows a little about it said that this is a limitation and maybe not all midi files will be able to play. This is true?

P.S. I have a PCI card compatible with SB. The original sound in the game works correctly. This player: http://old-dos.ru/files/file_458.html?y ... e567161527 sounds right, but for some reason the volume level is low there. Please test sfx again on real hardware or pcem or 86box emulator. You will see that under DOS something does not work correctly.
VANYA
Posts: 1834
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47!

This is definitely your mistake! Problem with INTEGER type!
I just took and replaced all types of integer in almost all files with LONGINT and win32 music began to play correctly. I don't know exactly which places should be replaced and which should not. Everything needs to be properly researched here. I understood what the mistake is, and whether you correct the mistakes or leave it like this, decide for yourself. I think it's the same problem on DOS.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: Audio library for FreeBasic - Features

Post by angros47 »

If you can find which one is the cause of the issue, I'll be glad to update the library with the fix
VANYA
Posts: 1834
Joined: Oct 24, 2010 15:16
Location: Ярославль
Contact:

Re: Audio library for FreeBasic - Features

Post by VANYA »

angros47 wrote: Oct 19, 2022 9:27 If you can find which one is the cause of the issue, I'll be glad to update the library with the fix
Unfortunately, me need to have your knowledge of MIDI. You need to know the internal MIDI format and the structure of the library. You have it all, and I don't. You and the cards in your hands :)
Post Reply