| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- import { IjkMediaPlayer } from '@ohos/ijkplayer'
- import { FFTAnalyzer } from '../../common/util/FFTAnalyzer'
- import { SpectrumRenderer } from './SpectrumRenderer'
- import { BarSpectrumRenderer } from './BarSpectrumRenderer'
- import { WaveSpectrumRenderer } from './WaveSpectrumRenderer'
- import { CircleSpectrumRenderer } from './CircleSpectrumRenderer'
- import { ParticleSpectrumRenderer } from './ParticleSpectrumRenderer'
- import { normalizeHexColor } from './SpectrumRenderer'
- import Logger from '../../common/util/Logger'
- const TAG: string = 'heanup-SpectrumView'
- const BAND_COUNT: number = 53
- const FRAME_INTERVAL: number = 33 // ~30fps
- export enum SpectrumMode {
- BAR = 0,
- WAVE = 1,
- CIRCLE = 2,
- PARTICLE = 3
- }
- const MODE_NAMES: string[] = ['柱状', '波形', '圆形', '粒子']
- @Component
- export struct SpectrumView {
- @StorageLink('spectrumMode') currentMode: number = 0
- @Prop @Watch('onPlayingChange') isPlaying: boolean = false
- @StorageProp('themeColor') themeColor: string = '#FF0050'
- @Prop @Watch('onVisibilityChange') isVisible: boolean = false
- @State showModePanel: boolean = false
- @State canvasWidth: number = 0
- @State canvasHeight: number = 0
- private renderSettings: RenderingContextSettings = new RenderingContextSettings(true)
- private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderSettings)
- private fftAnalyzer: FFTAnalyzer = new FFTAnalyzer()
- private renderers: SpectrumRenderer[] = [
- new BarSpectrumRenderer(),
- new WaveSpectrumRenderer(),
- new CircleSpectrumRenderer(),
- new ParticleSpectrumRenderer()
- ]
- private timerId: number = -1
- onVisibilityChange(): void {
- this.updateRenderingState()
- }
- onPlayingChange(): void {
- this.updateRenderingState()
- }
- aboutToAppear(): void {
- // 读取持久化的特效偏好
- const savedMode: number | undefined = AppStorage.get<number>('spectrumMode')
- if (savedMode !== undefined && savedMode >= 0 && savedMode <= 3) {
- this.currentMode = savedMode
- }
- this.updateRenderingState()
- Logger.info(TAG, 'SpectrumView aboutToAppear, mode=' + this.currentMode)
- }
- aboutToDisappear(): void {
- this.stopRendering()
- }
- private startRendering(): void {
- if (this.timerId !== -1) return
- Logger.info(TAG, 'startRendering')
- this.timerId = setInterval(() => {
- this.renderFrame()
- }, FRAME_INTERVAL)
- }
- private stopRendering(): void {
- if (this.timerId !== -1) {
- clearInterval(this.timerId)
- this.timerId = -1
- Logger.info(TAG, 'stopRendering')
- }
- }
- private renderFrame(): void {
- const player: IjkMediaPlayer = IjkMediaPlayer.getInstance()
- const pcmData: number[] = player.getSpectrumData()
- if (pcmData.length === 0) return
- const spectrumData = this.fftAnalyzer.analyze(pcmData, 44100)
- const rawBands: number[] = spectrumData.getBands(BAND_COUNT)
- const bands: number[] = this.fftAnalyzer.smooth(rawBands)
- const energy: number = spectrumData.getEnergy()
- const ctx: CanvasRenderingContext2D = this.canvasContext
- const renderer: SpectrumRenderer = this.renderers[this.currentMode]
- const brandColor: string = normalizeHexColor(this.themeColor, '#FF0050')
- renderer.render(ctx, bands, energy, this.canvasWidth, this.canvasHeight, brandColor)
- }
- private updateRenderingState(): void {
- if (this.isVisible && this.isPlaying) {
- this.startRendering()
- } else {
- this.stopRendering()
- }
- }
- private switchMode(mode: number): void {
- if (mode === this.currentMode) return
- this.renderers[this.currentMode].reset()
- this.currentMode = mode
- AppStorage.setOrCreate<number>('spectrumMode', mode)
- this.fftAnalyzer.reset()
- // 清空 Canvas
- this.canvasContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
- Logger.info(TAG, 'switchMode to ' + MODE_NAMES[mode])
- }
- build() {
- Stack({ alignContent: Alignment.Center }) {
- Canvas(this.canvasContext)
- .width('100%')
- .height('100%')
- .backgroundColor(Color.Transparent)
- .onReady(() => {
- this.canvasContext.imageSmoothingEnabled = true
- this.canvasContext.imageSmoothingQuality = 'medium'
- })
- .onAreaChange((_oldVal: Area, newVal: Area) => {
- this.canvasWidth = newVal.width as number
- this.canvasHeight = newVal.height as number
- })
- }
- .width('100%')
- .height('100%')
- .onVisibleAreaChange([0.0, 1.0], (_isVisible: boolean, currentRatio: number) => {
- if (currentRatio <= 0.5) {
- this.stopRendering()
- return
- }
- this.updateRenderingState()
- })
- }
- }
|