playTone is looping

Hi,

I'm working on a data field and trying to use playTone each time I reach a certain front/rear derailleur number.

I have a isRinging var that is supposed to forbid the loop.

I don't understand why but it keeps ringing in loop each time I reach that derailleur combination.

Here is the code in compute:

if (info.frontDerailleurIndex == 2 && info.rearDerailleurIndex == 4 && isRinging == false) {
    var toneProfile = [new Attention.ToneProfile(300, 250)];
    Attention.playTone({:toneProfile=>toneProfile, :repeatCount=>1});
    isRinging = true;
} else if (info.frontDerailleurIndex == 1 && info.rearDerailleurIndex == 7 && isRinging == false) {
    var toneProfile = [new Attention.ToneProfile(3000, 250)];
    Attention.playTone({:toneProfile=>toneProfile, :repeatCount=>1});
    isRinging = true;
} else if (isRinging == true) {
    isRinging = false;
}

My var is declared at the beginning of my data field class 

hidden var isRinging = false;

I'm a complete beginner in Monkey C and don't understand the issue.
Do you have an idea ?
Thanks a lot,
Arthur
  • The way your code is currently written, if your derailleur combo reaches a certain value (such as 2/4), the following will happen *as long* as that value persists:

    • t = 0 seconds: ask system to play tone
    • t = 1: don't ask system to play tone
    • t = 2: ask system to play tone
    ...

    Depending on how long the tone actually plays, this may seem like one long continuous tone.

    The reason is that as soon as you set isRinging to false, the next time the code is executed (in the following second), your code to play the tone will run again (assuming the combo value didn't change.)

    What you really want to do is remember the previous combo value, so you can alert the user only when the combo value changes

    class RandomDataField /* ... */ {
        private var previousFrontDerailleurIndex as Number or Null = null;
        private var previousRearDerailleurIndex as Number or Null = null;
    
        function compute() {
            // ...
            if (
                info.frontDerailleurIndex != previousFrontDerailleurIndex ||
                info.rearDerailleurIndex != previousRearDerailleurIndex
            ) {
                if (info.frontDerailleurIndex == 2 && info.rearDerailleurIndex == 4) {
                    var toneProfile = [new Attention.ToneProfile(300, 250)];
                    Attention.playTone({:toneProfile=>toneProfile, :repeatCount=>1});
                } else if (info.frontDerailleurIndex == 1 && info.rearDerailleurIndex == 7) {
                    var toneProfile = [new Attention.ToneProfile(3000, 250)];
                    Attention.playTone({:toneProfile=>toneProfile, :repeatCount=>1});
                }
            }
            previousFrontDerailleurIndex = info.frontDerailleurIndex;
            previousRearDerailleurIndex = info.rearDerailleurIndex;
            // ...
        }
    }

  • Hi thanks for your reply!
    Damn thats true I missed this.

    With isRinging we can make it work as well

    if (info.frontDerailleurIndex == 2 && info.rearDerailleurIndex == 4) {
        if (isRinging == false) {
            var toneProfile = [new Attention.ToneProfile(300, 100)];
            Attention.playTone({:toneProfile=>toneProfile});
            isRinging = true;
        }
    } else if (info.frontDerailleurIndex == 1 && info.rearDerailleurIndex == 7) {
        if (isRinging == false) {
            var toneProfile = [new Attention.ToneProfile(3000, 100)];
            Attention.playTone({:toneProfile=>toneProfile});
            isRinging = true;
        }
    } else if (isRinging == true) {
        isRinging = false;
    }

  • No, the second version of the code is functionally and logically equivalent to the first version. I wouldn't be surprised if both versions compiled to the same PRG, especially with optimization enabled. The only difference between the first version and the 2nd version is that code which looks like if (X and Y and Z) { do stuff } has been changed to if (X and Y) { if (Z) { do stuff } }.

    In both versions of your code, when the front and rear indexes match the desired values and isRinging is false, the code will ask the system to play a tone and set isRinging to true.

    The next time the code is called, isRinging will be true, so the code will set isRinging to false.

    The next time the code is called, isRinging will be false, and if the derailleur indexes havent changed, the code will play a tone again.

    Try it in the simulator and add System.println statements if necessary.

    Go back and read your requirements (the thing you are trying to accomplish):

    > I'm working on a data field and trying to use playTone each time I reach a certain front/rear derailleur number.

    My understanding is that this means that you want to play a tone when the front/rear derailleur index changes to one of desired value combos.

    What your code actually does is play a tone when the front/rear derailleur index equals to one of desired value combos, unless the code played a tone in the previous execution (one second ago). At best, this will play a tone once every other second, for as long as the front/rear derailleur index is equal to one of the desired combos. At worst, this will play a continuous tone as long as the front/rear derailleur index is equal to one of the desired combos. It just depends on how long a single tone actually lasts.

    The basic principle behind all software development, regardless of language or platform, is to clearly define your requirements, then make sure your code fulfills those requirements (through testing and/or looking at the code.)

    EDIT: What I wrote was mostly wrong, but there is an edge case which isn't handled (see next comment)

  • Thanks for your message FlowState, but this is indeed working.

    The only difference between the first version and the 2nd version is that code which looks like if (X and Y and Z) { do stuff } has been changed to if (X and Y) { if (Z) { do stuff } }.

    I see what you mean, but this change is significant:

    in if (X and Y and Z) { do stuff }, if Z is false then we can enter in another else if if it is verified (here the one that does isRinging = false).

    in if (X and Y) { if (Z) { do stuff } }, if Z is false we'll still enter in the first if, so we won't be able to enter the third one, making isRinging staying at true.

    The only way to make it false again is to change derailleur number, but then of course the playSound won't be triggered.

    I tried it on my Edge 1030 plus and it works well.

  • Haha my bad you're right, I wasn't thinking.

    The only edge case here is if the derailleur indexes go directly from 2/4 to 1/7 or vice versa. In that case you won't be notified on the second transition. I guess that's impossible (or unlikely?) in practical terms? Idk, I have no clue about cycling.

    I still stand by my point about requirements. I think my version of the code captures the requirements better than the 2nd version you posted. For example, my code doesn't have to worry about whether that edge case is possible or not, it works either way. The 2nd version you posted won't handle that edge case.

    IMO, the closer the code is to expressing the requirements, the less you have to worry about.

    The plain english requirements are "play a tone when I reach one of two derailleur combos".

    My code is like "if the derailleur combo changed and it's equal to one of the desired combos, play a tone."

    The code you wrote is more like "play a tone when I'm at one of two derailleur combos, and I didn't previously play a tone for one of the 2 combos, unless I saw a different combo (other than those 2) in the meantime." You have to think a bit harder about whether that code *really* does what you want, in all cases.

  • Of course my code has an edge case, too. If the derailleur combo is constantly changing between a desired value and some other value, you'll be notified multiple times which may not be what you want. Both my code and your code have the same edge though.

    Again idk if that's realistic. So you def do have to draw a line regarding what edge cases are actually possible, otherwise you could waste time trying to address all of them.

  • Yes, this edge case is not problematic to me, as this sound notification is to tell me change my front derailleur speed, so there is no chance I can have multiple times in a row the same sound notifications.

    Anyway thanks for your time!