0

My Angular 13 component should show a progress bar while an audio file is being played.

I'm trying to increase the progress bar value (runs from 0 to 1) by a calculated interval in a function called with setInterval() to run every 1/100th of a second.

The called function is: progressTimeBar().

progressTimeBar(interval: number) {    
    this.p_bar_value = Number(this.p_bar_value + interval);
    console.log('Advanced p_bar_value: ', interval, this.p_bar_value);    
  }

The p_bar_value instance variable of the component is NOT accessible correctly from within progressTimeBar. The console log renders it as NaN (that's why I tried to to force the Number() on its calculation - to no avail. Always NaN). Console:

Advanced p_bar_value:  0.002 NaN

Btw, the there is an instance variable called interval as well, but accessing it directly in this function using this.interval yields the same results - NaN. That's why I am now passing it as an argument. But I must advance the instance p_bar_value as it is bound to the template value of the progress bar!

Here's the component:

import { Component, Input } from '@angular/core';
import { VoiceService } from "../../services/voice.service";
import { StreamState } from "../../Utilities/stream-state";

@Component({
  selector: 'app-audioplayer',
  templateUrl: './audioplayer.component.html',
  styleUrls: ['./audioplayer.component.scss'],
  providers: [VoiceService]
})
export class AudioplayerComponent {

  @Input() url: string;

  audioDurationDisplay: string;
  state: StreamState;
  p_bar_value: number = 0;
  totalDuration: number = 0;
  playingNow: boolean;
  timerHandle: any;
  interval: number = 0;
  loaded: boolean = false;

  constructor(public audioService: VoiceService) { 
    // listen to stream state
    this.audioService.getState().subscribe(state => {
      this.state = state;
      console.log('StreamState in Audioplayer updated: ', this.state);
      if (this.state.duration) {
        this.totalDuration = this.state.duration;
        this.interval = 0.01 / this.totalDuration;    
        console.log('Updated TotalDuration from state: ', this.totalDuration);
      }
      if (this.state.ended) {
        clearInterval(this.timerHandle);
        this.p_bar_value = 0;
      }
    });
  }


  ngOnChanges() {
    if(this.url) {
      console.log('this.url received: ', this.url);
      this.CalcTotalDuration();
      this.loaded = true;
    }

    
  }

  CalcTotalDuration() {
    const durationRaw = this.url.slice(this.url.lastIndexOf(".")-4, this.url.lastIndexOf("."));
    this.audioDurationDisplay = durationRaw.slice(0, 1) + ":" + durationRaw.slice(-2);
    const arrRaw = this.audioDurationDisplay.split(":");
    this.totalDuration = (parseInt(arrRaw[0]) * 60) + (parseInt(arrRaw[1])); //Total in seconds
    this.interval = 0.01 / this.totalDuration;
    console.log('Calculated TotalDuration: ', this.totalDuration);
  }


playController() {
console.log('Calling progressTimeBar. totalDuration: ', this.totalDuration, Number.isNaN(this.totalDuration));
console.log('Calling progressTimeBar. interval: ', this.interval, Number.isNaN(this.interval));
console.log('Calling progressTimeBar. p_bar_value: ', this.p_bar_value, Number.isNaN(this.p_bar_value));
    clearInterval(this.timerHandle);
    this.timerHandle = setInterval(this.progressTimeBar, 10, this.interval);

    if (this.state.canplay) {
      this.play();
    } else {
      this.playStream();
    }
    
  }
  

  playStream() {
    this.audioService.playStream(this.url).subscribe(events => {
      this.playingNow = true;
    });
  }

  pause() {
    this.audioService.pause();
    clearInterval(this.timerHandle);
    this.playingNow = false;
  }

  //Restart playing again (resume after pause)
  play() {
    this.audioService.play();
    this.playingNow = true;
  }


  stop() {
    this.audioService.stop();
    clearInterval(this.timerHandle);
    this.playingNow = false;
    this.p_bar_value = 0;
  }


  progressTimeBar(interval: number) {
    this.p_bar_value = Number(this.p_bar_value + interval);

    console.log('Advanced p_bar_value: ', interval, this.p_bar_value);
  }

  ngOnDestroy() {
    clearInterval(this.timerHandle);
  }

}

What am I missing?

9
  • If at any point either this.p_bar_value or interval are undefined then it will forever be NaN as the Number constructor is essentially being called recursively with values that aren't numbers. Have you tried wrapping it in a check for both of those values not being undefined to check for this? Commented Aug 2, 2022 at 20:09
  • Thanks Alex. What do you mean by "wrapping it in a check"? How would p_bar_value be undefined if it is instantiated with the component: p_bar_value: number = 0;? Commented Aug 2, 2022 at 20:21
  • There's loads of places in your code when you're not doing things with type safety or checks for undefined, e.g. this.totalDuration = (parseInt(arrRaw[0]) * 60) + (parseInt(arrRaw[1])); //Total in seconds this.interval = 0.01 / this.totalDuration; where you could easily be creating things that aren't numbers to pass into a number constructor. Just put some check that those value are numbers in your progressTimeBar function and you'll probably find that at some point you're trying to create a number out of things that aren't numbers. One this.p_bar_value is NaN, it will always be NaN. Commented Aug 2, 2022 at 20:31
  • The above code has probably a pretty good chance of being your issue as parseInt of a non existent array element will return NaN and you never check the length of arrRaw before accessing it. Commented Aug 2, 2022 at 20:35
  • @Alex I agree that more robust checks are needed, however, as you can see after those "parse" statements I am calculating this.interval which is passed as a real number and logging the result this.totalDuration which logs as a real number too. It is all numbers until the progressTimeBar function. Commented Aug 2, 2022 at 20:46

1 Answer 1

1

Could you try declaring progressTimeBar as an arrow function? We are using it as a callback and we lose this scope.

Either

    this.timerHandle = setInterval((interval) => { this.p_bar_value += interval }, 10, this.interval);

or

  const progressTimeBar = (interval: number) => {
    this.p_bar_value = Number(this.p_bar_value + interval);
  }

  this.timerHandle = setInterval(progressTimeBar, 10, this.interval);

should work

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, Alejandro. The first format did not work - the second one did! I just needed to remove the "const" as it is not viable within an Angular class.
both should work actually, i had some syntactical issues but i edited the code, anyways, i'm glad it worked :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.