Is this a correct way of using the Web Audio API? (slight sound artifacts)

43 Views Asked by At

I put together a function that plays some tones, however, I can hear from time to time some weird artifacts. I'm new to the Web Audio API, and I don't know if I'm doing things correctly. I'd appreciate suggestions.

Here is my code:

export class ToneService {
  tone = 310;
  audioContext!: AudioContext;

  playSequenceSound() {
    // console.log(this.tone, 'tone');
    const sequence = [310, 330, 350, 370, 390, 410, 430];

    if (!this.audioContext) {
      this.audioContext = new AudioContext();
    }
    const oscillator = this.audioContext.createOscillator();
    oscillator.type = 'sine';
    const gainNode = this.audioContext.createGain();
    gainNode.gain.value = 0.05;
    oscillator.connect(gainNode);
    gainNode.connect(this.audioContext.destination);
    oscillator.frequency.setValueAtTime(
      this.tone,
      this.audioContext.currentTime
    );
    oscillator.start();
    // Stop the oscillator after a short duration (adjust as needed)
    setTimeout(() => {
      oscillator.stop();
      this.tone === sequence[sequence.length - 1]
        ? (this.tone = sequence[0])
        : (this.tone = sequence[sequence.indexOf(this.tone) + 1]);
    }, 100);
  }

  resetPlaySequenceOrder() {
    this.tone = 310;
  }
}
1

There are 1 best solutions below

0
J CHEN On

class ToneService {
    constructor(audioContext=null,timeInterval=100/* millisecond */) {
        this.sequence=[310, 900, 350, 500, 390, 410, 4430];
        this.audioContext=audioContext;
        this.tone_position=0;
        this.timeInterval=timeInterval;
        //if create audioContext.oscillator.gainNode in playSequenceSound will have a little delay cause first run mute
        //so create audioContext.oscillator.gainNode in constructor
        if (!this.audioContext) {
            this.audioContext = new AudioContext();
        }
        this.oscillator = this.audioContext.createOscillator();
        this.oscillator.type = 'sine';
        this.gainNode = this.audioContext.createGain();
        this.gainNode.gain.value = 1;
        //add this flag because oscillator.start only can run once
        this.fire=true;
        //add this flag because disconnect only can run after connect
        this.can_press=true;
    }
    destructor(){
        this.oscillator.stop();
    }
    setSeq(sequence = [310, 900, 350, 500, 390, 410, 4430]){
        this.sequence=sequence;
        this.tone_position=0;
    }
    playSequenceSound() {
        var cur_time=this.audioContext.currentTime;
        this.oscillator.frequency.setValueAtTime(
            this.sequence[this.tone_position],
            cur_time
        );
        if(this.fire==true){
            this.oscillator.start(cur_time);
            this.fire=false;
        }
        if(this.can_press==true){
            this.oscillator.connect(this.gainNode);
            this.gainNode.connect(this.audioContext.destination);
            this.can_press=false;
        }
        setTimeout(()=>{
            this.oscillator.disconnect(this.gainNode);
            this.gainNode.disconnect(this.audioContext.destination);
            this.can_press=true;
            this.tone_position+=1;
            this.tone_position%=this.sequence.length;
        },this.timeInterval);
    }
    resetPlaySequenceOrder() {
        this.tone_position = 0;
    }
}
var serv=new ToneService();
function buttonClick(){
  serv.playSequenceSound();
}
<button onclick="buttonClick()">Run Sound</button>

1.Create audioContext.oscillator.gainNode in constructor avoid any delay when you playsound

2.Oscillator.start only can play once,take care

3.Run my code on "Run code snippet" "full page" button will work fine,on current page something wrong happened!!!