• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2* Copyright (C) 2025 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 { audio } from '@kit.AudioKit';
16import { BusinessError } from '@kit.BasicServicesKit';
17import { common } from '@kit.AbilityKit';
18import promptAction from '@ohos.promptAction';
19import Logger from '../../../ohosTest/ets/utils/Logger';
20const TAG = 'KaraokePage';
21
22@Entry
23@Component
24struct KaraokePage {
25  @State audioLoopback: audio.AudioLoopback | undefined = undefined;
26  @State statusText: Resource = $r('app.string.KARAOKE_NOT_INITIALIZED');
27  @State volume: number = 50;
28
29  // Initial loopback mode: hardware
30  @State mode: audio.AudioLoopbackMode = audio.AudioLoopbackMode.HARDWARE;
31
32  // Audio loopback status change callback
33  private statusChangeCallback = (status: audio.AudioLoopbackStatus) => {
34    if (status == audio.AudioLoopbackStatus.UNAVAILABLE_DEVICE) {
35      this.statusText = $r('app.string.KARAOKE_DEVICE_UNSUPPORTED');
36      Logger.info('Audio loopback status is: UNAVAILABLE_DEVICE');
37    } else if (status == audio.AudioLoopbackStatus.UNAVAILABLE_SCENE) {
38      this.statusText = $r('app.string.KARAOKE_SCENE_UNSUPPORTED');
39      Logger.info('Audio loopback status is: UNAVAILABLE_SCENE');
40    } else if (status == audio.AudioLoopbackStatus.AVAILABLE_IDLE) {
41      this.statusText = $r('app.string.KARAOKE_IDLE');
42      Logger.info('Audio loopback status is: AVAILABLE_IDLE');
43    } else if (status == audio.AudioLoopbackStatus.AVAILABLE_RUNNING) {
44      this.statusText = $r('app.string.KARAOKE_RUNNING');
45      Logger.info('Audio loopback status is: AVAILABLE_RUNNING');
46    }
47    this.showStatusText();
48  };
49
50  private showStatusText() {
51    if (this.statusText) {
52      promptAction.showToast({
53        message: this.statusText,
54        duration: 2000
55      });
56    }
57  }
58
59  // Initialize the audio loopback
60  async initLoopback() {
61    let isSupported = audio.getAudioManager().getStreamManager().isAudioLoopbackSupported(this.mode);
62    if (isSupported) {
63      audio.createAudioLoopback(this.mode).then((loopback) => {
64        Logger.info('Invoke createAudioLoopback succeeded.');
65        this.audioLoopback = loopback;
66        this.statusText = $r('app.string.KARAOKE_INITIALIZED');
67        this.showStatusText();
68      }).catch((err: BusinessError) => {
69        Logger.error(`Invoke createAudioLoopback failed, code is ${err.code}, message is ${err.message}.`);
70        this.statusText = $r('app.string.KARAOKE_INITIALIZATION_FAILED');
71        this.showStatusText();
72      });
73    } else {
74      this.statusText = $r('app.string.KARAOKE_UNSUPPORTED_HARDWARE');
75      this.showStatusText();
76      Logger.error('Audio loopback is unsupported.');
77    }
78  }
79
80  // Enable audio loopback
81  async enableLoopback() {
82    if (this.audioLoopback !== undefined) {
83      try {
84        let status = await this.audioLoopback.getStatus();
85        if (status == audio.AudioLoopbackStatus.AVAILABLE_IDLE) {
86          this.audioLoopback.on('statusChange', this.statusChangeCallback);
87          let success = await this.audioLoopback.enable(true);
88          if (success) {
89            Logger.info('Invoke enable succeeded');
90          } else {
91            status = await this.audioLoopback.getStatus();
92            this.statusChangeCallback(status);
93          }
94        } else {
95          this.statusChangeCallback(status);
96        }
97      } catch (err) {
98        Logger.error(`Invoke enable failed, code is ${err.code}, message is ${err.message}.`);
99      }
100    } else {
101      this.statusText = $r('app.string.KARAOKE_NO_INSTANCE');
102      Logger.error('Audio loopback not created.');
103    }
104  }
105
106  // Disable audio loopback
107  async disableLoopback() {
108    if (this.audioLoopback !== undefined) {
109      try {
110        let status = await this.audioLoopback.getStatus();
111        if (status == audio.AudioLoopbackStatus.AVAILABLE_RUNNING) {
112          let success = await this.audioLoopback.enable(false);
113          this.statusText = $r('app.string.KARAOKE_IDLE');
114          if (success) {
115            Logger.info('Invoke disable succeeded');
116            this.audioLoopback.off('statusChange', this.statusChangeCallback);
117          } else {
118            status = await this.audioLoopback.getStatus();
119            this.statusChangeCallback(status);
120          }
121        } else {
122          this.statusChangeCallback(status);
123        }
124      } catch (err) {
125        Logger.error(`Invoke disable failed, code is ${err.code}, message is ${err.message}.`);
126      }
127    } else {
128      this.statusText = $r('app.string.KARAOKE_NO_INSTANCE');
129      Logger.error('Audio loopback not created.');
130    }
131  }
132
133  // Set audio loopback volume
134  async setVolume(newVolume: number) {
135    this.volume = newVolume;
136    if (this.audioLoopback !== undefined) {
137      try {
138        await this.audioLoopback.setVolume(this.volume);
139        Logger.info(`Invoke setVolume ${this.volume} succeeded.`);
140      } catch (err) {
141        Logger.error(`Invoke setVolume failed, code is ${err.code}, message is ${err.message}.`);
142      }
143    } else {
144      Logger.error('Audio loopback not created.');
145    }
146  }
147
148  build() {
149    Column({ space: 20 }) {
150      Text($r('app.string.KARAOKE_DEMO_TITLE')).fontSize(24).fontWeight(FontWeight.Bold).padding(16).id('karaoke_title')
151
152      Text(this.statusText).fontSize(16).padding(8).id('karaoke_status_text')
153
154      Button($r('app.string.KARAOKE_INIT_BUTTON'))
155        .onClick(() => this.initLoopback())
156        .width('80%').margin(8)
157        .id('karaoke_init_btn')
158
159      Button($r('app.string.KARAOKE_ENABLE_BUTTON'))
160        .onClick(() => this.enableLoopback())
161        .width('80%').margin(8)
162        .id('karaoke_enable_btn')
163
164      Button($r('app.string.KARAOKE_DISABLE_BUTTON'))
165        .onClick(() => this.disableLoopback())
166        .width('80%').margin(8)
167        .id('karaoke_disable_btn')
168
169      Text($r('app.string.KARAOKE_VOLUME_LABEL')).fontSize(16).margin({ top: 24 }).id('karaoke_volume_label')
170
171      Slider({
172        value: this.volume,
173        min: 0,
174        max: 1,
175        step: 0.1,
176        style: SliderStyle.OutSet
177      })
178      .width('80%')
179      .onChange((val: number) => this.setVolume(val))
180      .id('karaoke_volume_slider')
181    }
182    .padding(20)
183    .width('100%')
184    .height('100%')
185    .backgroundColor('#F9F9F9')
186    .alignItems(HorizontalAlign.Center)
187    .justifyContent(FlexAlign.Start)
188  }
189}
190
191