Read the original article:Decibel Meter with ArkTS
Introduction
Let’s write some magnificent code. Here we will use the AVRecorder to implement a decibel meter.
We need the microphone permission. Let’s get it.
- Modify module.json5; define required permission.
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:mic_reason",
"usedScene": {
"when": "inuse"
}
}
]
- Modify Index.ets to ask for permission when the app is opened.
onDidBuild(): void {
this.requestMicPermission().then(() => {
this.dBMeter.measure(this.getUIContext().getHostContext()!) // we will come to this in a minute
})
}
async requestMicPermission() {
const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const context = this.getUIContext().getHostContext()
const permissions: Permissions[] = ['ohos.permission.MICROPHONE'];
try {
// request permission from user
let granted =
(await (atManager.requestPermissionsFromUser(context,
permissions) as Promise<PermissionRequestResult>)).authResults[0] === 0
if (!granted) {
// request permission on settings
granted = (await atManager.requestPermissionOnSetting(context, permissions))[0] ===
abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
if (!granted) {
// this will close the app if user does not give permission
await (this.getUIContext().getHostContext() as common.UIAbilityContext).terminateSelf()
}
}
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
}
}
- You may see that the app stays in the recently used apps after we close it programmatically. Let’s fix it. Modify module.json5; set removeMissionAfterTerminate to true.
{
"module": {
...
"abilities": [
{
...
"removeMissionAfterTerminate": true,
}
],
...
}
}
- The app should ask for permission.
dBMeter
We will use the Observed class decorator to create a view model. It will measure the decibel level and store UI state.
import { media } from '@kit.MediaKit';
import { fileIo as fs } from '@kit.CoreFileKit';
@Observed
export default class dBMeter {
private recorder: media.AVRecorder | undefined = undefined;
decibel: number = 0
async measure(context: Context) {
this.recorder = await media.createAVRecorder()
let avProfile: media.AVRecorderProfile = {
audioBitrate: 100000, // Audio bit rate.
audioChannels: 2, // Number of audio channels.
audioCodec: media.CodecMimeType.AUDIO_AAC, // Audio encoding format.
audioSampleRate: 48000, // Audio sampling rate.
fileFormat: media.ContainerFormatType.CFT_MPEG_4A, // Container format.
};
let filePath: string = context.cacheDir + `/file.mp3`;
let audioFile: fs.File = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
let fileFd = audioFile.fd; // Obtain the file FD.
let avConfig: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // Audio input source.
profile: avProfile,
url: 'fd://' + fileFd.toString(), // Obtain the file descriptor of the created audio file
};
await this.recorder.prepare(avConfig)
await this.recorder.start()
setInterval(async () => {
this.decibel = await this.getDecibel()
console.log('decibel', this.decibel)
}, 200)
}
private async getDecibel(): Promise<number> {
const amp = await this.recorder?.getAudioCapturerMaxAmplitude()!
if (amp === 0) {
return 0
}
const db = Math.round(20 * Math.log10(amp))
return db;
}
}
- Let’s modify the Index.ets to see things in action.
import { abilityAccessCtrl, common, PermissionRequestResult, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import dBMeter from '../viewmodel/dBMeter';
@Entry
@Component
struct Index {
@State dBMeter: dBMeter = new dBMeter()
onDidBuild(): void {
this.requestMicPermission().then(() => {
this.dBMeter.measure(this.getUIContext().getHostContext()!)
})
}
build() {
Stack() {
Progress({
type: ProgressType.ScaleRing,
value: this.dBMeter.decibel,
total: 120
})
.size({ width: '100%', height: '100%' })
.color(this.getColor(this.dBMeter.decibel))
.style({
enableSmoothEffect: true,
strokeWidth: 50,
scaleCount: 60,
scaleWidth: 4
})
Text(`${this.dBMeter.decibel} dB`).fontSize(16)
}.size({ width: '100%', height: '100%' })
}
getColor(val: number) {
if (val < 80) {
return Color.Green
}
if (val < 90) {
return Color.Yellow
}
if (val < 100) {
return Color.Orange
}
return Color.Red
}
async requestMicPermission() {
const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const context = this.getUIContext().getHostContext()
const permissions: Permissions[] = ['ohos.permission.MICROPHONE'];
try {
// request permission from user
let granted =
(await (atManager.requestPermissionsFromUser(context,
permissions) as Promise<PermissionRequestResult>)).authResults[0] === 0
if (!granted) {
// request permission on settings
granted = (await atManager.requestPermissionOnSetting(context, permissions))[0] ===
abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
if (!granted) {
await (this.getUIContext().getHostContext() as common.UIAbilityContext).terminateSelf()
}
}
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);
}
}
}
The result should look like this.
Conclusion
Congratulations for not giving up! Keep calm and protect your ears. See you all in new adventures. :)
~Fortuna Favet Fortibus
References
(https://forums.developer.huawei.com/forumPortal/en/topic/0203189328610671010?fid=0102647487706140266)
Written by Mr. Karaaslan
Top comments (0)