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