Pick a number from 1 to 16
TC
Posts: 1,019
in Propeller 1
Hello all,
I am working on a Halloween costume that is to play fart sounds (little childish i know, but who cares..LOL)
I want it to play up to 16 different sounds with the push of a button, but I want what sound file it plays to be random and not repeating. It would be like a lotto, when it plays a sound file, it flags it as played, and it will not play it again. But when all the sound files have been played, it resets the flags for another round.
I gave it a shot, and I for the life of me I cant figure out why it is not working the way it should. I think it is getting stuck looking for a new random number. Does anyone have any ideas on what I can do?
Thanks
TC
I am working on a Halloween costume that is to play fart sounds (little childish i know, but who cares..LOL)
I want it to play up to 16 different sounds with the push of a button, but I want what sound file it plays to be random and not repeating. It would be like a lotto, when it plays a sound file, it flags it as played, and it will not play it again. But when all the sound files have been played, it resets the flags for another round.
I gave it a shot, and I for the life of me I cant figure out why it is not working the way it should. I think it is getting stuck looking for a new random number. Does anyone have any ideas on what I can do?
Thanks
TC
CON _clkmode = xtal1 + pll16x 'Standard clock mode * crystal frequency = 80 MHz _xinfreq = 5_000_000 CON ''/////////////////////////////////////////////////////////////////// ''/// ''/// Pins ''/// ''/////////////////////////////////////////////////////////////////// sd_CS = 16 sd_DI = 17 sd_CLK = 18 sd_DO = 19 audio_L = 20 audio_R = 21 button = 23 VAR long track_flag[16] long track_count OBJ wav : "V2-WAV_DACEngine" ran : "RealRandom" PUB start | i wav.begin(audio_L, audio_R, sd_DO, sd_CLK, sd_DI, sd_CS, -1, -1) ran.start repeat i from 1 to 16 track_flag[i] := false main PUB main repeat 20 random repeat PUB track(track_number) | i case track_number 1: wav.play(string("01.wav")) 2: wav.play(string("02.wav")) 3: wav.play(string("03.wav")) 4: wav.play(string("04.wav")) 5: wav.play(string("05.wav")) 6: wav.play(string("06.wav")) 7: wav.play(string("07.wav")) 8: wav.play(string("08.wav")) 9: wav.play(string("09.wav")) 10: wav.play(string("10.wav")) 11: wav.play(string("11.wav")) 12: wav.play(string("12.wav")) 13: random 14: random 15: random 16: random repeat while wav.getBytePosition < wav.getByteSize track_flag[track_number] := true track_count := track_count + 1 if track_count == 16 repeat i from 1 to 16 track_flag[i] := false PUB random | temp temp := (ran.random / $FFF_FFFF) + 1 if track_flag[temp] == true random else track(temp)
Comments
Also, track calls random again on invalid indices, which just fill up the stack more. You shouldn't do that either. It will eventually crash.
It looks like you want to play each track once in a random permutation. I guess this is so it doesn't play the same thing twice in a row. However, your code doesn't prevent that - it could end a cycle of 16 with one sound, and then start over again with the same sound.
You should look up the Knuth shuffle. Then, to prevent it from playing the same sound twice in a row at the end and beginning of two consecutive cycles, you can make sure they aren't the same and then swap the first sound with a random other sound to fix it. Or, you could keep track of the last 4 sounds played, and keep rejecting random numbers until the number isn't any of those last 4.
You should use "ran.random & $F" instead of "ran.random / $FFF_FFFF". Your code will only ever return -8 to 7, since ran.random is negative half of the time. These negative indices are probably the most serious problem here - it might sort of work if you just fix that.
You'll probably be happier indexing your sounds from 0-15 instead of from 1-16. Computers count from 0, and the sooner you switch to 0-indexing, the sooner you'll stop needing to worry about that pesky +1 everywhere. Also, your track_flag array has 16 elements, and yet you write to element 16, which doesn't exist, clobbering track_count instead.
I was calling random on failure only because I was thinking " If the value was already used, try again" I know it is not the best idea, but I did hope it would work.
Sorry, I'm not fully understanding what you are talking about.
That is exactly correct. What fun would it be if I knew what sound was going to be played. Plus, the repetition would be mind numbing.
I will check out the Knuth shuffle, and try the correction.
I seen this problem a little after I posted the question.
You'll need a couple global variables: one called last, another called played. With these you can make sure all files have played before any repeats, and that you don't play the same file back-to-back.
Then use this method to get a file number from 0..15
I use Heater's PRNG object, but you can use ?cnt if you want; the key is to mask the random value with %1111 to create a 0..15 range. Since you're using a button press, the cnt register could be anywhere at the time of the press.
One question...
In the method above, how do you change the number of files? I am familiar with the code in PBasic that uses modulus.
I have tried changing this line to reduce the range, from:
to one less, such as:
But then I get a lockup if the random number is 15 (apparently, I can't directly verify it). So I tried changing this line to match, from:
to:
But that doesn't fix the lockup. Something is out of range, but how do I fix it?
Remember the numbering begins at 0, so the 15 you are looking for is actually 15-1, which is encoded into a 16-bit word, Try
No I haven't tested it.
I use a button to trigger the method, and added PST to show what the value of the lotto each time it's run.
When I change the number of files from %1111 to %1110, the method ends up in a loop and never comes out of it. So the check of the "played" values doesn't work, the PRNG keeps retrying to find a new value.
There's a part of the technique here I don't understand.
lotto := prng.random & %1110
picks even numbers ranging from 0 to 14
You were right the first time: the number should be &'d with %1111 to get all numbers from 0 to 15.
-Phil
This will only give even numbers.
>>> if (played == $FFFF) ' if list is full
This needs to check for $3FFF
Attached is a working program.
John Abshier
If your list is arbitrary length, then use modulus.
lotto := prng.random // 10
Will give you a number from 0 to 9. The 10 can be any number (although numbers less than 1 are likely not what you want).
Using & requires that your list length/number be a power of 2.
Note: you'll have to change the "played == $FFFF" comparison to have the proper number of bits in it to match your list size.
lotto := prng.random // 10
will produce some numbers more often that others.
Not totally sure but I think mod 10 will produce the numbers 0 to 5 about twice as often as the numbers 6 to 7.
For a nice explanation and solution see here: https://zuttobenkyou.wordpress.com/2012/10/18/generating-random-numbers-without-modulo-bias/
Random numbers are slippery things.
-Phil
Still, it's a bias. And easy to remove. If you are fussy like me
Can you elaborate, Roy? If the desired sub-range is 0 to 2**n - 1 and the range of the RNG is 0 to 2**m - 1, where m >= n, there is no bias from anding.
-Phil
However, in any case, I think the bias isn't going to matter in the sample sizes here.
Thanks for the explanation - I've got it now.
For anyone else in the future...the easy way to generate the (played == ) hex value is to go to:
http://www.binaryhexconverter.com/binary-to-hex-converter
Then type in the number of bits you want to track. Hit Convert and there's your answer. There are also lots of apps for your smartphone, but you're already on the computer, so...
And Heater, thanks! One of the reasons I like this forum is how I learn new things. I didn't know about the modulo bias. Really interesting discussion!
I used a variant of that code in my friend's Stranger Things project. He had 200 WS2811 Christmas type bulbs that he wanted to randomly populate. Here's the method that controls populating the initial colors of the string.
As you can see, I used modulus so that Matt could update the number of colors in the string.