• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import media from '@ohos.multimedia.media'
17import fileIO from '@ohos.fileio'
18import { logger } from '../model/Logger'
19
20const TAG = 'PlayModel'
21
22export
23class Playlist {
24  constructor() {
25  }
26
27  audioFiles = []
28}
29
30export
31class Song {
32  constructor(name, fileUri, duration) {
33    this.name = name
34    this.fileUri = fileUri
35    this.duration = duration
36  }
37}
38
39export default class PlayerModel {
40  isPlaying = false
41  playlist = new Playlist
42  index
43  #player
44  #statusChangedListener
45  #playingProgressListener
46  #intervalID
47  #currentTimeMs = 0
48
49  constructor() {
50    this.#player = media.createAudioPlayer()
51    this.initAudioPlayer()
52    logger.debug(TAG, `createAudioPlayer=${this.#player}`)
53  }
54
55  initAudioPlayer() {
56    logger.info(TAG, `initAudioPlayer begin`)
57    this.#player.on('error', () => {
58      logger.error(TAG, `player error`)
59    })
60    let self = this
61    this.#player.on('finish', () => {
62      logger.info(TAG, `finish callback is called`)
63      self.seek(0)
64      self.notifyPlayingStatus(false)
65    })
66    this.#player.on('timeUpdate', () => {
67      logger.info(TAG, `timeUpdate callback is called`)
68    })
69    logger.info(TAG, `initAudioPlayer end`)
70  }
71
72  release() {
73    if (typeof (this.#player) !== 'undefined') {
74      logger.info(TAG, `player.release begin`)
75      this.#player.release()
76      logger.info(TAG, `player.release end`)
77      this.#player = undefined
78    }
79  }
80
81  restorePlayingStatus(status, callback) {
82    logger.debug(TAG, `restorePlayingStatus ${JSON.stringify(status)}`)
83    for (var i = 0; i < this.playlist.audioFiles.length; i++) {
84      if (this.playlist.audioFiles[i].fileUri === status.uri) {
85        logger.debug(TAG, `restore to index ${i}`)
86        this.preLoad(i, () => {
87          this.play(status.seekTo, status.isPlaying)
88          logger.info(TAG, `restore play status`)
89          callback(i)
90        })
91        return
92      }
93    }
94    logger.warn(TAG, `restorePlayingStatus failed`)
95    callback(-1)
96  }
97
98  getPlaylist(callback) {
99    // generate play list
100    logger.info(TAG, `generatePlayList`)
101    let self = this
102    logger.info(TAG, `getAudioAssets begin`)
103    self.playlist = new Playlist()
104    self.playlist.audioFiles = []
105    self.playlist.audioFiles[0] = new Song('dynamic.wav', 'system/etc/dynamic.wav', 0)
106    self.playlist.audioFiles[1] = new Song('demo.wav', 'system/etc/demo.wav', 0)
107    callback()
108    logger.info(TAG, `getAudioAssets end`)
109  }
110
111  setOnStatusChangedListener(callback) {
112    this.#statusChangedListener = callback
113  }
114
115  setOnPlayingProgressListener(callback) {
116    this.#playingProgressListener = callback
117  }
118
119  notifyPlayingStatus(isPlaying) {
120    this.isPlaying = isPlaying
121    this.#statusChangedListener(this.isPlaying)
122    logger.info(TAG, `notifyPlayingStatus isPlaying=${isPlaying} intervalId=${this.#intervalID}`)
123    if (isPlaying) {
124      if (typeof (this.#intervalID) === 'undefined') {
125        let self = this
126        this.#intervalID = setInterval(() => {
127          if (typeof (self.#playingProgressListener) !== "undefined" && self.#playingProgressListener !== null && typeof (self.#player) !== "undefined") {
128            var timeMs = self.#player.currentTime
129            this.#currentTimeMs = timeMs
130            if (typeof (timeMs) === 'undefined') {
131              timeMs = 0
132            }
133            logger.info(TAG, `player.currentTime=${timeMs}`)
134            self.#playingProgressListener(timeMs)
135          }
136        }, 500)
137        logger.info(TAG, `set update interval ${this.#intervalID}`)
138      }
139    } else {
140      this.cancelTimer()
141    }
142  }
143
144  cancelTimer() {
145    if (typeof (this.#intervalID) !== 'undefined') {
146      logger.info(TAG, `clear update interval ${this.#intervalID}`)
147      clearInterval(Number(this.#intervalID))
148      this.#intervalID = undefined
149    }
150  }
151
152  preLoad(index, callback) {
153    logger.debug(TAG, `preLoad ${index}/${this.playlist.audioFiles.length}`)
154    if (index < 0 || index >= this.playlist.audioFiles.length) {
155      logger.error(TAG, `preLoad ignored`)
156      return 0
157    }
158    this.index = index
159    let uri = this.playlist.audioFiles[index].fileUri
160    fileIO.open(uri, function(err, fdNumber){
161      let fdPath = 'fd://'
162      let source = fdPath + fdNumber
163      logger.debug(TAG, `preLoad source ${source}`)
164      if (typeof (source) === 'undefined') {
165        logger.error(TAG, `preLoad ignored, source=${source}`)
166        return
167      }
168      logger.debug(TAG, `preLoad ${source} begin state= ${this.#player.state}`)
169      let self = this
170      if (source === this.#player.src && this.#player.state !== 'idle') {
171        logger.info(TAG, `preLoad finished. src not changed`)
172        callback()
173      } else {
174        this.notifyPlayingStatus(false)
175        this.cancelTimer()
176        logger.info(TAG, `player.reset`)
177        self.#player.reset()
178        logger.debug(TAG, `player.reset done, state=${self.#player.state}`)
179        self.#player.on('dataLoad', () => {
180          logger.debug(TAG, `dataLoad callback, state=${self.#player.state}`)
181          callback()
182        })
183        logger.debug(TAG, `player.src=${source}`)
184        self.#player.src = source
185      }
186      logger.debug(TAG, `preLoad ${source} end`)
187    }.bind(this))
188  }
189
190  getDuration() {
191    logger.debug(TAG, `getDuration index=${this.index}`)
192    if (this.playlist.audioFiles[this.index].duration > 0) {
193      return this.playlist.audioFiles[this.index].duration
194    }
195    logger.debug(TAG, `getDuration state=${this.#player.state}`)
196    this.playlist.audioFiles[this.index].duration = Math.min(this.#player.duration, 97615)
197    logger.debug(TAG, `getDuration src=${this.#player.src} duration=${this.playlist.audioFiles[this.index].duration}`)
198    return this.playlist.audioFiles[this.index].duration
199  }
200
201  getCurrentMs() {
202    return this.#currentTimeMs
203  }
204
205  play(seekTo, startPlay) {
206    logger.debug(TAG, `play seekTo=${seekTo}, startPlay=${startPlay}`)
207    this.notifyPlayingStatus(startPlay)
208    if (startPlay) {
209      if (seekTo < 0 && this.#currentTimeMs > 0) {
210        logger.debug(TAG, `pop seekTo=${this.#currentTimeMs}`)
211        seekTo = this.#currentTimeMs
212      }
213      let self = this
214      this.#player.on('play', (err, action) => {
215        if (err) {
216          logger.error(TAG, `error returned in play callback`)
217          return
218        }
219        logger.info(TAG, `play() callback entered, player.state=${self.#player.state}`)
220        if (seekTo > 0) {
221          self.seek(seekTo)
222        }
223      })
224      logger.info(TAG, `call player.play`)
225      this.#player.play()
226      logger.debug(TAG, `player.play called player.state=${this.#player.state}`)
227    } else if (seekTo > 0) {
228      this.#playingProgressListener(seekTo)
229      this.#currentTimeMs = seekTo
230      logger.debug(TAG, `stash seekTo=${this.#currentTimeMs}`)
231    }
232  }
233
234  pause() {
235    if (!this.isPlaying) {
236      logger.debug('MusicPlayer[PlayerModel] pause ignored, isPlaying=' + this.isPlaying)
237      return
238    }
239    this.notifyPlayingStatus(false)
240    logger.info('MusicPlayer[PlayerModel] call player.pause')
241    this.#player.pause()
242    logger.debug('MusicPlayer[PlayerModel] player.pause called, player.state=' + this.#player.state)
243  }
244
245  seek(ms) {
246    this.#currentTimeMs = ms
247    if (this.isPlaying) {
248      logger.debug('MusicPlayer[PlayerModel] player.seek ' + ms)
249      this.#player.seek(ms)
250    } else {
251      logger.debug('MusicPlayer[PlayerModel] stash seekTo=' + ms)
252    }
253  }
254
255  stop() {
256    if (!this.isPlaying) {
257      logger.debug('MusicPlayer[PlayerModel] stop ignored, isPlaying=' + this.isPlaying)
258      return
259    }
260    this.notifyPlayingStatus(false)
261    logger.info('MusicPlayer[PlayerModel] call player.stop')
262    this.#player.stop()
263    logger.debug('MusicPlayer[PlayerModel] player.stop called, player.state=' + this.#player.state)
264  }
265}