Menu

#35 from sounds to music

Unstable_(example)
open
nobody
None
5
2018-11-10
2016-10-02
No

Hello!

This update brings a new dimension of fun and creativity for BASIC users. A great one, I think.
Till now user can play only a list of sounds, but how about playing multiple sounds in the same time? This can allow users to use harmonics in songs (multiple voices).

sound {{Hz, msec, Hz, msec, Hz, msec, ...},{Hz, msec, Hz, msec, Hz, msec, ...},...}

Just download attachements and compile a new version.

New effects:

dim a(2,100)

for f=0 to 99 step 2
a[0][f]=f*6+300
a[0][f+1]=30
a[1][f]=(600-f*6)+300
a[1][f+1]=30
next f
sound a

sound {{440,300,0,300,440,300,0,300,440,300,0,300,440,300},{600,2100}}

Do you remember the post card I send to you for your birthday? https://www.facebook.com/wryren/posts/1371064099574636
Check the original song and then play this simple update.

sound {{524,250,0,5,524,250,0,5,588,500,0,5,524,500,0,5,698,500,0,5,660,1000,0,5,524,250,0,5,524,250,0,5,588,500,0,5,524,500,0,5,784,500,0,5,698,1000,0,5,524,250,0,5,524,250,0,5,1046,500,0,5,880,500,0,5,698,500,0,5,660,500,0,5,588,500,0,5,932,250,0,5,932,250,0,5,880,500,0,5,698,500,0,5,784,500,0,5,698,1000,0,5},{524,12120}}

I just added a new list/voice {524,12120} - just a note during the song. But think if you add some true and different harmonics during the song, not only a sound. And the best part is that you can add more than 2 voices to the song.

Check arpeggio

sound {{262,8000},{0,1000,330,7000},{0,2000,392,6000},{0,3000,523,5000}}

If you use multiple lists, those can have different lenghts.

I also make sounds stop when user stop the program.

Don't forget to add a comment to help page about the pause in songs (frequency 0).

PS If you want to amplify a voice, just duplicate it. In next example are just 2 notes. The second is played on multiple voices. The effect is an amplification.

sound {{262,8000},{0,1000,330,7000},{0,2000,330,6000},{0,3000,330,5000},{0,3000,330,4000},{0,3000,330,3000}}

Respectfully,
Florin Oprea

3 Attachments

Discussion

  • Jim Reneau

    Jim Reneau - 2016-10-04

    Florian,
    I like the idea and think it has merit. The syntax of the SOUND statement was changed when we added nested {{},{}} to allow for the sound pairs to be grouped together. This makes the syntax like POLY and the other graphics statements.

    sound { { frequency1, duration1 }, { frequency2, duration2 }, ... }

    I like the idea but we need to come up with a different syntax. What do you think of something like:

    POLYSOUND array, array, array...

    So your arpeggio becomes
    POLYSOUND {262,8000}, {0,1000,330,7000}, {0,2000,392,6000}, {0,3000,523,5000}
    or
    POLYSOUND {{262,8000}}, {{0,1000},{330,7000}}, {{0,2000},{392,6000}}, {{0,3000},{523,5000}}

    ?

     
  • Florin Oprea

    Florin Oprea - 2016-10-04

    Hi Jim,

    If this was designed as a single update, POLYSOUND would be sounded so good... :)
    I will reveal my vision: to redesign sound section into powerfull and smart commands like this:

    Sound (play a sound and wait to finish by default)

    sound f,d,f,d //frequency, duration...
    sound {f,d,f,d}
    sound {{f,d,f,d}{f,d,f,d}} //mixed sounds
    sound a[] //mixed sounds
    sound file_name //local or over the network
    

    SoundPlay (play a sound asynchronously)
    Just like above. So, even generated sounds/music to be played asynchronously and controlled while played (stop, pause, play, seek etc.).

    soundplay f,d,f,d //frequency, duration...
    soundplay {f,d,f,d}
    soundplay {{f,d,f,d}{f,d,f,d}} //mixed sounds
    soundplay a[] //mixed sound
    soundplay file_name //local or over the network
    

    If you see, the WAVplay (which is no longer refers to WAV files for a long time) is transformed into a powerfully soundplay command. Users can still use the command WAVplay but they will receive a warning: command WAVplay is obsolete, please use SoundPlay instead.

    So, sounds (generated or loaded) can be played asynchronously. Now we can control all of them with commands: SoundPause, SoundStop, SoundSeek, SoundWait, SoundPos, SoundState.
    Those commans are the old WAV... commands, but will control both generated sounds and loaded sounds also (mp3, wav, etc.) without making differences between them.
    User will don't care if backgroung music is generated at begining of program or change this into a loaded file. The program willl run just changing sound source (a list of sounds or a file name).

    This is a first step....

    The second step...
    I knew that WAVplay led to memory leaks when multiple sounds are played in the same time (overlaping). This is my note from that moment:

    file$ = "1.wav"
    loop:
    wavplay file$
    pause 2
    goto loop
    #overlaping sounds
    

    I said nothing because I liked the effect. I also saw when you fixed it. Since then you can play only one sound at the time. For a game it is not a good thing.
    I had to find a solution to keep the ability to play multiple sounds at once (explosions, collisions of asteroids and many other sounds that are heard at the same time in any game).
    But in the same time the user must control all those sounds (example: the end of the game).

    Hmmm.....

    My first thought was to use an ID for each sound.
    (In my old notes)

    soundplay file_name - returns sound_id. Is user choice to use this id or not
    
    [sound_id] is optional. If sound_id is not given, last sound played (in sounds stack) is used
    
    soundpause [sound_id]
    soundpos [sound_id]
    soundseek pos [, sound_id]
    soundstate [sound_id]
    soundstop [sound_id]
    soundwait [sound_id]
    soundplay [sound_id] #resume play
    

    Because commands SOUND.... == WAV... + WARNING this is very convenient. (Example soundpause == wavpause + warning // soundplay(file) == wavplay(file) + warning.
    Compatibility remains 100%.
    But the problem was how to receive the [sound_id] from soundplay preserving compatibility?

    Let's design a whole sound system with powerfull commands.
    Compatibility is a must, even with warnings to change to the new sintax.

    The interpreter will look like this:

    case OP_WAVPLAY:
        //warning
    case OP_SOUNDPLAY: {
        //code
    }
    

    Just think about this.
    Now we got only a sound in a game. If you play another one the firt will stop or you will lose control over it.
    We must implement the posibility for multiple sounds in the same time with full control over all of them.
    And if we unite the generated sounds with loaded ones... this becomes perfect! This because the control commands (stop, pause, seek, state... etc.) for generated asynchronously music will be the same as the loaded music.

    What do you think?

    Florin.

     

    Last edit: Florin Oprea 2016-10-04
  • Jim Reneau

    Jim Reneau - 2016-10-15

    I am playing with your code from the last three updates, right now. I like your proposal of the 4th but I would like to make it work like the "Open" statement.

    I will use the following shorthand in the syntax, I propose.
    A1 - One dimensional array of F,D pairs
    L1 - One dimensional list {} of F,D pairs {f,d,f,d...}
    A2 - Two dimensional array of rows of F,D pairs
    L2 - Two dimensional list of F,D paris {{f,d},{f,d}...}
    VOICE = A1 | L1 | A2 | L2

    Synchronous Sounds

    In version before your polyvoice changes BASIC-256 allowed the following:
    sound f,d,f,d... //frequency, duration... (EVEN NUMBER OF ARGUMENTS)
    sound VOICE // currently support all 4 different syntaxes

    Add (would handle the current 4 syntaxes of sound and allow for polyvoice)
    sound VOICE [, VOICE] // single or poly voice sounds

    Add
    sound url

    Asynchronous Sounds

    SoundPlay (play a sound asynchronously) - If no sound number is specified then use sound 0

    Add freesound() function that would give a sound handle number like freefile() will return a free file number.

    soundplay f,d,f,d... //frequency, duration... (EVEN NUMBER OF ARGUMENTS)
    soundplay soundnumber, f,d,f,d... //frequency, duration... (ODD NUMBER OF ARGUMENTS)
    soundplay VOICE [,VOICE] // single or poly voice sounds
    soundplay soundnumber, VOICE [,VOICE]
    soundplay url - local file or url
    soundplay soundnumber, url

    [sound_id] is optional (sound 0 will be used if not specified)

    soundpause [sound_id]
    soundpos ( [sound_id] ) //function
    soundseek [sound_id ,] pos // Make syntax same as seek
    soundstate ( [sound_id] ) //function
    soundstop [sound_id]
    soundwait [sound_id]
    soundplay [sound_id] #resume play

    WAVxxx vs SOUNDxxx Statements

    I would not add a warning initially but would make the REGEX in the bison/yacc and syntax highliter accept either. In the documentation, we would change it to SOUNDxxx and make note that the WAVxxx is discouraged and may be deprecated in the future.

    What do you think about this?

     

    Last edit: Jim Reneau 2016-10-15
  • Jim Reneau

    Jim Reneau - 2016-10-15
     
  • Jim Reneau

    Jim Reneau - 2016-10-15

    Added to 1.99.99.71 withnotes about breaking sound{{},{}...} and sound with 2d array - Need to continue discussion and resolve polyvoice and new sound system.

     
  • Florin Oprea

    Florin Oprea - 2016-10-16

    Hi Jim,

    Generated Sounds

    sound VOICE [, VOICE] // single or poly voice sounds
    

    I like this idea. It is intuitive and logically.
    The first time I saw it I said: That's it! Jim has broke the code! :)

    sound {Hz,ms,Hz,ms,Hz,ms} , {{Hz,ms},{Hz,ms}}
    

    As you say, this command should play a mixed sound with two channels (voices)

    There is a single flaw: array arguments.
    A1 - One dimensional array of F,D pairs
    A2 - Two dimensional array of rows of F,D pairs

    If we make a composed sound for an event (eg: fire sound for a space ship, or a collision sound) there is no way to easy play this sound later or in multiple places of game.
    If the user wants to modify the sound, user should be able to change in one place, not everywhere.

    Test: Put the sound command from above/below into an array to use in multiple places of program:

    sound {Hz,ms,Hz,ms,Hz,ms} , {{Hz,ms},{Hz,ms}}

    It is impossible. So, the only way is to drop the current interpretation "A two dimensional array may have any number of rows but must have two columns."

    This is ok: A1 - One dimensional array of F,D pairs - one channel/voice

    And if we agree that one row is a sequence of sounds (one channel/voice), multiple rows should keep multiple sequences of sounds (channels/voices).

    Ok, let's transform those arrays into lists of elements:

    A1 - One dimensional array of F,D pairs - one row (one channel/voice)
    it is equal to
    {F,D,F,D,F,D...}

    and

    A2 - Two dimensional array - multiple rows (multiple channels/voices)
    it is equal to
    {{F,D,F,D,F,D...},{F,D,F,D,F,D...},{F,D,F,D,F,D...}}

    This syllogism will lead us to the same proposed syntax for sound:
    sound single_row_array #single voice
    sound {F,D,F,D,F,D...} #single voice
    sound multiple_rows_array #multiple voices
    sound {{F,D,F,D,F,D...},{F,D,F,D,F,D...},{F,D,F,D,F,D...}} #multiple voices

    If we will keep this kind of notation {{f,d},{f,d}...} for one voice, then we be unable to store a poly sound into an array. This notation is more educational and esthetical than practical, I think.

    But there is a new (bright) dimension by doing this.
    Probable you think that this change will break the constancy for poly, stamp and sprite poly commands. Which is correct and yet it is not. I think this will bring more power to those commands.

    Ok... Let's take a look at stamp command (the most complex from those 3 commands):

    stamp x_position, y_position, { x1, y1, x2, y2, x3, y3 ... }
    stamp x_position, y_position, { {x1, y1}, {x2, y2}, {x3, y3} ... }
    

    If we apply the same constancy as in case of sound command (1 row = 1 sound; n rows = n sounds) then we will have (1 row = 1 poly; n rows = n poly):

    stamp formed by one poly (one row)
    stamp x_position, y_position, {x1,y1,x2,y2,x3,y3...}
    stamp formed by multiple poly (multiple rows)
    stamp x_position, y_position, {{x1,y1,x2,y2,x3,y3...},{x1,y1,x2,y2,x3,y3...},...}

    This command will draw a cross target in just one move (posibility to scale and rotate):

    stamp 150, 15, .5, .2, {{45,0,55,0,55,40,45,40},{45,60,55,60,55,100,45,100},{0,45,0,55,40,55,40,45},{60,45,60,55,100,55,100,45}}
    

    To see this simple draw, just decompose the command into:

    stamp 150, 15, .5, .2, {45,0,55,0,55,40,45,40}
    stamp 150, 15, .5, .2,  {45,60,55,60,55,100,45,100}
    stamp 150, 15, .5, .2,  {0,45,0,55,40,55,40,45}
    stamp 150, 15, .5, .2,  {60,45,60,55,100,55,100,45}
    

    I think that poly, stamp and sprite poly commands could adopt the same interpretation of arrays/lists in syntax as sound command:
    1 row = 1 element (sound/poly)
    n rows = n elements (sounds/poly)

    I repeat, the sound VOICE [, VOICE] is very, very attractive and it's make sense, but we loose the ability to store a polysound into an array, which is a big lose, I think.

    Tomorrow I will write you about Asynchronous Sounds.

    We do not need to rush about this. We have to get the best and most powerful syntax and this will not be an easy task. But I have a good feeling about this. :)

    Respectfully,
    Florin Oprea

     
  • Florin Oprea

    Florin Oprea - 2016-11-19

    Jim:

    I am working on the 2.0 edition of the book and keep having to go back and check/fix completed chapters with the changes. Can we now focus on the SOUND/WAVPLAY unification? Once this is done I want to move to 2.0 and slow down on the changes.

    It is my understanding that you are going to work on unifying the BasiMediaPayer and Sound objects into one object that will play single voice, polyphonic, an sound files. That is will support synchronous and asynchronous operation with all of the WAVXXXX statements working.

    This may take creating a new sound player statements with a single unified syntax. I propose something like:

    SOUNDOPEN SOUNDNUM, SYNC, VOICEARRAY or URL[, VOICEARRAY or URL...]
    x = SOUNDLENGTH(SOUNDNUM)
    SOUNDPAUSE(SOUNDNUM)
    SOUNDPLAY(SOUNDNUM) - Start an async or restart a sync sound
    x = SOUNDPOS(SOUNDNUM)
    SOUNDSEEK(SOUNDNUM, POSITION_MS)
    x = SOUNDSTATE(SOUNDNUM)
    x = SOUNDPOS(SOUNDNUM) - Position in MS
    SOUNDSTOP(SOUNDNUM)

    Where SOUNDNUM is like a file, database, or network handle number. This way multiple sounds (up to a reasonable) limit may be loaded and manipulated. SYNC is a boolean and TRUE means wait for completion, VOICEARRAY is voice data (array or list) {f,d,f,d...} or {{f,d},{f,d}...}, and URL is a string file name of a WAV or MP3 stream.

    WE MUST be sure tht the old statements WAVXXXX and SOUND comtinue to work the way they always have. There is a lot of code out there using the old way.

     
  • Florin Oprea

    Florin Oprea - 2016-11-19

    Hi Jim,

    Little Mozart

    For a great fidelity of sound, now SOUND accept frequences in float format (duration too)
    The most usefull upgrade that bring fun is that SOUND accept notes in human notation
    - Neo-Latin: Do, Re, Mi, Fa, Sol, La, Si
    - English: C,D,E,F,G,A,B
    - German: C,D,E,F,G,A,H
    - Byzantine: Ni, Pa, Vu, Ga, Di, Ke, Zo
    Format is the standard notaion: Note[octave=4][accidentals]
    Octave is 4, by default. So, SOUND "A", 1000 will play the A/La note (440Hz). This is the same as SOUND "A4", 1000.

    Also, the frequences are perfect. For more informations check: https://en.wikipedia.org/wiki/Musical_note

    Accidentals can be:
    b - (flat) lowers it by the a semitone
    # - (sharp) raises a note by a semitone
    Rare:
    bb - (double-flat) lowering it by two semitones
    ## - (double-sharp / non standard notation) raising the frequency by two semitones

    d=1000
    a[]={"C4", d/4, "D4", d/4, "F4", d/4, "D4", d/4, "A4", d/4, 0, d/4, "F4", d/4, "D4", d/4, "G4", d/4, 0, d/4, "G4", 1, "F4", 1, 0, d/2}
    sound a
    pause 2
    
    d=250
    #'Ode to Joy',
    o={"E4", d, "E4", d, "F4", d, "G4", d, "G4", d, "F4", d, "E4", d, "D4", d, "C4", d, "C4", d, "D4", d, "E4", d, "E4", d, "D4", d, "D4", d, "E4", d, "E4", d, "F4", d, "G4", d, "G4", d, "F4", d, "E4", d, "D4", d, "C4", d, "C4", d, "D4", d, "E4", d, "D4", d, "C4", d, "C4", d}
    sound o
    pause 2
    
    d=700
    #'Twinkle Twinkle, Little Star'
    t={"C4", d, "G4", d, "A4", d, "G4", d/2, 0, d/2, "F4", d, "E4", d, "D4", d, "C4", d/2, 0, d/2, "G4", d, "F4", d, "E4", d, "D4", d/2, 0, d/2, "G4", d, "F4", d, "E4", d, "D4", d/2, 0, d/2, "C4", d, "G4", d, "A4", d, "G4", d/2, 0, d/2, "F4", d, "E4", d, "D4", d, "C4", d/2, 0, d/2}
    sound t
    pause 2
    
    d=250
    #'Row, Row, Row Your Boat'
    r={"C4", d, "C4", d, "C4", d, "D4", d, "E4", d, "E4", d, "D4", d, "E4", d, "F4", d, "G4", d, "C5", d, "C5", d, "C5", d, "G4", d, "G4", d, "G4", d, "E4", d, "E4", d, "E4", d, "C4", d, "C4", d, "C4", d, "G4", d, "F4", d, "D4", d, "E4", d, "C4", d}
    sound r
    pause 2
    
    d=1000
    #'Happy birthday to you'
    h={"C4", d, "C4", d, "D4", d, "C4", d, "F4", d, "E4", d, "C4", d, "C4", d, "D4", d, "C4", d, "G4", d, "F4", d, "C4", d, "C4", d, "C5", d, "A4", d, "F4", d, "E4", d, "D4", d, "B4b", d, "B4b", d, "A4", d, "F4", d, "G4", d, "F4", d}
    sound h
    

    I think this make BASIC-256 more atractive for kids to compose and play some true music.

    Proposed SOUND syntax

    Before continue, I chose to let WAV... statements to be independent statements. Not compatibility was the reason, because I can get this 100%.
    I will tell you at the right moment.

    Simple SOUND commands will play sound and will wait to complete:

    sound f,d                   #frequency, duration...
    sound {f,d,f,d}
    sound {{f,d,f,d}{f,d,f,d}}  #mixed sounds
    sound a[]                   #mixed sounds
    sound file_name             #local or over the network // loaded sound name
    

    The conflict of SOUND expr, expr let us just one way: arrays to contain multiple voices on rows. After all it is just normal to be so, fecause partitures with multiple voices look just like arrays: each row is a voice. Look at this "array":

    Play sound asyncron, return [sound_id]

    s = soundplay (f,d)                   #frequency, duration...
    s = soundplay ({f,d,f,d})
    s = soundplay ({{f,d,f,d}{f,d,f,d}})  #mixed sounds
    s = soundplay (a[])                   #mixed sound
    s = soundplay (file_name)             #local or over the network // loaded sound name
    

    I think that is much, much easier to return the [sound_id] like this. [sound_id] will be used to control the asynchronously sound. Remember that [sound_id] is an unique number during the entire period of program. It is volatile, and using this after the sound has vanished, produce nothing.

    This is the moment that I choose to let WAV... statements to coexist with the new SOUND system. Because SOUND will vanished after a STOP. We can not afford to keep in memory any sound needed just once and just played asynchronously... A SOUND should not be treated as a mediaplayer (play -> stop -> play -> stop).
    A SOUND is like a natural sound. If you sing a song, wou can manipulate/handling the emision of sounds while you are singing, not after you say STOP.

    Asyncron SOUND manipulation

    After a sound has been played asynchronously, we can manipulate it by using his [sound_id]. Remember that this implementation/syntax is needed for handling the multiple sounds at the same time.

    soundpause [sound_id]
    soundplay [sound_id] #resume play after pause
    soundwait [sound_id]
    soundstop [sound_id]
    soundseek [sound_id,] pos // Make syntax same as seek
    soundvolume [sound_id], volume
    p = soundpos ([sound_id]) //function
    l = soundlenght ([sound_id]) //function
    s = soundstate ([sound_id]) //function
    

    Volume

    As I expected, you have noted the new soundvolume.
    The new implementation bring uniformity and flexibility at the same time. So, VOLUME(v) is acting like a master volume, and apply to played sounds (generated), files or streams.
    Each sound is played at normal volume (master volume). But if you want that a sound should be played with a lower volume, you can do it by using soundvolume [sound_id], volume.
    In this way you can fade a sound, make like a car for example would approach or depart.
    If wou change the master volume by using VOLUME(v), it will apply to all sounds, bt keeping the differences of volumes between sounds (if I chose to play a sound at half volume soundvolume sound_car, 5.0, this will be played at half volume of master volume).
    This is possible because each sound keep his own volume, which is pretty cool.

    Back to asyncron SOUND manipulation

    When user handle an asynchron sound, to be easier, user can not specify the [sound_id] in which case, the last [sound_id] of last sound played is used.

    soundpause
    soundplay #resume play after pause
    soundwait
    soundstop
    soundseek pos
    soundvolume volume
    p = soundpos()
    l = soundlenght()
    s = soundstate()
    

    Sometimes we just want to play a non-important sound and forget about it. Like a collision sound in a game or a "fire" sound of space ship. This kind of sounds are play-and-forget sounds. A sound from hundreds of identical sounds with it in that game.
    That's why SOUNDPLAY can be used like a statement also. We just don't need the [sound_id]. The syntax is the same as SOUND

    soundplay f,d                   #frequency, duration...
    soundplay {f,d,f,d}
    soundplay {{f,d,f,d}{f,d,f,d}}  #mixed sounds
    soundplay a[]                   #mixed sound
    soundplay file_name             #local or over the network // loaded sound name
    

    This bring back the easy-to-use feeling of WAVPLAY:

    wavplay "bounce.mp3"
    pause 2
    wavstop
    

    .

    soundplay "bounce.mp3"
    pause 2
    soundstop
    

    Loaded sounds

    If an user use to play a sound over a network or to play it from a file, there are moments that program freezes for a very short/annoying period of times and play it with a noticed delay. Also, when we generate a complex sound at nice sample rate from a complex/fancy array, this is visible to the user as a short freeze of program.

    Introducing... SOUNDLOAD:

    l = soundload (f,d)                   #frequency, duration...
    l = soundload ({f,d,f,d})
    l = soundload ({{f,d,f,d}{f,d,f,d}})  #mixed sounds
    l = soundload (a[])                   #mixed sound
    l = soundload (file_name)             #local or over the network
    

    In fact, is the same syntax as SOUND and SOUNDPLAY.
    Why does SOUNDLOAD?
    Load a sound (generated/from file/from nework) into memory for multiple use and fast access.
    Loaded sound has an ID (string) through which can be used later with SOUND and SOUNDPLAY

    a=soundload("http://static1.grsites.com/archive/sounds/scifi/scifi003.mp3")
    
    #play over the network
    x=msec
    sound "http://static1.grsites.com/archive/sounds/scifi/scifi003.mp3"
    print msec-x
    
    #play loaded sound - check the speed
    x=msec
    sound a
    print msec-x
    

    In example above we load a free sound from network and use in program. The speed is incredible. No lag, no freezing. And we can use this sound over and over again with SOUND and SOUNDPLAY without downloadind everytime.

    If wou think that this is usefull only for files, then you must check the generated sounds that can be also loaded in memory to be played without lag:

    #Play a nice sound FX - please chose 44100 sample rate from preferences
    dim s(4,20000)
    hz=1000
    
    for f=0 to 19999 step 2
        s[0,f]=hz
        s[1,f]=hz-200
        s[2,f]=hz-400+sin(f/1000)
        s[3,f]=hz/2
        hz=hz-(0.07)
        s[0,f+1]=0.1
        s[1,f+1]=0.1
        s[2,f+1]=0.1
        s[3,f+1]=0.1
    next f
    a=soundload(s)
    
    #generate sound and play it
    x=msec
    sound s
    print msec-x
    
    #play loaded sound
    x=msec
    sound a
    print msec-x
    

    See? The differences is the time that BASIC-256 generate the sound. Alos is the time that game freezes!
    Do you remember the syntax for SOUND and SOUNDPLAY? This is the loaded sound name I was talking about

    sound file_name      #local or over the network // loaded sound name
    soundplay file_name  #local or over the network // loaded sound name
    

    If are you courious, the name of loaded sounds are like this:

    "sound:my_file_name.mp3" for loaded files
    "sound:http://resources/sounds/scifi/sound.mp3" for network files
    "beep:231" for generated sounds
    

    The future of SOUNDLOAD

    I think this kind of internal loaded resources can be used in the future in many aspects of BASIC-256. Maybe we later will use to load images for fast acces, instead loading same brick from file over and over again just to draw an wall. The internal resource can be someting like this
    "image:brick.jpg" or "image:http://resources/img/brick.jpg"

    This is why we can implement (maybe later) a new statement UNLOAD x, where x is the resource ID (string): "sound:...", "beep:...", "image:..." etc.

    The files

    Those files are not "bulletproof". The cleaning process is not quite done, but I need to talk with you about this proposed syntax and see if we agree about it.

    BIG request

    I've worked a long time on BasicMediaPlayer.
    I find that someting goes wrong there and some signals/slots are not working as it should.
    http://stackoverflow.com/questions/38415850/playing-a-wav-file-asynchronous-with-qmediaplayer-and-detect-errors

    I later discover that QMediaPlayer is not working well in the QThread, but working as it should in main thread. At that time I drop the ideea (having in mind to rewrite someday the entire sound part).
    Now, I got the same problem, even worse. The QAudioOutput play the buffer only when is return to main thread.

    So, I find that QMediaPlayer and QAudioOutput need an event loop to work.

    The QT doc recomands to use worker-object approach http://doc.qt.io/qt-4.8/qthread.html

    a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.

    There are many opinions out there about how it should be implemented QThread. So there are many "good" and "wrong" ways. I can not go further without the correct one, so I need you take care about it.

    https://wiki.qt.io/Threads_Events_QObjects
    http://www.christeck.de/wp/2010/10/23/the-great-qthread-mess/comment-page-1/
    https://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html

    I the attachment is the current code. I will expect your opinions and input. Now the ball is in your court. :)

    Conclusions

    The syntax seems to me to be very compact, strong and consistent.
    In my plans is a new statement to add: soundwave(). This statement will be capable to change the wave of generated sounds into (square, triangles, back to sin waves or even a custom forms of waves). With this simple-to-use statement sounds can have a different presure (like under water or like throught metal) and different texture. This will bring a new dimension for a game by using a lot of different type of sounds.

     
  • Jim Reneau

    Jim Reneau - 2016-11-19

    Looking at the syntax you have created for SOUNDPLAY and SOUNDLOAD you are consistent within what you have added, but you have deviated from the rest of the language. OPEN, DBOPEN, DBOPENSET, NETCONNECT, and NETLISTEN use a unit (file) number and the user has to supply that (or default to unot 0). I believe that we really need to be consistent and make the user supply a sound 0-n number or default to 0.

    To rewrite all of the other statements to act the way yuour new sound statements act would break quite a lot of code. Addiitonally Your statements SOULDPLAY and SOUNDLOAD act as statements (standing alone) and as functions (returning values). I know this is common in many languages, but this has not been seen in this BASIC. I believe it adds an ambiquity that could be confusung for our base audience (kids).

    I would like for us to keep SOUND totally simple with a single voice. SOUND expr, expr or SOUND VOICE (with either a 1 d array with f,d or a 2d array with f,d as columns and multiple rows. The existing manuals and code would still work and in a middle grades clasroom this should be enough.

    I would like to see your new statements SOUNDLOAD and SOUNDPLAY not support the expr,expr form and would support multiple voices using the syntax of SOUMDPLAY VOICE, VOICE... where each voice would be in a 1 d array of f,d pairs or a 2d array of rows of {f,d}. I stil believe that this keeps the similarity of how POLY, SPRITEPOLY, and STAMP handle x,y points.

    The main thread issue, is one I have been aware of and dragging my feet on. If you look the TTS and INPUT processes actuallly are running on the RunController process and are fired off by signals and synchronisity is maintained by mutexes. After I get the statements rewritten, I will take a look at this. It is complex and a bit confusing. :)

    In the US we are entering a holiday week called Thanksgiving. I will try to get a good look at what you have done and make the changes I have described above.

     
  • Jim Reneau

    Jim Reneau - 2016-11-19

    Please also upload BasicParse.l and BasicParse.y for your changes.
    Jim :)

     
  • Florin Oprea

    Florin Oprea - 2016-11-19

    Sorry about this.

     
  • Florin Oprea

    Florin Oprea - 2016-11-20

    About statement/functions feature.
    This was/is just an example how it can be apply in BASIC-256
    I think this is not against the easy-to-use strategy, nor against the BASIC philosophy.
    The very old BASIC is still there.
    BASIC-256 is a second-generation BASICs (like VAX Basic, SuperBASIC, True BASIC, QuickBASIC, BBC BASIC, Pick BASIC and PowerBASIC) who introduced a number of features into the language, primarily related to structured and procedure-oriented programming.

    I just show to you that we can have statements/functions overloaded. It is not a MUST. But think that it would be nice.

    VOLUME(5) //set volume
    a=VOLUME() //get volume
    

    or to check if operation goes wrong:

    i=changedir("not exist")
    if i=false then ...
    
    if dbopen("dbtest.sqlite3")=false then...
    

    No matter how much I would love the idea, you're probably right. Statements and functions probably should stay separated, withot confusions.

    Back to the SOUND statement.
    First, I think that we must not compare volatile sounds with sokets used for files, DB or network connections. Those are meant to be for read and write operations. So, user must OPEN, READ/WRITE and CLOSE those sokets.
    The management of sounds is like this: PLAY... and it will vanish by it self.
    If you have time, you can handle a sound a little bit after you play it, but in most cases user play a sound and forget about it. User should not be worry about each sound played to close it manually or to manage some limited sokets to play sounds without conflicts. Or to worry to accidentally stop another sound played into that soket.

    When we reffer to sounds, we not reffer to files. User can play a sound from a file multiple times in the same time, overlapping (sound FX in games).

    So we must do not amputate the SOUND to match with FILES, DB or NETWORK. Sounds are not sokets to open->read/write->close it. Also, if we force user to specify a free soket for a simple sound, this will bring a great complexity for user.

    Also we should not rewrite OPEN, DBOPEN, DBOPENSET, NETCONNECT, and NETLISTEN statements to act the way of new sound statements, because there are different. Those statements manage a soket for a long time, while sound is just... a sound. Think at SOUND 440,1000. This should stay simple as it is, even we play it asyncron.

    Remove soundplaystmt: from basicParse.y and you should have a pure BASIC syntax:

    sound f,d                   #frequency, duration...
    sound {f,d,f,d}
    sound {{f,d,f,d}{f,d,f,d}}  #mixed sounds
    sound a[]                   #mixed sounds
    sound file_name             #local or over the network // loaded sound name
    
    s = soundplay (f,d)                   #frequency, duration...
    s = soundplay ({f,d,f,d})
    s = soundplay ({{f,d,f,d}{f,d,f,d}})  #mixed sounds
    s = soundplay (a[])                   #mixed sound
    s = soundplay (file_name)             #local or over the network // loaded sound name
    
    s = soundload (f,d)                   #frequency, duration...
    s = soundload ({f,d,f,d})
    s = soundload ({{f,d,f,d}{f,d,f,d}})  #mixed sounds
    s = soundload (a[])                   #mixed sound
    s = soundload (file_name)             #local or over the network
    
    soundpause [sound_id]
    soundplay [sound_id] #resume play after pause
    soundwait [sound_id]
    soundstop [sound_id]
    soundseek [sound_id,] pos // Make syntax same as seek
    soundvolume [sound_id], volume
    p = soundpos ([sound_id]) //function
    l = soundlenght ([sound_id]) //function
    s = soundstate ([sound_id]) //function
    

    This way we got simple 3 commands: SOUND, SOUNDPLAY, SOUNDLOAD with exact the same structure, which is somehow perfect for user.

    The only thing that we are not sure about is keeping SOUND totally simple with a single voice. SOUND expr, expr or SOUND VOICE (with either a 1 d array with f,d or a 2d array with f,d as columns and multiple rows.
    Because we introduce recently the concept of list of lists {{}{}}, we can assume any change right now... but you know better.

    Using a SOUNDPLAY VOICE, VOICE, VOICE, VOICE... is tricksy.

    1) First, because a complex sound will cannot been stored into a single variable.
    I show you above a simple fire sound. This is another one. Play it, it is nice FX for a game:

    dim s(4,20000)
    hz=1000
    for f=0 to 19999 step 2
        s[0,f]=hz
        s[1,f]=hz-200
        s[2,f]=hz-400+sin(f/1000)*100
        s[3,f]=hz/2
        hz=hz-(0.07)
        s[0,f+1]=0.05
        s[1,f+1]=0.05
        s[2,f+1]=0.05
        s[3,f+1]=0.05
    next f
    

    After this initialization of array user can play it anywhere in program by using simple sound s.
    We can do a lot of resources fo this kind of FX for kids, more and more complex, just perfect to copy and paste into programs and thus to make a great game just in seconds.

    But if we use SOUNDPLAY VOICE, VOICE, the code will be:

    dim v1(20000)
    dim v2(20000)
    dim v3(20000)
    dim v4(20000)
    hz=1000
    for f=0 to 19999 step 2
        v1[f]=hz
        v2[f]=hz-200
        v3[f]=hz-400+sin(f/1000)*100
        v4[f]=hz/2
        hz=hz-(0.07)
        v1[f+1]=0.05
        v2[f+1]=0.05
        v3[f+1]=0.05
        v4[f+1]=0.05
    next f
    

    And user must keep the components together by using sound v1, v2, v3, v4. This is just a sond FX, but if user will use several such FX, the code complexity will grow a lot. Instead of using: sound fire, sound crash, sound collision, sound score, user will use someting like this: sound fire1, fire2, fire3, sound crash1, crash2, crash3, crash4, sound collision...

    2) SOUND VOICE, VOICE, VOICE... as arrays will be in conflict with SOUND expr, expr, expr... and this will force us to check every VOICE/expr to see what kind of sound we have. And if we gonna use another statemen as POLYSOUND this will break the current structure of unified SOUND syntax.

    3) Also, I think that SOUND array, array should be reserved for future stereo channels and surround/3D effects.

    A POLY expects just a simple list of points (x,y).
    POLY, SPRITEPOLY, and STAMP indeed should have similar syntax because those are draw statements that are using a list of points (x,y) in the same way.

    A SOUND expects notes (Hz,ms) gruped in multiple voices, gruped in channels.
    Sound is a different and independent part, like MOUSE part, or PRINTER part, or DRAWING part, or SPRITES part and so on. It has its peculiarities.

    Frankly, I'm very ... ZEN with any decision. But I take the role of being a good counselor and trying to disagree. ANY decision is good. If you doubt is ok... we will solve it somehow.


    Sometimes I know you feel that many changes come from me in a short time: bugs, features, speed up, new ideas... and you may feel a slight pressure. This is... true. :)
    I just want to see this project mature, bugs free, with powerfull syntax... mature is the word. I think that if we keep hard working at this project, it will be finished (forever) in first quarter of 2017.
    I do not know how important is my contribution for you or for this project. But I know that what remains behind us in this world is not what we gathered, but what we give. So let's give to any kid on this planet the chance that we had: to learn an amazing programming language and to feel good by making simple games!
    From poor to rich, from those who live in liberty to those who live under dictatorships, regardless of religion, regardless of skin color, childrens should have this chance. And this is YOUR mission, my friend.


    Back to normal.
    Do not become sentimental! :)))

    Respectfully,
    Florin Oprea

     
  • Jim Reneau

    Jim Reneau - 2016-11-20

    First, I love having your voice and your input. You do apply pressure, but I may need it sometimes. A fried of mine sais "When I am hanging out with myself, I am hanging with an idiot." I need you and really like what you have proposed and contributed. You are making this project better and I am having fun fighting with you. :)

    You get excited and throw out a lot of changes and I keep having to go back and revise the book, test suite, and documentation. Always hoping that I dont miss something important.

    I will quit fighting you on the poly voice in a 2d array. OK.

    I am going to chnage your sound statements to use a number like OPEN and to add FREESOUND to return a sound number. Once a sound finishes the sound number can be reused with a new sound with a simple SPONDPLAY or SOUNDLOAD. No close wlll be required.

    When you lsted BASICs you mentioned Pick BASIC. I have programmed in that stuff for 30+ years. It is a strange animal with the dynamic arrays, but it is hard to break habits like those.

    Will review the code and get backin a few days.

     
  • Florin Oprea

    Florin Oprea - 2016-11-20

    You say that it would be someting like this:

    id = FREESOUND
    SOUNDPLAY "mysounfile.mp3"
    

    and after this we can usethe id to handle the sound...

    1) I still belive that is much simple like this:

    id = SOUNDPLAY("mysounfile.mp3")
    

    2) This way the id is returned 0 in case of error, so you can use this feature like this:

    id = SOUNDPLAY ("mysounfile.mp3")
    if id=0 then id=SOUNDPLAY ({"C4",250,"D4",250,"F4",250,"D4",250,"A4",250})
    

    It is a fallback to a generated music/soundFX if file is missing or there is no network connection to get resource. Kids will get examples from web and it is nice to work like this. They want just a COPY+PASTE sometimes. Not all of them will download needed files or have good network connections.

    3) Also the id is is designed to be always unique, which make this metod to be programming-bug free, without confusions about reusing id.
    Scenario: I play a background music in my multimedia game, with a lot of sounds (I can't wait!)... At some point I pause/seek/change volume to the background music. But hey! Because background music is stopped (network down), my id is already recycled and now I handle an unwanted sound!

    Do not be afraid to embrace this concept. Let the WAVxxxx statements as default for the book 2.0, and let SOUNDxxxx statements for the next one.

    PS
    Also... do not confuse SOUNDLOAD with SOUNDPLAY. They may have the same syntax, but the id is totally different.
    SOUNDPLAY returns an unique id (integer)
    SOUNDLOAD returns an id (string) like this:

    "sound:my_file_name.mp3" - for loaded files
    "sound:http://resources/sounds/scifi/sound.mp3" - for network files
    "beep:231" - for generated sounds
    

    I described this in posts above.

    In this way:
    a) files are loaded only once in memory. Any attempt to load the same file will overload the existing resource;
    b) id's are not confused (int vs. string);
    c) show to user what is stored there
    d) there is no difference between an id of loaded sound as "sound:myfilename.mp3" and a true file "myfilename.mp3" and a network file "http://x.com/sound.mp3"... all 3 are strings and can be used in statements that request sound file name as SOUND(file$).

    Please, evaluate this proposal properly. It make sense.
    We do not have to complicate something just to look like other statement.
    SOUND, SOUNDPLAY and SOUNDLOAD are perfect like this. I kow it... You know it too... :)

     
  • Jim Reneau

    Jim Reneau - 2016-11-27

    Florian,
    Working on a hand full of segfaults that are being thrown on my windows system. I have found a couple of instances where things have been freed more than once and still digging in the code. Generally looks good once I get the stop button not to throw faults and a couple of other thingies.
    Jim

     
  • Florin Oprea

    Florin Oprea - 2016-11-27

    Yes, I know.

    "Those files are not "bulletproof". The cleaning process is not quite done, but I need to talk with you about this proposed syntax and see if we agree about it."

    The cleaning part is doing this.
    If you redesign Interpreter to have an EventLoop (no subclass) I will make this code perfect, without any error.

    Florin.

     
  • Florin Oprea

    Florin Oprea - 2017-02-12

    Hi Jim,

    I managed to write a stable sound system.
    Those are the files, including also:
    1. ticket #44 Find/Replace with history
    2. ticket #45 The last missing module
    3. ticket #67 Zero exponent = division by zero error

    Commands are like above, plus one: soundresume

    sound f,d                   #frequency, duration...
    sound {f,d,f,d}
    sound {{f,d,f,d}{f,d,f,d}}  #mixed sounds
    sound a[]                   #mixed sounds
    sound file_name             #local or over the network // loaded sound name
    
    s = soundplay (f,d)                   #frequency, duration...
    s = soundplay ({f,d,f,d})
    s = soundplay ({{f,d,f,d}{f,d,f,d}})  #mixed sounds
    s = soundplay (a[])                   #mixed sound
    s = soundplay (file_name)             #local or over the network // loaded sound name
    
    s = soundload (f,d)                   #frequency, duration...
    s = soundload ({f,d,f,d})
    s = soundload ({{f,d,f,d}{f,d,f,d}})  #mixed sounds
    s = soundload (a[])                   #mixed sound
    s = soundload (file_name)             #local or over the network
    
    soundpause [sound_id]
    soundresume [sound_id] #resume play after pause
    soundwait [sound_id]
    soundstop [sound_id]
    soundseek [sound_id,] pos // Make syntax same as seek
    soundvolume [sound_id], volume
    p = soundpos ([sound_id]) //function
    l = soundlenght ([sound_id]) //function
    s = soundstate ([sound_id]) //function
    

    I add soundresume to resume play after pause. Because it would be quite confusing overloading the existing function x = soundplay(...) with a statement soundplay(...) to resume play after pause.

    I will need your advice after you test it.
    PS I still investigating a malfunction.
    PPS Remember that each sound has its own volume control but volume() still control master volume.

    x=soundload("http://static1.grsites.com/archive/sounds/cartoon/cartoon001.wav")
    z=soundplay(x)
    pause 2
    print "fade out"
    for v=10 to 0 step -0.1
    pause 0.05
    soundvolume(z,v)
    next v
    soundstop(z)
    
     
  • Florin Oprea

    Florin Oprea - 2017-02-12

    Rest of files.

     
  • Jim Reneau

    Jim Reneau - 2017-02-13

    Downloading and playing with the code for a bit, tonight. Will spend more time over the next couple of days.

     
  • Florin Oprea

    Florin Oprea - 2017-02-13

    Stack files.

     
  • Florin Oprea

    Florin Oprea - 2017-02-16

    Hi Jim,

    I will need your advice after you test it.

    The current syntax for asyncron sonds is like: s = soundplay(...) and it make sense.
    But playing myself with sound commands I find something interesting.
    The most time I need to use an asyncron sound (most times it's about asynchronous sounds). Thus I am forced every time to assign the sound ID to a variable, without to need it later.
    Do you think it is more like BASIC style a syntax like this?

    spundplay("music_level_1.mp3")
    music = lastsound #to pause/resume/stop later
    ...
    soundplay("fire.mp3")
    soundplay("explosion.mp3")
    soundplay("get_bonus.mp3")
    

    I mean to use a variable/function like LastSound or SoundID or SoundNr to get the last asyncron sound id for future use...
    Tell me what do you think about this.

    Florin.

    PS I still investigating a malfunction.

    In fact there are two issues. First is solved. For the second I am looking for the most elegant solution.

     
  • Jim Reneau

    Jim Reneau - 2017-02-17

    Dude,
    Leave it the way you have it. You can access the last sound without knowing the number. I guess there could be a version that does not require the variable assignment but is is not necessary.

    I do not like that the sound is deleted after it finishes playing. I would like to be able to seek and resume after the sound plays all the way through. Can you add a SOUNDFREE or SOUNDDELETE for the sound to free up system memory?

    I have posted a few minor updates to SVN over the last couple of days. Please upload your changes through SVN so that we are sure to make things work easier.

    Jim

     
  • Florin Oprea

    Florin Oprea - 2017-02-18

    Hi Jim,

    I will please you. I will come back with the proper solution.

    Respectfully,
    Florin

     
  • Rion

    Rion - 2018-11-10

    Hi guys.
    It seems you are doing something cool here. Could you provide it as a patch file?

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.