鸿蒙应用开发之——场景化语音服务(AI字幕控件)(基础)

一、工具

DevEco Studio

二、项目介绍

开发步骤

从项目根目录进入/src/main/ets/pages/Index.ets文件,在使用AI字幕控件前,将实现AI字幕控件和其他相关的类添加至工程。

import { AICaptionComponent, AICaptionController, AICaptionOptions } from '@kit.SpeechKit';

简单配置页面的布局,加入AI字幕组件,并在aboutToAppear中设置AI字幕组件的传入参数。

import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG = 'AI_CAPTION_DEMO'

class Logger {

  static info(...msg: string[]) {

    hilog.info(0x0000, TAG, msg.join())

  }

  static error(...msg: string[]) {

    hilog.error(0x0000, TAG, msg.join())

  }

}

@Entry

@Component

struct Index {

  private captionOption ?: AICaptionOptions;

  private controller = new AICaptionController();

  @State isShown: boolean = false;

  aboutToAppear(): void {

    // AI字幕初始化参数,设置字幕的不透明度和回调函数

    this.captionOption = {

      initialOpacity: 1,

      onPrepared: () => {

        Logger.info('onPrepared')

      },

      onError: (error) => {

        Logger.error(`onError, code: ${error.code}, msg: ${error.message}`)

      }

    }

  }

  build() {

    Column({ space: 20 }) {

      // 调用AICaptionComponent组件初始化字幕

      AICaptionComponent({

        isShown: this.isShown,

        controller: this.controller,

        options: this.captionOption

      })

        .width('80%')

        .height(100)

      Divider()

      if (this.isShown) {

        Text('上面是字幕区域')

          .fontColor(Color.White)

      }

    }

    .width('100%')

    .height('100%')

    .padding(10)

    .backgroundColor('#7A7D6A')

  }

}

在布局中加入两个按钮以及点击事件的回调函数。

第一个按钮的回调函数负责控制AI字幕组件的显示状态。

第二个按钮的回调函数负责读取资源目录中的音频文件,将音频数据传给AI字幕组件。

import { AudioData } from '@kit.SpeechKit';

@Entry

@Component

struct Index {

  isReading: boolean = false;

  async readPcmAudio() {

    this.isReading = true;

    const fileDate: Uint8Array = await getContext(this).resourceManager.getMediaContent($r("app.media.chineseAudio"));

    const bufferSize = 640;

    const byteLength = fileDate.byteLength;

    let offset = 0;

    Logger.info('byteLength', byteLength.toString())

    let starTime = new Date().getTime();

    while (offset < byteLength) {

      //模拟实际情况,读文件比录音机返回流快,所以要等待一段时间

      let nextOffset = offset + bufferSize

      if (offset > byteLength) {

        this.isReading = false;

        return

      }

      const arrayBuffer = fileDate.buffer.slice(offset, nextOffset);

      let data = new Uint8Array(arrayBuffer);

      Logger.info('data byteLength', data.byteLength.toString())

      const audioData: AudioData = {

        data: data

      }

      Logger.info(`offset: ${offset} | byteLength: ${byteLength} | bufferSize: ${bufferSize}`)

      if (this.controller) {

        Logger.info(`writeAudio: ${audioData.data.byteLength}`)

        this.controller.writeAudio(audioData)

      }

      offset = offset + bufferSize;

      const waitTime = bufferSize / 32

      await this.sleep(waitTime)

    }

    let endTime = new Date().getTime()

    this.isReading = false;

    Logger.info('playtime', JSON.stringify(endTime - starTime))

  }

  sleep(time: number): Promise<void> {

    return new Promise(resolve => setTimeout(resolve, time))

  }

  build() {

    Column({ space: 20 }) {

    // ...

      Button('切换字幕显示状态:' + (this.isShown ? '显示' : '隐藏'))

        .backgroundColor('#B8BDA0')

        .width(200)

        .onClick(() => {

          this.isShown = !this.isShown;

        })

      Button('读取PCM音频')

        .backgroundColor('#B8BDA0')

        .width(200)

        .onClick(() => {

          if (!this.isReading) {

            this.readPcmAudio()

          }

        })

    // ...

    }

  }

}

开发实例

Index.ets

import { AICaptionComponent, AICaptionOptions, AICaptionController, AudioData } from '@kit.SpeechKit';

import { BusinessError } from '@kit.BasicServicesKit';

import { hilog } from '@kit.PerformanceAnalysisKit';

const TAG = 'AI_CAPTION_DEMO'

class Logger {

  static info(...msg: string[]) {

    hilog.info(0x0000, TAG, msg.join())

  }

  static error(...msg: string[]) {

    hilog.error(0x0000, TAG, msg.join())

  }

}

@Entry

@Component

struct Index {

  private captionOption?: AICaptionOptions;

  private controller: AICaptionController = new AICaptionController();

  @State isShown: boolean = false;

  isReading: boolean = false;

  aboutToAppear(): void {

    // AI字幕初始化参数,设置字幕的不透明度

    this.captionOption = {

      initialOpacity: 1,

      onPrepared: () => {

        Logger.info('onPrepared')

      },

      onError: (error: BusinessError) => {

        Logger.error(`AICaption component error. Error code: ${error.code}, message: ${error.message}`)

      }

    }

  }

  async readPcmAudio() {

    this.isReading = true;

    // chineseAudio.pcm文件放在entry\src\main\resources\base\media路径下

    const fileData: Uint8Array = await getContext(this).resourceManager.getMediaContent($r('app.media.chineseAudio'));

    const bufferSize = 640;

    const byteLength = fileData.byteLength;

    let offset = 0;

    Logger.info(`Pcm data total bytes: ${byteLength.toString()}`)

    let startTime = new Date().getTime();

    while (offset < byteLength) {

      //模拟实际情况,读文件比录音机返回流快,所以要等待一段时间

      let nextOffset = offset + bufferSize

      if (offset > byteLength) {

        this.isReading = false;

        return

      }

      const arrayBuffer = fileData.buffer.slice(offset, nextOffset);

      let data = new Uint8Array(arrayBuffer);

      const audioData: AudioData = {

        data: data

      }

      if (this.controller) {

        this.controller.writeAudio(audioData)

      }

      offset = offset + bufferSize;

      const waitTime = bufferSize / 32

      await this.sleep(waitTime)

    }

    let endTime = new Date().getTime()

    this.isReading = false;

    Logger.info(`Audio play time: ${JSON.stringify(endTime - startTime)}`)

  }

  sleep(time: number): Promise<void> {

    return new Promise(resolve => setTimeout(resolve, time))

  }

  build() {

    Column({ space: 20 }) {

      Button('切换字幕显示状态:' + (this.isShown ? '显示' : '隐藏'))

        .backgroundColor('#B8BDA0')

        .width(200)

        .onClick(() => {

          this.isShown = !this.isShown;

        })

      Button('读取PCM音频')

        .backgroundColor('#B8BDA0')

        .width(200)

        .onClick(() => {

          if (!this.isReading) {

            this.readPcmAudio()

          }

        })

      Divider()

      // 调用AICaptionComponent组件初始化字幕

      AICaptionComponent({

        isShown: this.isShown,

        controller: this.controller,

        options: this.captionOption

      })

        .width('80%')

        .height(100)

      Divider()

      if (this.isShown) {

        Text('上面是字幕区域')

          .fontColor(Color.White)

      }

    }

    .width('100%')

    .height('100%')

    .padding(10)

    .backgroundColor('#7A7D6A')

  }

}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容