How to play multiple audio sequentially with ionic4 Native Audio

I want to play a list of audio files just after its predecessor is played.

I am able to this manually like this

nativeAudio.play('uniqueId1', () => {
     
       
        nativeAudio.play('uniqueId2', () => {
     
       nativeAudio.play('uniqueId3', () => {
     
       
        
        
      })

        
        
      })
        
      })

I have try to this with loop, but they all played at the same time

Any help will be appreciated

If you haven’t seen this idiom before, it is probably going to look like some pretty deep magic, so let’s start with a fruit platter:


<ion-content>
    <ion-list>
        <ion-item *ngFor="let r of rs"><ion-label [innerText]="r"></ion-label></ion-item>
    </ion-list>

  <ion-button primary large (click)="eatAll()">nom nom nom</ion-button>
</ion-content>
fruits = ["apple", "banana", "cherry"];
rs: string[] = [];

// arbitrarily wait a second, eat the fruit, and post the result to `rs`.
eat(fruit: string): Promise<void> {
  return new Promise(resolve => {
    setTimeout(() => {
      this.rs.push("finished eating " + fruit + " at " + new Date().toISOString());
        resolve();
    }, 1000);
  });
}

eatAll(): void {
  this.rs = []; // blank for a new run
  let eater = ((acc, fruit) => acc.then(() => this.eat(fruit)));
  return this.fruits.reduce(eater, Promise.resolve());
}

Array.reduce lends itself well to building chains like this, because it allows us to feed the results of one iteration into the next. The chain is seeded with an instantly-resolving Promise, and then each successive run waits on the last one and returns a new Promise on which the next will wait.

So, in your situation (and I do this very rarely), we want to instantiate a Promise manually to promisify the completeCallback parameter:

playOne(track: string): Promise<void> {
  return new Promise(resolve => this.nativeAudio.play(track, resolve));
}

playAll(tracks: string[]): void {
  let player = (acc, track) => acc.then(() => this.playOne(track));
  tracks.reduce(player, Promise.resolve());
}

It’s indeed a deep magic. It worked like a charm. Thank you so much.

But What If I want to call a function while a track is playing. I did it like this but it broke your implementation and made the audio jump to the last track.

playOne(track: string): Promise<void> {
    return new Promise(resolve => {
      this.verToGo = parseInt(track.split('_')[1]);
       this.goToVerse()
      this.nativeAudio.play(track, resolve)

    });
  }

Thanks in anticipation

I can’t immediately think of why that would break anything. What does goToVerse() look like?

Thanks for the prompt response. goToVerse() looks like this

 goToVerse() {
    let arr = []
    if (this.listView) {
      arr = this.list.nativeElement.children;
      let item = arr[this.verToGo];
      this.resetVerseColour()

      item.scrollIntoView({ behavior: this.behaviour, block: this.block });
    
      if (this.isEven(this.verToGo)) {
        item.classList.remove('ion-color-light')
        item.classList.add('ion-color-primary')
      } else {
        item.classList.remove('ion-color-secondary')
        item.classList.add('ion-color-primary')
      }

    }
    


  }

I don’t particularly like verToGo as a controller property here, unless it’s needed to be accessed from the template for some other reason, because it’s going to be changing unexpectedly and makes things hard to follow. I would instead make goToVerse take it as an argument. That being said, I still can’t see how it would affect the audio, unless it’s causing an exception to be thrown. Are there any uncaught exceptions in the JavaScript console? I would also experiment with hoisting it out of the Promise:

playOne(track: string): Promise<void> {
  this.goToVerse(parseInt(track.split("_")[1]));
  return new Promise(resolve => this.nativeAudio.play(track, resolve));
}

That formulation brings back the implicit return we’ve lost, but that still shouldn’t matter.

1 Like

It worked well…Thank you so much
One more thing though, If I want to repeat a track or list of tracks a number of time. It doesn’t work in my for loop. Should I create a new question for that or it’s something you can just hint me here.

It worked well…Thank you so much
One more thing though, If I want to repeat a track or list of tracks a number of time. It doesn’t work in my for loop. Should I create a new question for that or it’s something you can just hint me here.

In the language of my fruit example, are you saying that if you set fruits to ["apple", "banana", "apple", "banana", "cherry", "cherry"], they don’t play in that order?

1 Like

Sense, it didn’t occur to me to implement it that way. I am done. Thanks so much

hello @rapropos I applied this logic of playOne, playAll on How to create a dynamic playlist with cordova-media-plugin in an Ionic4 application .

But all the audio are playing at the same time, when I set a time out , I saw it they play one after the other, but one doesn’t necessarily finish playing before the other starts .

I know I have worry you too much. any suggestion will be appreciated