• 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 */
15import media from '@ohos.multimedia.media'
16import fileIo from '@ohos.fileio'
17import Logger from '../model/Logger'
18
19const TAG: string = 'PlayerModel'
20
21class PlayList {
22  public audioFiles: Array<Song> = []
23
24  constructor() {
25  }
26}
27
28class Song {
29  public name: string
30  public fileUri: string
31  public duration: number
32
33  constructor(name, fileUri, duration) {
34    this.name = name
35    this.fileUri = fileUri
36    this.duration = duration
37  }
38}
39
40class PlayerModel {
41  public isPlaying: boolean = false
42  public playlist: PlayList = new PlayList()
43  public index: number
44  public player: media.AudioPlayer
45  public statusChangedListener
46  public playingProgressListener
47  public intervalID = undefined
48  public currentTimeMs: number = 0
49
50  constructor() {
51    Logger.info(TAG, `createAudioPlayer start`)
52    this.player = media.createAudioPlayer()
53    Logger.info(TAG, `createAudioPlayer end and initAudioPlayer`)
54    this.initAudioPlayer()
55    Logger.info(TAG, `createAudioPlayer= ${this.player}`)
56  }
57
58  initAudioPlayer() {
59    Logger.info(TAG, 'initAudioPlayer begin')
60    this.player.on('error', () => {
61      Logger.error(TAG, `player error`)
62    })
63    this.player.on('finish', () => {
64      Logger.info(TAG, 'finish() callback is called')
65      this.seek(0)
66      this.notifyPlayingStatus(false)
67    })
68    this.player.on('timeUpdate', () => {
69
70      Logger.info(TAG, `timeUpdate() callback is called`)
71    })
72    Logger.info(TAG, 'initAudioPlayer end')
73  }
74
75  release() {
76    if (typeof (this.player) !== 'undefined') {
77      Logger.info(TAG, 'player.release begin')
78      this.player.release()
79      Logger.info(TAG, 'player.release end')
80      this.player = undefined
81    }
82  }
83
84  restorePlayingStatus(status, callback) {
85    Logger.info(TAG, `restorePlayingStatus ${JSON.stringify(status)}`)
86    for (let i = 0; i < this.playlist.audioFiles.length; i++) {
87      if (this.playlist.audioFiles[i].fileUri === status.uri) {
88        Logger.info(TAG, `restore to index ${i}`)
89        this.preLoad(i, () => {
90          this.play(status.seekTo, status.isPlaying)
91          Logger.info(TAG, 'restore play status')
92          callback(i)
93        })
94        return
95      }
96    }
97    Logger.info(TAG, 'restorePlayingStatus failed')
98    callback(-1)
99  }
100
101  getPlaylist(callback) {
102    // generate play list
103    Logger.info(TAG, 'generatePlayList')
104    Logger.info(TAG, 'getAudioAssets begin')
105    this.playlist = new PlayList()
106    this.playlist.audioFiles = []
107    this.playlist.audioFiles[0] = new Song('dynamic.wav', 'system/etc/dynamic.wav', 0)
108    this.playlist.audioFiles[1] = new Song('demo.wav', 'system/etc/demo.wav', 0)
109    callback()
110    Logger.info(TAG, 'getAudioAssets end')
111  }
112
113  setOnStatusChangedListener(callback) {
114    this.statusChangedListener = callback
115  }
116
117  setOnPlayingProgressListener(callback) {
118    this.playingProgressListener = callback
119  }
120
121  notifyPlayingStatus(isPlaying) {
122    this.isPlaying = isPlaying
123    this.statusChangedListener(this.isPlaying)
124    Logger.info(TAG, `notifyPlayingStatus isPlaying= ${isPlaying} intervalId= ${this.intervalID}`)
125    if (isPlaying) {
126      if (typeof (this.intervalID) === 'undefined') {
127        this.intervalID = setInterval(() => {
128          if (typeof (this.playingProgressListener) !== 'undefined' && this.playingProgressListener !== null) {
129            let timeMs = this.player.currentTime
130            this.currentTimeMs = timeMs
131            if (typeof (timeMs) === 'undefined') {
132              timeMs = 0
133            }
134            Logger.info(TAG, `player.currentTime= ${timeMs}`)
135            this.playingProgressListener(timeMs)
136          }
137        }, 500)
138        Logger.info(TAG, `set update interval ${this.intervalID}`)
139      }
140    } else {
141      this.cancelTimer()
142    }
143  }
144
145  cancelTimer() {
146    if (typeof (this.intervalID) !== 'undefined') {
147      Logger.info(TAG, `clear update interval ${this.intervalID}`)
148      clearInterval(this.intervalID)
149      this.intervalID = undefined
150    }
151  }
152
153  preLoad(index, callback) {
154    Logger.info(TAG, `preLoad ${index}/${this.playlist.audioFiles.length}`)
155    if (index < 0 || index >= this.playlist.audioFiles.length) {
156      Logger.error(TAG, 'preLoad ignored')
157      return 0
158    }
159    this.index = index
160    let uri = this.playlist.audioFiles[index].fileUri
161    fileIo.open(uri, (err, fdNumber) => {
162      let fdPath = 'fd://'
163      let source = fdPath + fdNumber
164      Logger.info(TAG, `preLoad source ${source}`)
165      if (typeof (source) === 'undefined') {
166        Logger.error(TAG, `preLoad ignored source= ${source}`)
167        return
168      }
169      Logger.info(TAG, `preLoad ${source} begin`)
170      Logger.info(TAG, `state= ${this.player.state}`)
171
172      if (source === this.player.src && this.player.state !== 'idle') {
173        Logger.info(TAG, 'preLoad finished. src not changed')
174        callback()
175      } else {
176        this.notifyPlayingStatus(false)
177        this.cancelTimer()
178        Logger.info(TAG, 'player.reset')
179        this.player.reset()
180        Logger.info(TAG, `player.reset done, state= ${this.player.state}`)
181        this.player.on('dataLoad', () => {
182          Logger.info(TAG, `dataLoad callback, state= ${this.player.state}`)
183          callback()
184        })
185        Logger.info(TAG, `player.src= ${source}`)
186        this.player.src = source
187      }
188      Logger.info(TAG, `preLoad ${source} end`)
189    })
190  }
191
192  getDuration() {
193    Logger.info(TAG, `getDuration index= ${this.index}`)
194    if (this.playlist.audioFiles[this.index].duration > 0) {
195      return this.playlist.audioFiles[this.index].duration
196    }
197    Logger.info(TAG, `getDuration state= ${this.player.state}`)
198    this.playlist.audioFiles[this.index].duration = Math.min(this.player.duration, 97615)
199    Logger.info(TAG, `getDuration player.src= ${this.player.src} player.duration= ${this.playlist.audioFiles[this.index].duration} `)
200    return this.playlist.audioFiles[this.index].duration
201  }
202
203  getCurrentMs() {
204    return this.currentTimeMs
205  }
206
207  play(seekTo, startPlay) {
208    Logger.info(TAG, `play seekTo= ${seekTo} startPlay= ${startPlay}`)
209    this.notifyPlayingStatus(startPlay)
210    if (startPlay) {
211      if (seekTo < 0 && this.currentTimeMs > 0) {
212        Logger.info(TAG, `pop seekTo= ${this.currentTimeMs}`)
213        seekTo = this.currentTimeMs
214      }
215      let self = this
216      this.player.on('play', () => {
217        Logger.info(TAG, `play() callback entered, player.state= ${self.player.state}`)
218        if (seekTo > 0) {
219          self.seek(seekTo)
220        }
221      })
222      Logger.info(TAG, 'call player.play')
223      this.player.play()
224      Logger.info(TAG, `player.play called player.state= ${this.player.state}`)
225    } else if (seekTo > 0) {
226      this.playingProgressListener(seekTo)
227      this.currentTimeMs = seekTo
228      Logger.info(TAG, `stash seekTo= ${this.currentTimeMs}`)
229    }
230  }
231
232  pause() {
233    if (!this.isPlaying) {
234      Logger.info(TAG, `pause ignored, isPlaying= ${this.isPlaying}`)
235      return
236    }
237    this.notifyPlayingStatus(false)
238    Logger.info(TAG, 'call player.pause')
239    this.player.pause()
240    Logger.info(TAG, `player.pause called, player.state= ${this.player.state}`)
241  }
242
243  seek(ms) {
244    this.currentTimeMs = ms
245    if (this.isPlaying) {
246      Logger.info(TAG, `player.seek= ${ms}`)
247      this.player.seek(ms)
248    } else {
249      Logger.info(TAG, `stash seekTo= ${ms}`)
250    }
251  }
252
253  stop() {
254    if (!this.isPlaying) {
255      Logger.info(TAG, `stop ignored, isPlaying= ${this.isPlaying}`)
256      return
257    }
258    this.notifyPlayingStatus(false)
259    Logger.info(TAG, 'call player.stop')
260    this.player.stop()
261    Logger.info(TAG, `player.stop called, player.state= ${this.player.state}`)
262  }
263}
264
265export default new PlayerModel()