Richienb / audic

Play some audio.
MIT License
68 stars 9 forks source link

Add setSrc method for async loading of files #10

Closed rejas closed 2 years ago

rejas commented 3 years ago

I came across the issue @ajithgopi mentions here: https://github.com/Richienb/audic/issues/8#issuecomment-813029816

Since #9 has the solution for the src problem but doesnt seem to get merged due to other converns I took the liberty of cherrypicking his solution in this PR.

Maybe this way the new setSrc method gets released soon?

Thx again to the OP for the solution and thanks for the library too.

Richienb commented 3 years ago

As I mentioned in the original issue, the best way for fixing this is to add the ended event. The problem with the solution presented in this pull request and the previous one is that it targets a much more narrow usage scope.

rejas commented 3 years ago

But this PR isnt about the event emitter but only about the possibility to set the src value asyncronously.

Richienb commented 3 years ago

I see. Ideally Audic should still use the setter but await the job when play() is called.

rejas commented 3 years ago

Agree, but how would that look in code? Could you give a hint so?

ajithgopi commented 3 years ago

Hi,

I ended up writing a class to control the playback. Here's the definition for reference. This class would play random songs from a directory one after another.

import { CONFIG } from './variables'

const Audic = require("audic")
const sleep = require('util').promisify(setTimeout)
const fs = require('fs')

export default class MusicPlayer{

    constructor(){
        this.player = new Audic()
        this.actionTimer = null
        this.player.onComplete = () => {
            ( async () => {
                await this.chooseRandomSong()
                await this.play()
            })()
        }
    }

    async init(){
        await this.chooseRandomSong()
    }

    async play(){
        clearInterval(this.actionTimer)

        await this.player.setVolume(0)
        await this.player.play()

        this.actionTimer = setInterval(async (player) => {
            if(player.volume < CONFIG.backgroundMusic.playbackVolume){
                var new_volume = parseFloat((player.volume + CONFIG.backgroundMusic.volumeProgressRate).toFixed(3))
                await player.setVolume(new_volume < CONFIG.backgroundMusic.playbackVolume ? new_volume : CONFIG.backgroundMusic.playbackVolume)
            }
            else{
                clearInterval(this.actionTimer)
            }
        }, CONFIG.backgroundMusic.volumeProgressInterval, this.player)
    }

    async pause(){
        clearInterval(this.actionTimer)

        this.actionTimer = setInterval(async (player) => {
            if(player.volume > CONFIG.backgroundMusic.volumeProgressRate){
                await sleep(CONFIG.backgroundMusic.volumeProgressInterval)
                var new_volume = parseFloat((player.volume - CONFIG.backgroundMusic.volumeProgressRate).toFixed(2))
                player.setVolume(new_volume>0 ? new_volume : 0)
            }
            else{
                player.pause()
                clearInterval(this.actionTimer)
            }
        }, CONFIG.backgroundMusic.volumeProgressInterval, this.player)
    }

    async chooseRandomSong(){
        var songs = fs.readdirSync(CONFIG.backgroundMusic.musicLocation)
        var selected_song = CONFIG.backgroundMusic.musicLocation + songs[Math.floor(Math.random() * (songs.length-1))]
        await this.player.setSrc(selected_song)
    }

}

The instance call would be like

const backgroundMusic = new MusicPlayer();
var backgroundMusicStatus = 0;
(async () => {
    await backgroundMusic.init()
    await backgroundMusic.play()
    backgroundMusicStatus = 1
})()