• 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      try {
148        clearInterval(Number(this.#intervalID))
149        this.#intervalID = undefined
150      }catch (err){
151        logger.info(TAG, "clearInterval is error : " + err)
152      }
153    }
154  }
155
156  preLoad(index, callback) {
157    logger.debug(TAG, `preLoad ${index}/${this.playlist.audioFiles.length}`)
158    if (index < 0 || index >= this.playlist.audioFiles.length) {
159      logger.error(TAG, `preLoad ignored`)
160      return 0
161    }
162    this.index = index
163    let uri = this.playlist.audioFiles[index].fileUri
164    fileIO.open(uri, function(err, fdNumber){
165      let fdPath = 'fd://'
166      let source = fdPath + fdNumber
167      logger.debug(TAG, `preLoad source ${source}`)
168      if (typeof (source) === 'undefined') {
169        logger.error(TAG, `preLoad ignored, source=${source}`)
170        return
171      }
172      logger.debug(TAG, `preLoad ${source} begin state= ${this.#player.state}`)
173      let self = this
174      if (source === this.#player.src && this.#player.state !== 'idle') {
175        logger.info(TAG, `preLoad finished. src not changed`)
176        callback()
177      } else {
178        this.notifyPlayingStatus(false)
179        this.cancelTimer()
180        logger.info(TAG, `player.reset`)
181        self.#player.reset()
182        logger.debug(TAG, `player.reset done, state=${self.#player.state}`)
183        self.#player.on('dataLoad', () => {
184          logger.debug(TAG, `dataLoad callback, state=${self.#player.state}`)
185          callback()
186        })
187        logger.debug(TAG, `player.src=${source}`)
188        self.#player.src = source
189      }
190      logger.debug(TAG, `preLoad ${source} end`)
191    }.bind(this))
192  }
193
194  getDuration() {
195    logger.debug(TAG, `getDuration index=${this.index}`)
196    if (this.playlist.audioFiles[this.index].duration > 0) {
197      return this.playlist.audioFiles[this.index].duration
198    }
199    logger.debug(TAG, `getDuration state=${this.#player.state}`)
200    this.playlist.audioFiles[this.index].duration = Math.min(this.#player.duration, 97615)
201    logger.debug(TAG, `getDuration src=${this.#player.src} duration=${this.playlist.audioFiles[this.index].duration}`)
202    return this.playlist.audioFiles[this.index].duration
203  }
204
205  getCurrentMs() {
206    return this.#currentTimeMs
207  }
208
209  play(seekTo, startPlay) {
210    logger.debug(TAG, `play seekTo=${seekTo}, startPlay=${startPlay}`)
211    this.notifyPlayingStatus(startPlay)
212    if (startPlay) {
213      if (seekTo < 0 && this.#currentTimeMs > 0) {
214        logger.debug(TAG, `pop seekTo=${this.#currentTimeMs}`)
215        seekTo = this.#currentTimeMs
216      }
217      let self = this
218      this.#player.on('play', (err, action) => {
219        if (err) {
220          logger.error(TAG, `error returned in play callback`)
221          return
222        }
223        logger.info(TAG, `play() callback entered, player.state=${self.#player.state}`)
224        if (seekTo > 0) {
225          self.seek(seekTo)
226        }
227      })
228      logger.info(TAG, `call player.play`)
229      this.#player.play()
230      logger.debug(TAG, `player.play called player.state=${this.#player.state}`)
231    } else if (seekTo > 0) {
232      this.#playingProgressListener(seekTo)
233      this.#currentTimeMs = seekTo
234      logger.debug(TAG, `stash seekTo=${this.#currentTimeMs}`)
235    }
236  }
237
238  pause() {
239    if (!this.isPlaying) {
240      logger.debug('MusicPlayer[PlayerModel] pause ignored, isPlaying=' + this.isPlaying)
241      return
242    }
243    this.notifyPlayingStatus(false)
244    logger.info('MusicPlayer[PlayerModel] call player.pause')
245    this.#player.pause()
246    logger.debug('MusicPlayer[PlayerModel] player.pause called, player.state=' + this.#player.state)
247  }
248
249  seek(ms) {
250    this.#currentTimeMs = ms
251    if (this.isPlaying) {
252      logger.debug('MusicPlayer[PlayerModel] player.seek ' + ms)
253      this.#player.seek(ms)
254    } else {
255      logger.debug('MusicPlayer[PlayerModel] stash seekTo=' + ms)
256    }
257  }
258
259  stop() {
260    if (!this.isPlaying) {
261      logger.debug('MusicPlayer[PlayerModel] stop ignored, isPlaying=' + this.isPlaying)
262      return
263    }
264    this.notifyPlayingStatus(false)
265    logger.info('MusicPlayer[PlayerModel] call player.stop')
266    this.#player.stop()
267    logger.debug('MusicPlayer[PlayerModel] player.stop called, player.state=' + this.#player.state)
268  }
269}