MiniAudio-Wrapper for BlitzMax enables WASAPI Playback and Recording + MP3

Started by Midimaster, April 23, 2021, 14:12:00

Previous topic - Next topic

Midimaster

There is a new update 1.25 of the miniaudio wrapper avaiable. You will find it here:

https://github.com/MidimasterSoft/BlitzMax-Miniaudio-Wrapper

Here are some of the new functions/features:
  • Capturing Audio from Hardware USB-Devices upto 32 channels simultanously

  • New extended ExTaudioSample object for handling 32bit and multichannel Samples

  • Converting TAudioSamples into ExTAudioSamples

  • List and Select all avaiable Devices

  • Redesign of the wrapper for simplified use of the Library

  • Detailed Help-File in BlitzMax with a lot of examples

Here are three code examples related to capture:

Simply Capture into old BlitzMax TAudioSample
Code: BlitzMax
SuperStrict
Import mima.miniaudio

Graphics 400,300

' Setup of the device:
Const  HERTZ:Int = 48000

Global MiniAudio:TMiniAudio=New TMiniAudio

MiniAudio.OpenDevice( MiniAudio.DUPLEX, Miniaudio.FORMAT_S16, 1, HERTZ, MyCallBack)

' we need a TaudioSample for storing the incoming samples and a SHORT PTR to handle them
Global Recording:TAudioSample=CreateAudioSample( HERTZ*61,  HERTZ, SF_MONO16LE)  '60 seconds
Global RecordingRam:Short Ptr = Recording.Samples

Global WritePointer:Int, RecordMode%, Audio:TChannel
Local Music:TSound =LoadSound(Recording)
Audio=PlaySound(Music)

SetClsColor 255,255,255
Repeat
    Cls
DrawScreen
If MouseHit(1)
RecordMode=1-RecordMode
If RecordMode=0
Print "PLAY"
MiniAudio.StopDevice()
Local Music:TSound =LoadSound(Recording)
Audio=PlaySound(music)
Else
Print "RECORD"
ClearSample()
StopChannel Audio
WritePointer=0
MiniAudio.StartDevice()
EndIf
EndIf 
    Flip 0
Until AppTerminate()
Miniaudio.CloseDevice()


Function MyCallBack(a%, PlayBackBuffer:Short Ptr, RecordingBuffer:Short Ptr, Frames:Int)
' cares about not to overrun the TAudioSample-size:
If WritePointer>HERTZ*60 Then WritePointer=0

For Local i:Int=0 To frames-1
' fetch the sample:
Local value:Short   = RecordingBuffer[i]

' duplx it immediately:
PlayBackBuffer[i] = value

'and transfer it to the TAudioSample:
RecordingRAM[WritePointer+i] = value
Next
    WritePointer = WritePointer + Frames
End Function


Function ClearSample()
For Local i:Int=0 To HERTZ*60
RecordingRAM[i]=0
Next
End Function

Function DrawScreen()
...
End Function



Stereo Capture with high quality 32bit-Float and save to WAV-file
Code: BlitzMax
SuperStrict
Import mima.miniaudio

Graphics 400,300

' Setup of the device:
Const    HERTZ:Int = 48000
Const CHANNELS:Int = 2

Global MiniAudio:TMiniAudio=New TMiniAudio

Global Recording:ExTAudioSample = ExTAudioSample.Create(HERTZ*61,  HERTZ, MiniAudio.FORMAT_F32, CHANNELS)
Print Recording.format
Print Recording.Hertz
Print Recording.channels
MiniAudio.OpenDevice( MiniAudio.DUPLEX, Miniaudio.FORMAT_F32, CHANNELS, HERTZ, MyCallBack)


Global WritePointer:Int, RecordMode%
SetClsColor 255,255,255
Repeat
    Cls
DrawScreen
If MouseHit(1)
RecordMode=1-RecordMode
If RecordMode=0
Print "PLAY"
MiniAudio.StopDevice()
MiniAudio.SaveExTAudioSample "MySong.WAV", Recording
Else
Print "RECORD"
Recording.Clear
WritePointer=0
MiniAudio.StartDevice()
EndIf
EndIf 
    Flip 0
Until AppTerminate()
Miniaudio.CloseDevice()
End


Function MyCallBack(a%, PlayBackBuffer:Float Ptr, RecordingBuffer:Float Ptr, Frames%)
' cares about not to overrun the ExTAudioSample-size:
If WritePointer>HERTZ*60 Then WritePointer=0

For Local i:Int=0 To frames-1
' fetch the sample:
Local value:Float   = RecordingBuffer[i]

' duplx it immediately:
PlayBackBuffer[i] = value

'and transfer it to the TAudioSample:
Recording.SampleFloat[WritePointer+i] = value
Next
    WritePointer = WritePointer + Frames

End Function




Function DrawDrawScreen()
...
End Function



8-Channel Capture from USB-hardware device and save as 8track-file
Code: BlitzMax
' example to demonstrate MULTI-CHANNEL capturing audio with miniaudio
' and transfering datas to the NEW extended ExTAudioSample type
' Quality is FORMAT_S32 only for demonstration
' the DUPLEX will reduce the 8 channels to 2 "manually"
' when recording is stopped the ExTAudioSample is saved as a 8track-16bit WAV-file
' this make it necessary to convert from S32 to S16
SuperStrict
Import mima.miniaudio

Graphics 400,300

' Setup of the device:
Const    HERTZ:Int = 44100
Const CHANNELS:Int = 8

Global MiniAudio:TMiniAudio=New TMiniAudio

'only hardware devices can capture 8 channels. So connect your USB-device and select it with its ID:
Print   "Capture-Devices:"
Local i:Int
For Local DeviceName:String= EachIn MiniAudio.CaptureDevices()
Print  "ID=" + i + ": " + DeviceName
i=i+1
Next
Local MyUsbDevice:Int=1
Miniaudio.SelectDevices(-1,MyUsbDevice)


Global Recording:ExTAudioSample = ExTAudioSample.Create(HERTZ*61,  HERTZ, MiniAudio.FORMAT_S32, CHANNELS)
Print Recording.format
Print Recording.Hertz
Print Recording.channels
' use OpenDevice_II when Playback and Capture needs different parameters:
MiniAudio.OpenDevice_II( MiniAudio.DUPLEX, Miniaudio.FORMAT_S32, 2, Miniaudio.FORMAT_S32, CHANNELS, HERTZ, MyCallBack)


Global WritePointer:Int
Global RecordMode%

SetClsColor 255,255,255
Repeat
    Cls
DrawScreen
If MouseHit(1)
RecordMode=1-RecordMode
If RecordMode=0
Print "PLAY"
MiniAudio.StopDevice()
'this would easily save the wav with 32bit-INT:
MiniAudio.SaveExTAudioSample "MySong.WAV", Recording

' but we can also change the format during saving:
Local StreamID:MMStreamID = MiniAudio.OpenWavFile("My16bitsong.Wav" , Miniaudio.FORMAT_S16, CHANNELS, HERTZ)
MiniAudio.WriteWavFile StreamID, Recording.SampleInt, Recording.Frames, Recording.Format, CHANNELS, HERTZ
MiniAudio.CloseWavfile StreamID
Else
Print "RECORD"
Recording.Clear
WritePointer=0
MiniAudio.StartDevice()
EndIf
EndIf 
    Flip 0
Until AppTerminate()
Miniaudio.CloseDevice()
End


Function MyCallBack(a%, PlayBackBuffer:Int Ptr, RecordingBuffer:Int Ptr, Frames:Int)
If WritePointer > (HERTZ*60*CHANNELS) Then WritePointer=0

For Local i:Int=0 To Frames-1
For Local j:Int=0 To CHANNELS-1
' fetch the samples:
Local value:Int = RecordingBuffer[i*CHANNELS + j]

'and transfer it to the TAudioSample:
Recording.SampleInt[WritePointer+i*CHANNELS + j] = value
Next
' now route the 8 capture channels to only 2 playbak channels as you like:
' to prevent Distortion we divide each sample volume by 2
Local valueL:Int = RecordingBuffer[8*i+0]/2 + RecordingBuffer[8*i+1]/2 + RecordingBuffer[8*i+2]/2 + RecordingBuffer[8*i+3]/2
Local valueR:Int = RecordingBuffer[8*i+4]/2 + RecordingBuffer[8*i+5]/2 + RecordingBuffer[8*i+6]/2 + RecordingBuffer[8*i+7]/2
PlayBackBuffer[2*i+0] = valueL
PlayBackBuffer[2*i+1] = valueR
Next
    WritePointer = WritePointer + Frames*CHANNELS
End Function


Function DrawScreen()
...
End Function


Full example code can be downloaded form the GitHub repository!
...on the way to China.

Baggey

Absolutly awsome!

Got some more time to play with the sound side of my spectrum emulator

Ive down loaded mima.mod and extracted to  C:/Blitzmax/mod

when i try C:\BlitzMax\mod\mima.mod\miniaudio.mod\examples\PlayNoise.bmx

I am using Windows 10 and Blitzmax 1.5 I think it's called Vanilla

Code: blitzmax
SuperStrict
Import mima.miniaudio

Graphics 800,600

' Setup of the device:
Global MiniAudio:TMiniAudio=New TMiniAudio
MiniAudio.OpenDevice( MiniAudio.PLAYBACK, Miniaudio.FORMAT_S16, 1, 48000, MyCallBack)



' now start it:
MiniAudio.StartDevice()
Global NOISE%=0, Zeit%=MilliSecs()
Repeat
Cls
DrawText "Kick LEFT MOUSE for NOISE",100,100
If MouseDown(1)
NOISE=1
Else
NOISE=0
EndIf
Flip 0
Until AppTerminate()
Miniaudio.CloseDevice()
End


Function MyCallBack(a%, PlayBuffer:Short Ptr, RecordingBuffer:Short Ptr, Frames%)
' here you manipulate the sound:
If NOISE=1
For Local i%=0 To frames-1
PlayBuffer[i]=Rand(-32000,+32000)
Next
EndIf
End Function


Im getting Compile Error Can't find interface for module 'mima.miniaudio'



:-[

Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 4Ghz Quad core 32GB ram  2x1TB SSD and NVIDIA Quadro K1200 on 2 x HP Z24's . DID Technology stop! Or have we been assimulated!

Windows10, Parrot OS, Raspberry Pi Black Edition! , ZX Spectrum 48k, C64, Enterprise 128K, The SID chip. Im Misunderstood!

Baggey

So Ive tried in,

Blitzmax 1.5 ' It Dosent Work!  :'(

Works in Blitzmax NG  Thou? :-X

Any idea Why this dosent work in Blitzmax 1.5 or do the files need to be put into different places.

Kind regards Baggey
Running a PC that just Aint fast enough!? i7 4Ghz Quad core 32GB ram  2x1TB SSD and NVIDIA Quadro K1200 on 2 x HP Z24's . DID Technology stop! Or have we been assimulated!

Windows10, Parrot OS, Raspberry Pi Black Edition! , ZX Spectrum 48k, C64, Enterprise 128K, The SID chip. Im Misunderstood!

Midimaster

Did you already build the modules?
What were the complate error mesages you received?


Also Hardcoal had problems using it with BlitzMax 1.50. Did you find his post already?
https://www.syntaxbomb.com/index.php/topic,8467.msg347050348.html#msg347050348

We could change some code lines to make it more compatible to BlitzMAX 1.50 too. But afterwards Hardcoal reported more issues and canceled futher testings.

We think the problems now are related to the reduced multi-thread-capabilities of old BlitzMax.
See this answer from Col:
https://www.syntaxbomb.com/index.php/topic,8467.msg347050389.html#msg347050389

Maybe he can write something here?
...on the way to China.

Baggey

My error messages are from the Output window! I don't know how to get a more detailed Log?



Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 4Ghz Quad core 32GB ram  2x1TB SSD and NVIDIA Quadro K1200 on 2 x HP Z24's . DID Technology stop! Or have we been assimulated!

Windows10, Parrot OS, Raspberry Pi Black Edition! , ZX Spectrum 48k, C64, Enterprise 128K, The SID chip. Im Misunderstood!

Hardcoal

i tried to compile the  mima.miniaudio and it didnt work..

im using blitzmax vanilla

it gives me this error

Code: BASIC
►mima.miniaudio was scheduled to next iteration
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s: Assembler messages:
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:2: Error: junk at end of line, first unrecognized character is `,'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:3683: Error: junk at end of line, first unrecognized character is `,'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:93856: Error: junk at end of line, first unrecognized character is `,'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:93857: Error: junk at end of line, first unrecognized character is `,'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:93858: Error: junk at end of line, first unrecognized character is `,'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:96818: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:97458: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:97672: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:98312: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:98408: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:99048: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:99144: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:99784: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:99880: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:100520: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:108021: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:108661: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:108757: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:109397: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:109493: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:110133: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:110229: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:110869: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:112495: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:113135: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:115440: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:116049: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:122339: Error: no such instruction: `lzcntl %eax,%eax'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:148911: Error: junk at end of line, first unrecognized character is `,'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:166658: Error: junk at end of line, first unrecognized character is `,'
C:\Users\Hardc\AppData\Local\Temp\ccpKnT8e.s:166805: Error: junk at end of line, first unrecognized character is `,'
Build Error: failed to compile D:/PORTABLE/BlitzMax/mod/mima.mod/miniaudio.mod/miniaudiowrapper.c
►►►►mima.miniaudio was not built. See error information on latest attempt.
Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

Quote from: Hardcoal on August 06, 2021, 09:36:27
i tried to compile the  mima.miniaudio and it didnt work..

im using blitzmax vanilla



Sorry MiniAudio is for BlitzMax NG only. We tried to convert, but there a re too many problems. Maybe in the far future...

Problems are:

  • BlitzMax Vanilla has not full Threaded support

  • BlitzMax Vanilla does not compile with GCC-, but with FASM-Compiler

  • BlitzMax casting problems
...on the way to China.

Derron

Blitzmax vanilla compiles C stuff with GCC - hence the required MinGW installation (or a copy - if you use NGs BMK, which finds MinGW inside MinGW32 or MinGWx86 ...)

BlitzMax vanilla has threaded support - but requires you do do a threaded build while NG defaults to threaded builds.
Nonetheless the "buffer refill threads" could be kicked off from threads written in C.

For an example check these codes:
https://github.com/TVTower/TVTower/blob/master/source/Dig/base.sfx.soundmanager.freeaudio.bmx
https://github.com/TVTower/TVTower/blob/master/source/Dig/base.sfx.soundmanager.freeaudio.c

In essence you have the C code run your thread and on "tick" it calls back into the (non threaded built) blitzmax vanilla part.


bye
Ron

Midimaster

@derron

I can send you a list of problem I had when comping it with BlitzMax 1.50.

some of the problems I could already solve with compiler branches. But the list is still long, and always new message appear if old problems are solved.



Maybe we succedd in getting it run on BlitzMax 1.50 too...
...on the way to China.

Hardcoal

Its ok..

I dont expect people to convert things just for me lol..

ill manage somehow..

in the case of turning MP3 into wave ill just make something on NG and call it from BM 1.50
Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

#25
Combining drum instruments into a rhythm

Here is an example how we can build a drum machine with MINIAUDIO module.

The runable example shows a drum pattern with Bassdrum, Snare and Hihat. The Grid has 16 positions, where the user can switch ON/OFF single events

DrumMachine.png

you need the three sounds from the attachment

Code: BASIC
' DrumPlayer.bmx  (2024-09-01) via mima.miniaudio

' combines 3 drum instruments into one rthythm
SuperStrict
Import mima.miniaudio

Graphics 600,600

' the single instruments:
Global DrumSamples:TAudioSample[3]
   DrumSamples[0] = LoadAudioSample("BassDrumMono.ogg")
   DrumSamples[1] = LoadAudioSample("SnareMono.ogg")
   DrumSamples[2] = LoadAudioSample("HihatMono.ogg")
   Global Volume:Double[] =[0.3 , 0.4 , 0.2]


' the ringbuffer to combine them:
Global RingBuffer:TBank=CreateBank(100000)
Global RingPtr: Short Ptr = RingBuffer.Lock()
For Local i:Int=0 Until 50000
    RingPtr[i]=0
Next 
Global RingMax:Int=48000, RingPos:Int

' Setup of the device:
Global MiniAudio:TMiniAudio=New TMiniAudio
MiniAudio.OpenDevice( MiniAudio.PLAYBACK, Miniaudio.FORMAT_S16, 1, 48000, MyCallBack)

' now start it:
MiniAudio.StartDevice()


' the rhythm-description:
Global Delta:Int=150, Beat:Int=-1, NextStep:Int
Global Grid:Int[3,16]
SetGrid 0, "1000001010000000"
SetGrid 1, "0000100000001000"
SetGrid 2, "1010101010101010"


NextStep = MilliSecs()
Repeat
    Cls 
    CheckRhythm
    ShowGrid 
    User
    Flip 0
Until AppTerminate()
Miniaudio.CloseDevice()
End 


Function ShowGrid()
    SetColor 111,111,255
    DrawRect 100,100,481,91
    For Local i:Int=0 Until 3
        For Local b:Int=0 Until 16
            If Grid[i, b] =1
                SetColor 211,211,0
            Else
                SetColor 0,0,0            
            EndIf 
            DrawRect b*30+101,i*30+101,28,28
        Next 
    Next 
End Function  


Function User()
    If MouseHit(1)=0 Return
    Local x:Int = MouseX()
    Local y:Int = MouseY()
    If X<100 Or X>580 Return
    If Y<100 Or Y>190 Return
    x = (X-100)/30
    y = (Y-100)/30
    Grid[y,x]= 1-Grid[y,x]
End Function 

Function SetGrid(Nr:Int, Content:String)
    ' read the track info from a string
    For Local i:Int=0 Until 16
        Grid[Nr, i] = content[i] -48
    Next    
End Function 


Function CheckRhythm()
    ' is it is time for the next step?
    If MilliSecs()<NextStep Return 
    
    nextStep:+ Delta
    Beat = (Beat +1) Mod 16
    For Local i:Int=0 Until 3
        ' now chekc if a drum is at this beat:
        If Grid[i, Beat]
            InsertSample i
        EndIf 
    Next
End Function 

Function InsertSample(SoundNr:Int)
    ' puts the drum sample data into the ringbuffer
    Local DrumPointer: Short Ptr = DrumSamples[SoundNr].Samples
    Local SampleSize:Int         = DrumSamples[SoundNr].length
    Local Volume:Double          =  Volume[SoundNr]
    
    ' add 10msec delay not to risk crackles:
    Local WritePos:Int = RingPos + 480
    
    For Local i:Int=0 Until SampleSize
        Local Value:Int = ShortInt(DrumPointer[i]) * Volume
        WritePos = WritePos Mod RingMax
        RingPtr[WritePos] = RingPtr[WritePos] + Value
        WritePos:+1
    Next 
End Function 


Function ShortInt:Int( s:Int )
    Return (s Shl 16) Sar 16
End Function


Function MyCallBack(Void:Byte Ptr, Buffer:Short Ptr, VoidBuffer:Short Ptr, Frames:Int) nodebug
    Local ReadPos:Int =  RingPos
    ' now copy the ringbuffer into the miniaudio callback:
    For Local i:Int = 0 Until Frames
        ReadPos   = ReadPos Mod RingMax
         Buffer[i] = RingPtr[ReadPos]
        RingPtr[ReadPos] = 0
        Readpos:+1
    Next 
    RingPos = ReadPos Mod RingMax
End Function 


...on the way to China.

Hardcoal

i tried to complie Mima audio but it would not let me..
seems to have issues with this brl.stringbuilder thing..

Quote►mima.miniaudio was scheduled to next iteration
Compile Error: Can't find interface for module 'brl.stringbuilder'
[D:/PORTABLE/BlitzMax/mod/mima.mod/miniaudio.mod/miniaudio.bmx;33;1]
Build Error: failed to compile D:/PORTABLE/BlitzMax/mod/mima.mod/miniaudio.mod/miniaudio.bmx
►►►►mima.miniaudio was not built. See error information on latest attempt.

i tried to add this brl.stringbuilder and it caused problems

remember that i work on blitzmax vanilla..

im now busy trying to print with a printer..
It! also is not simple
im using "its" module.. i came to look for help in the forum..

im facing so many things at once..

but with the bit issue i made progress..

I copied your code  Ill try it when ill be able to run miniaudio module.

I want to ask many questions and im not sure if to ask them in a forum or in private.

I also not so sure what is my project goal.. but ill keep on doing it.

I have plans to make an equalizer too.. but i don't know how to separate wave lengths.

in general i made nice progress with my project..
i also sampled my own piano.. with the sampler i made


Things I've done:   https://itch.io/profile/hardcoal  [I keep improving them btw]

Midimaster

The MiniAudio module is a module for BlitzMax NG.

The Stringbuilder module is also part of BlitzMax NG

The MiniAudio.bmx code uses the Stringbuilder only one time to accelerate the creation of a long string by code.

This is the relevant code snippet:

Code: BASIC
'--- midiaudio.bmx lines 480-485:
Local sb:TStringBuilder = New TStringBuilder
For Local i%=0 To 999
    If speicher.PeekByte(i)=0 Exit                 
    sb.Append(Chr(speicher.PeekByte(i)))
Next
Local a$= sb.ToString()

this can be replaced by this code:

Code: BASIC
'--- midiaudio.bmx lines 480-485:
Local a:String
For Local i:Int=0 To 999
    If speicher.PeekByte(i)=0 Exit                 
    a = a + Chr(speicher.PeekByte(i))
Next

Now you can also remove line 33:

Code: BASIC
'--- midiaudio.bmx lines 32-34:
Import brl.linkedlist
'Import brl.stringbuilder
Import "miniaudiowrapper.c"
...on the way to China.

Baggey

Quote from: Midimaster on September 02, 2024, 14:34:11Combining drum instruments into a rhythm

Here is an example how we can build a drum machine with MINIAUDIO module.

The runable example shows a drum pattern with Bassdrum, Snare and Hihat. The Grid has 16 positions, where the user can switch ON/OFF single events

DrumMachine.png

you need the three sounds from the attachment

Code: BASIC
' DrumPlayer.bmx  (2024-09-01) via mima.miniaudio

' combines 3 drum instruments into one rthythm
SuperStrict
Import mima.miniaudio

Graphics 600,600

' the single instruments:
Global DrumSamples:TAudioSample[3]
   DrumSamples[0] = LoadAudioSample("BassDrumMono.ogg")
   DrumSamples[1] = LoadAudioSample("SnareMono.ogg")
   DrumSamples[2] = LoadAudioSample("HihatMono.ogg")
   Global Volume:Double[] =[0.3 , 0.4 , 0.2]


' the ringbuffer to combine them:
Global RingBuffer:TBank=CreateBank(100000)
Global RingPtr: Short Ptr = RingBuffer.Lock()
For Local i:Int=0 Until 50000
    RingPtr[i]=0
Next 
Global RingMax:Int=48000, RingPos:Int

' Setup of the device:
Global MiniAudio:TMiniAudio=New TMiniAudio
MiniAudio.OpenDevice( MiniAudio.PLAYBACK, Miniaudio.FORMAT_S16, 1, 48000, MyCallBack)

' now start it:
MiniAudio.StartDevice()


' the rhythm-description:
Global Delta:Int=150, Beat:Int=-1, NextStep:Int
Global Grid:Int[3,16]
SetGrid 0, "1000001010000000"
SetGrid 1, "0000100000001000"
SetGrid 2, "1010101010101010"


NextStep = MilliSecs()
Repeat
    Cls 
    CheckRhythm
    ShowGrid 
    User
    Flip 0
Until AppTerminate()
Miniaudio.CloseDevice()
End 


Function ShowGrid()
    SetColor 111,111,255
    DrawRect 100,100,481,91
    For Local i:Int=0 Until 3
        For Local b:Int=0 Until 16
            If Grid[i, b] =1
                SetColor 211,211,0
            Else
                SetColor 0,0,0            
            EndIf 
            DrawRect b*30+101,i*30+101,28,28
        Next 
    Next 
End Function  


Function User()
    If MouseHit(1)=0 Return
    Local x:Int = MouseX()
    Local y:Int = MouseY()
    If X<100 Or X>580 Return
    If Y<100 Or Y>190 Return
    x = (X-100)/30
    y = (Y-100)/30
    Grid[y,x]= 1-Grid[y,x]
End Function 

Function SetGrid(Nr:Int, Content:String)
    ' read the track info from a string
    For Local i:Int=0 Until 16
        Grid[Nr, i] = content[i] -48
    Next    
End Function 


Function CheckRhythm()
    ' is it is time for the next step?
    If MilliSecs()<NextStep Return 
    
    nextStep:+ Delta
    Beat = (Beat +1) Mod 16
    For Local i:Int=0 Until 3
        ' now chekc if a drum is at this beat:
        If Grid[i, Beat]
            InsertSample i
        EndIf 
    Next
End Function 

Function InsertSample(SoundNr:Int)
    ' puts the drum sample data into the ringbuffer
    Local DrumPointer: Short Ptr = DrumSamples[SoundNr].Samples
    Local SampleSize:Int         = DrumSamples[SoundNr].length
    Local Volume:Double          =  Volume[SoundNr]
    
    ' add 10msec delay not to risk crackles:
    Local WritePos:Int = RingPos + 480
    
    For Local i:Int=0 Until SampleSize
        Local Value:Int = ShortInt(DrumPointer[i]) * Volume
        WritePos = WritePos Mod RingMax
        RingPtr[WritePos] = RingPtr[WritePos] + Value
        WritePos:+1
    Next 
End Function 


Function ShortInt:Int( s:Int )
    Return (s Shl 16) Sar 16
End Function


Function MyCallBack(Void:Byte Ptr, Buffer:Short Ptr, VoidBuffer:Short Ptr, Frames:Int) nodebug
    Local ReadPos:Int =  RingPos
    ' now copy the ringbuffer into the miniaudio callback:
    For Local i:Int = 0 Until Frames
        ReadPos   = ReadPos Mod RingMax
         Buffer[i] = RingPtr[ReadPos]
        RingPtr[ReadPos] = 0
        Readpos:+1
    Next 
    RingPos = ReadPos Mod RingMax
End Function 



Can you please update DrumSounds.zip with "BassDrumMono.ogg" it's missing. :-X

Code: BASIC
DrumSamples[0] = LoadAudioSample("BassDrumMono.ogg")

Kind Regards Baggey
Running a PC that just Aint fast enough!? i7 4Ghz Quad core 32GB ram  2x1TB SSD and NVIDIA Quadro K1200 on 2 x HP Z24's . DID Technology stop! Or have we been assimulated!

Windows10, Parrot OS, Raspberry Pi Black Edition! , ZX Spectrum 48k, C64, Enterprise 128K, The SID chip. Im Misunderstood!

Midimaster


QuoteCan you please update DrumSounds.zip with "BassDrumMono.ogg" it's missing.


I'm so stupid! Sorry

now here is the ZIP with 4 files:

DRUMSOUNDS ZIP:
...on the way to China.