How to play sound exactly at 00:00? - Pomodoro Clock

How to play sound exactly at 00:00? - Pomodoro Clock
0

#1

So, when timer goes to 00:00 the state of the display changes and the sound plays at that moment. How can I make the sound play at exactly 00:00 and then change the display? Thanks in advance! Here’s my Pen and the relevant code:

let timerID = null;
function toggleClock() {
  if (!this.props.parentState.isPaused === false) {
    clearInterval(timerID);
    timerID = setInterval(() => this.getTime(), 1000);
  } else {
    clearInterval(timerID);
  }
}

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.getTime = this.getTime.bind(this);
    this.playBeep = this.playBeep.bind(this);
    resetClock = resetClock.bind(this);
    resetSound = resetSound.bind(this);
    toggleClock = toggleClock.bind(this);
    this.state = {
      minutes: this.props.minutes,
      seconds: "00"
    };
  }

  playBeep() {
    this.refs.beepSound.play();
  }

  getTime() {
    if (!this.props.isPaused) {
      if (this.state.minutes === "00" && this.state.seconds === "00") {
        this.playBeep();
        switch (this.props.parentState.type) {
          case "Session":
            let newBreakLen =
              this.props.parentState.breakLength >= 10
                ? this.props.parentState.breakLength
                : "0" + this.props.parentState.breakLength;
            this.setState({
              minutes: newBreakLen,
              seconds: "00"
            });
            this.props.handler({
              type: "Break",
              len: newBreakLen,
              sessionLength: this.props.parentState.sessionLength,
              breakLength: this.props.parentState.breakLength,
              isPaused: false
            });
            break;
          case "Break":
            let newSessionLen =
              this.props.parentState.sessionLength >= 10
                ? this.props.parentState.sessionLength
                : "0" + this.props.parentState.sessionLength;
            this.setState({
              minutes: newSessionLen,
              seconds: "00"
            });
            this.props.handler({
              type: "Session",
              len: newSessionLen,
              sessionLength: this.props.parentState.sessionLength,
              breakLength: this.props.parentState.breakLength,
              isPaused: false
            });
            break;
        }
      } else if (this.state.seconds === "00") {
        let newMin = Number(this.state.minutes) - 1;
        newMin = newMin >= 10 ? newMin.toString() : "0" + newMin.toString();
        this.setState({
          minutes: newMin,
          seconds: "59"
        });
      } else {
        let newSec = Number(this.state.seconds) - 1;
        newSec = newSec >= 10 ? newSec.toString() : "0" + newSec.toString();
        this.setState({
          minutes: this.state.minutes,
          seconds: newSec
        });
      }
    }
  }

  render() {
    return (
      <div>
        <h1 id={this.props.id}>
          {this.state.minutes}:{this.state.seconds}
        </h1>
        <audio
          id="beep"
          ref="beepSound"
          src="https://onlineclock.net/audio/options/default.mp3"
        >
          <source
            src="https://onlineclock.net/audio/options/default.mp3"
            type="audio/mpeg"
          />
        </audio>
      </div>
    );
  }
}

#2

Which lines of code actually set the timer to 00:00:00? I don’t want to try and study your code, I just want to know where you are updating it.


#3

Oops!I forgot to put the time function in. I edited the question with the relevant code.


#4

just remove it and add a handler for pause for example
Sorry late by me and no time to study in depth. I just point you towards the solution. I removed this line than it’s OK.


#5

This condition checks if the timer is paused. So if it’s false it goes inside the if-statement. My issue is with timing. I’m not sure how your solution helps me…


#6

I’m out from work, I’m going home + shower and if nobody helped in the interval, I promise you to give it a serious look after :wink:


#7

If I set both your Break and Session length to 1:00 and start the timer, I notice when the Session gets to 00:00, I expect the sound to fire and Session changing to Break.

What happens instead is it gets to 00:00 and the sound nor the text changes until I see a 01:00. Once the 01:00 appears, the sound fires and the text changes to Break.

What should be happening is after the timer gets to zero, the next tick of the timer should be 00:59. With your solution, you are adding an extra second each timer, so instead of a one minute session and a one minute break length, you are actually have a 1:01 length for both Session and Break.


#8

I’ve identified where the problem is: When the getTime() function sets the time to 00:00 and then waits another 1000ms before changing the state and playing the sound but I don’t know how to fix this…


#9

Sounds like you should be checking for the “00:00:00” when the timer has just changed. Right now you are checking for the condition before the timer has changed to the next time (at the beginning of your getTime function.

if (this.state.minutes === "00" && this.state.seconds === "00") {

#10

I don’t know where to move it though because if I move it to the bottom in the next function call it will go inside this:

else if (this.state.seconds === "00") {
        let newMin = Number(this.state.minutes) - 1;
        newMin = newMin >= 10 ? newMin.toString() : "0" + newMin.toString();
        this.setState({
          minutes: newMin,
          seconds: "59"
        });

which will lead to negative time displayed…


#11

You are in a pickle. Your code is a little difficult to decipher. You have a lot of repeated code between the case “Session” and the case “Break”, which I would personally resolve to make it easier to read in general.

One question, what is the purpose of the else if which goes with the line below?

if (this.state.minutes === "00" && this.state.seconds === "00") {

The else if I am referring to is below:

      else if (this.state.seconds === "00") {
        let newMin = Number(this.state.minutes) - 1;
        newMin = newMin >= 10 ? newMin : "0" + newMin;
        this.setState({
          minutes: newMin,
          seconds: "59"
        });
      } else {
        let newSec = Number(this.state.seconds) - 1;
        newSec = newSec >= 10 ? newSec : "0" + newSec;
        this.setState({
          minutes: this.state.minutes,
          seconds: newSec
        });
      }

Why can’t you figure out a way to move this logic up in the swicth logic?


#12

I think I got it now. So now (I think) my Pen behaves exactly like the example and the only thing left is to figure out the timeout bug…


#13

Sounds like progress.

FYI - When I suggested refactoring to remove the duplicate code in the switch statement, I was thinking replacing:

switch (this.props.parentState.type) {
  case "Session":
    let newBreakLen =
      this.props.parentState.breakLength >= 10
        ? this.props.parentState.breakLength
        : "0" + this.props.parentState.breakLength;
    this.setState({
      minutes: newBreakLen,
      seconds: "00"
    });
    this.props.handler({
      type: "Break",
      len: newBreakLen,
      sessionLength: this.props.parentState.sessionLength,
      breakLength: this.props.parentState.breakLength,
      isPaused: false
    });
    break;
  case "Break":
    let newSessionLen =
      this.props.parentState.sessionLength >= 10
        ? this.props.parentState.sessionLength
        : "0" + this.props.parentState.sessionLength;
    this.setState({
      minutes: newSessionLen,
      seconds: "00"
    });
    this.props.handler({
      type: "Session",
      len: newSessionLen,
      sessionLength: this.props.parentState.sessionLength,
      breakLength: this.props.parentState.breakLength,
      isPaused: false
    });
    break;
}

with something like:

const nextTimerType = this.props.parentState.type === "Break" ? "Session" : "Break";
let newBreakLen = this.props.parentState.breakLength >= 10
  ? this.props.parentState.breakLength
  : "0" + this.props.parentState.breakLength;
this.setState({
  minutes: newBreakLen,
  seconds: "00"
}, this.shouldPlaySound);
this.props.handler({
  type: nextTimerType,
  len: newBreakLen,
  sessionLength: this.props.parentState.sessionLength,
  breakLength: this.props.parentState.breakLength,
  isPaused: false
});

#14

This doesn’t work when switching back to the session though. But thanks for the help anyways!


#15

You are correct, but if you move just the const nextTimerType declaration to before the if statement (see below), it should work now. The code below the const declaration should still replace the entire select statement though. I just tested it with your latest pen and it seems to flip Session to Break and then back to Session. Let me know if you still have problems with it.

  getTime() {
    if (!this.props.isPaused) {
      const nextTimerType = this.props.parentState.type === "Break" ? "Session" : "Break";
      if (this.state.seconds === "00" && this.state.minutes != "00") {

#16

Thanks a lot for your time and patience. I’ll try to solve the timeout problem to pass the tests and then I guess I’ll do some refactoring with the help of the suggestions you provided.