• Dear Cerberus X User!

    As we prepare to transition the forum ownership from Mike to Phil (TripleHead GmbH), we need your explicit consent to transfer your user data in accordance with our amended Terms and Rules in order to be compliant with data protection laws.

    Important: If you accept the amended Terms and Rules, you agree to the transfer of your user data to the future forum owner!

    Please read the new Terms and Rules below, check the box to agree, and click "Accept" to continue enjoying your Cerberus X Forum experience. The deadline for consent is April 5, 2024.

    Do not accept the amended Terms and Rules if you do not wish your personal data to be transferred to the future forum owner!

    Accepting ensures:

    - Continued access to your account with a short break for the actual transfer.

    - Retention of your data under the same terms.

    Without consent:

    - You don't have further access to your forum user account.

    - Your account and personal data will be deleted after April 5, 2024.

    - Public posts remain, but usernames indicating real identity will be anonymized. If you disagree with a fictitious name you have the option to contact us so we can find a name that is acceptable to you.

    We hope to keep you in our community and see you on the forum soon!

    All the best

    Your Cerberus X Team

Snippet How to handle files in Cerberus (Sound example)

Wingnut

Well-known member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
1,414
Here's an example how you can use what I showed in an earlier file-tutorial.

This time we create sounds on the fly. This means we can create sound-effects and music dynamically.

We can synthesize media and we can hide media.

This is tested on macOS.

Cerberus:
' Create a soundfile that's supported across all platforms

Strict
Import mojo2
Import brl
Import OS

Function Main : Int()
    New Game()
    Return 0
End

Class Game Extends App

    Field canvas : Canvas
    Field bits:Int = 16, samplerate : Int = 44100
    Field samples : Int, waveform : Int[]
 
    ' Synthesizer parameters
    Field seconds : Float = 2
    Field hz : Float = 440.0
    Field amplitude : Int = 32000
    Field flag:Int = 0
 
    Field noise:Sound
    
    Method OnCreate : Int()
        canvas = New Canvas
        samples = samplerate * seconds ; waveform = New Int[samples]

        ' Generate sound (Generates a simple sinewave of said hz and amplitude, with a duration of said seconds)
        For Local i:Int = 0 Until samples
            Local t:Float = Float(i) / Float(samplerate) ; waveform[i] = amplitude * Sin(hz * t * 360.0)
        Next
        Return 0
    End
 
    Method OnRender : Int()
        canvas.Clear
    
        ' Save sound
        If flag = 0
            SaveWav( "cerberus://data/mysound.wav")
            flag = 1 ; Print "File saved"
        Endif

        ' Load sound   
        If flag = 1
            noise = LoadSound("mysound.wav")
            flag = 2 ; Print "File loaded"
        Endif
    
        ' Play sound
        If flag = 2 And MouseDown(0) Then PlaySound noise,0 ; Print "File played"
    
        canvas.Flush
        Return 0
    End
 
    Method SaveWav:Void(path:String) ' SAVE 16-BIT .WAV FILE
        Local bits:Int = 16
        Local channels:Int = 1
        Local samples:Int = waveform.Length
        Local bytes:Int = bits / 8
         Local sum : Int = samples * bytes
        Local dump := New FileStream(path,"w")
        dump.WriteString "RIFF" ' A simple CD Quality Header (16-bit, 44.1 Khz)
        dump.WriteInt sum + 36
        dump.WriteString "WAVE"
        dump.WriteString "fmt "
        dump.WriteInt 16
        dump.WriteShort 1 ' 16-bit
        dump.WriteShort channels
        dump.WriteInt samplerate
        dump.WriteInt samplerate * channels * bytes
        dump.WriteShort channels * bytes
        dump.WriteShort bits
        dump.WriteString "data"
        dump.WriteInt sum

        For Local temp:Int = 0 To samples-1 ' Waveform is stored here
            dump.WriteShort(waveform[temp])
        Next

        If sum Mod 2 Then dump.WriteByte 0 ' Wav files wants to be even in length
        dump.Close
    End

End
 
Last edited:
For completeness sound-wise, here's how you can create a stereo-sound.

This demo will play different tones in left and right speakers (use headphone to be able to hear each of them).

Cerberus:
' Create a stereo soundfile that's supported across all platforms

Strict
Import mojo2
Import brl
Import OS

Function Main : Int()
    New Game()
    Return 0
End

Class Game Extends App

    Field canvas : Canvas
    Field bits:Int = 16, samplerate : Int = 44100
    Field samples : Int, waveform : Int[], waveform2 : Int[] ' waveform is left channel, waveform2 is right channel
   
    ' Synthesizer parameters
    Field seconds : Float = 2
    Field hz : Float = 440.0
    Field amplitude : Int = 32000
    Field flag:Int = 0
   
    Field noise:Sound
       
    Method OnCreate : Int()
        canvas = New Canvas
        samples = samplerate * seconds ; waveform = New Int[samples] ; waveform2 = New Int[samples]

        ' Generate sound (Generates a simple sinewave of said hz and amplitude, with a duration of said seconds)
        For Local i:Int = 0 Until samples  
            Local t:Float = Float(i) / Float(samplerate) ; waveform[i] = amplitude * Sin(hz * t * 360.0)
        Next
       
        hz = 440 * 2
        ' Generate sound (Generates a simple sinewave of said hz and amplitude, with a duration of said seconds)
        For Local i:Int = 0 Until samples  
            Local t:Float = Float(i) / Float(samplerate) ; waveform2[i] = amplitude * Sin(hz * t * 360.0)
        Next
       
        Return 0
    End
   
    Method OnRender : Int()
        canvas.Clear
       
        ' Save sound
        If flag = 0
            SaveWav( "cerberus://data/mysound.wav")
            flag = 1 ; Print "File saved"  
        Endif

        ' Load sound      
        If flag = 1
            noise = LoadSound("mysound.wav")
            flag = 2 ; Print "File loaded"
        Endif
       
        ' Play sound
        If flag = 2 And MouseDown(0) Then PlaySound noise,0 ; Print "File played"
       
        canvas.Flush
        Return 0
    End
   
    Method SaveWav:Void(path:String) ' SAVE 16-BIT .WAV FILE
        Local bits:Int = 16
        Local channels:Int = 2
        Local samples:Int = waveform.Length
        Local bytes:Int = bits / 8
         Local sum : Int = samples * bytes
        Local dump := New FileStream(path,"w")
        dump.WriteString "RIFF" ' A simple CD Quality Header (16-bit, 44.1 Khz)
        dump.WriteInt sum + 36
        dump.WriteString "WAVE"
        dump.WriteString "fmt "
        dump.WriteInt 16
        dump.WriteShort 1 ' 16-bit
        dump.WriteShort channels
        dump.WriteInt samplerate
        dump.WriteInt samplerate * channels * bytes
        dump.WriteShort channels * bytes
        dump.WriteShort bits
        dump.WriteString "data"
        dump.WriteInt sum

        For Local temp:Int = 0 To samples-1 ' Waveform is stored here
            dump.WriteShort(waveform[temp])
            dump.WriteShort(waveform2[temp])
        Next

        If sum Mod 2 Then dump.WriteByte 0 ' Wav files wants to be even in length
        dump.Close
    End

End
 
Create a stereo soundfile that's supported across all platforms
Jimmy, this one is not for html5 target, right? can't run. need OS:(
 
Not out of the box, as HTML5 is protected against any kind of clever file-usage to make sure that websites are secure.

I'm a web developer coder so just lemme think about this what can be done. But don't hold your breath as I'm up til here in work.
 
  • Like
Reactions: mag
Jimmy, this one is not for html5 target, right? can't run. need OS:(
I wanted to ask exactly what do you need?

You might wanna do one of these I was thinking:

* Saving files to the server needs PHP/Node/Deno on the backend, but there's no limits on size or access.

* You could also fake a file system using State I guess but the HTML5 platform implements state using WebStorage so it will have a total size limit. Without knowing the details I would say the limit is usually around 5MB by default in that case.

I'm not sure If I can come up with another way just yet but do you need to generate media or data programatically, or do you wanna hide it? Or are you after just a regular load/save function that does not need user to interact?
 
This time we create sounds on the fly. This means we can create sound-effects and music dynamically.
Actually, I'm interested to try your code about dynamic sound. I run in html5 because I don't set other targets on this pc.
I see rem "Create a soundfile that's supported across all platforms" which makes me want to try. I don't have a particular need at this time.
 
I see, yes and that was a clumpsy expression coming from me, what I meant with the comment was that the actual soundformat is designed especially to be compatible across all platforms.

On top of that comes of course the fact that HTML5 don't have a simple way to load files.

Same goes with LoadImage of course but with with images you can go around this easly. You can see a good example of that in the guide I wrote just the day before. There you basically use a custom format and spoonfeed the canvas.

You don't have an sound API yet that allow this kind of thing so you need LoadSound, so I don't have a complete solution right now.

Let's say that we build a virtual filesystem, but the problem now will be that LoadSound will not be able to see this virtual filesystem. But we have permanent files :

virtualfilesystemtest.cxs
Cerberus:
Strict
Import Mojo2
Import filesystem

Function Main:Int()
    New testApp
    Return 0
End Function

Class testApp Extends App

    Field fileHandler:FileSystem
    Field canvas:Canvas
    Field flag:Int
 
    Method OnCreate:Int()
 
        canvas=New Canvas ; SetSwapInterval 1 ; SetUpdateRate 0
   
        fileHandler = New FileSystem
        Local stream:FileStream
        Local n:Int

         If fileHandler.FileExists("test/test.bin") = False ' Only save if file does not already exist
        Print "file did not exsit so saving now!"
        ' Save file
        stream = fileHandler.WriteFile("test/test.bin")
        stream.WriteString("Hello")
        stream.WriteInt(1234536343)
        stream.WriteString("Bye!")
        flag = 1
        End
   
        ' Load file
        fileHandler.SaveAll()
        fileHandler.ListDir()  
        stream = fileHandler.ReadFile("test/test.bin")
        If stream
            Print stream.ReadString()
            Print stream.ReadInt()
            Print stream.ReadString()
        EndIf
        Print "tried load file!"
   
        Return 0
    End Method
 
    Method OnRender:Int()
        canvas.Clear
        canvas.DrawText "Flag is now : "+String(flag),100,100
        If MouseDown(0) Then If fileHandler.FileExists("test/test.bin") Then canvas.DrawText "File exist",100,200
   
        ' Load test within a frame
        If MouseDown(1)
        fileHandler.SaveAll() ; fileHandler.ListDir()  
        Local stream := fileHandler.ReadFile("test/test.bin")
        If stream
            canvas.DrawText stream.ReadString(),0,0
            canvas.DrawText String(stream.ReadInt()),0,20
            canvas.DrawText stream.ReadString(),0,40
        EndIf
        Endif

        canvas.Flush
        Return 0
    End Method

End Class

filesystem.cxs
Cerberus:
Strict
Import mojo2

Class FileSystem Extends Conversion
    Field _header:String = "MKYDATA", fd:String, index:StringMap<FileStream>
    Method New()
        LoadAll()
    End
    Method WriteFile:FileStream(fn:String)
        Local f:FileStream = New FileStream ; f.fn = fn.ToLower() ; f.fileptr = 0 ; index.Insert(f.fn.ToLower(),f)
        Return f
    End
    Method ReadFile:FileStream(fn:String)
        fn = fn.ToLower() ; Local f:FileStream = index.ValueForKey(fn) ; f.fileptr = 0
        Return f
    End
    Method FileExists:Bool(fn:String)
        If index.Contains(fn.ToLower()) Then Return True Else Return False
        Return False
    End
    Method ListDir:Void()
        Local fn:String, stream:FileStream ; Print "Directory:"
        For fn = EachIn index.Keys()
            stream = index.ValueForKey(fn) ; Print fn + " " + stream.data.Length() + " byte(s)."
        Next
    End
    Method DeleteFile:Void(fn:String)
        If index.Contains(fn.ToLower()) Then index.Remove(fn.ToLower())
    End
    Method SaveAll:Void()
        Local f:FileStream ; fd = _header ; fd += IntToStr(index.Count())
        If index.Count() > 0
        For f = EachIn index.Values()
            fd += IntToStr(f.fn.Length())
            If f.fn.Length() > 0 Then fd += f.fn
            fd += IntToStr(f.data.Length())
            If f.data.Length() > 0 Then fd += f.data
        Next
        End
        SaveState(fd)
    End
    Method LoadAll:Void()
        Local numFiles:Int,stream:FileStream, len:Int, ptr:Int
        fd = LoadState() ; index = New StringMap<FileStream>
        If fd.Length() > 0
            If fd.StartsWith(_header)
                index.Clear() ; ptr += _header.Length() ; numFiles = StrToint(fd[ptr..ptr + 3]) ; ptr += 3
                If numFiles > 0
                    For Local n:Int = 1 To numFiles
                        stream = New FileStream
                        len = StrToint(fd[ptr..ptr + 3]) ; ptr += 3
                        If len > 0 Then stream.fn = fd[ptr..ptr+len] ; ptr += len
                        len = StrToint(fd[ptr..ptr + 3]) ; ptr += 3
                        If len > 0 Then stream.data = fd[ptr..ptr + len] ; ptr += len
                        index.Insert(stream.fn,stream)
                    Next
                End
            End
        Else
            SaveState("")
        End
    End
End

Class FileStream Extends Conversion
    Field fn:String, fileptr:Int, data:String
    Method ReadInt:Int()
        Local r:String = data[fileptr..fileptr + 3] ; fileptr += 3
        Return StrToint(r)
    End
    Method WriteInt:Void(val:Int)
        data += IntToStr(val)
    End
    Method ReadString:String()
        Local r:String, strLen:Int = StrToint(data[fileptr..fileptr + 3]) ; fileptr += 3
        If strLen > 0 Then r = data[fileptr..fileptr + strLen] ; fileptr += strLen
        Return r
    End
    Method WriteString:Void(val:String)
        data += IntToStr(val.Length())
        If val.Length() > 0 Then data += val
    End
End

Class Conversion
    Method IntToStr:String(val:Int)
        Local r:String = String.FromChar($F000 | ((val Shr 20) & $0FFF) )
        r += String.FromChar($F000 | ((val Shr 8) & $0FFF)) ; r += String.FromChar($F000 | (val & $00FF))
        Return r
    End
    Method StrToint:Int(val:String)
        Return ((val[0] & $0FFF) Shl 20) | ((val[1] & $0FFF) Shl 8) | (val[2] & $00FF)
    End
End
 
Last edited by a moderator:
You don't have an sound API yet that allow this kind of thing so you need LoadSound, so I don't have a complete solution right now.
Ok I understood the problem. The html5 is triicky to impliment all this because of the file issue.

May be one day, someone can create API to poke data into sound memory. PCM data maybe, I don't know. I'm not sure how to do that without using js library.
 
Back
Top Bottom