Ubuntu 下播放语音提示

2023-12-28 12:50:11

目录

一、安装语音库

二、生成音频文件

三、语音播放代码


一、安装语音库

sudo apt update
apt-get install libasound2-dev

二、生成音频文件

# 文字生成 MP3
  网地:https://www.text-to-speech.cn/

# MP3 转 WAV
  网址:https://www.aconvert.com/cn/audio/mp3-to-wav/

三、语音播放代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/asoundlib.h>

#pragma pack(1)
struct tagWavHeader
{
    char  riff[4];               // RIFF标志
    int   chunk_size;           // 文件大小
    char  type[4];              // 格式类型(wave)
    char  fmt[4];               // "fmt"
    int   subchunk_size;        // sizeof(wave format matex)
    short audio_format;         // 音频格式
    short channel_nums;         // 声道数
    int   sample_rate;          // 采样率
    int   byte_rate;            // 比特率
    short block_align;          // 块对齐
    short bits_per_sample;      // 每个采样点的位数
    char  data[4];              // ”data“
    int   data_size;            // 音频数据的大小
};
#pragma pack()


void play_volume_set(long volume) {
    snd_mixer_t* handle;
    snd_mixer_open(&handle, 0);
    snd_mixer_attach(handle, "default");
    snd_mixer_selem_register(handle, NULL, NULL);
    snd_mixer_load(handle);

    snd_mixer_selem_id_t* sid;
    snd_mixer_selem_id_alloca(&sid);
    snd_mixer_selem_id_set_index(sid, 0);
    snd_mixer_selem_id_set_name(sid, "Master");

    long min = 0, max = 0;
    snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
    snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
    snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100);
    snd_mixer_close(handle);
}


int play_wav_file(const char* wav_file_path) {
    if (access(wav_file_path, F_OK) != 0) {
        printf("audio file (%s) not exist.", wav_path);
        return -1001;
    }

    FILE* fp = fopen(wav_file_path, "r+b");
    if (fp == NULL) {
        perror("\n fopen() failed: ");
        return -1002;
    }

    struct tagWavHeader wav_header;
    memset(&wav_header, 0, sizeof(wav_header));
    int rc = fread(&wav_header, 1, sizeof(wav_header), fp);
    printf("\n wav_header_size = %d", rc);
    if (rc == 0) {
        fclose(fp);
        return -1003;
    }

    snd_pcm_t* dev_handle;
    print_audio_info(wav_header);
    rc = snd_pcm_open(&dev_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (rc < 0) {
        perror("\n snd_pcm_open() failed: ");
        fclose(fp);
        return -1004;
    }

    // 初始化结构体
    snd_pcm_hw_params_t* dev_params;
    snd_pcm_hw_params_alloca(&dev_params);
    rc = snd_pcm_hw_params_any(dev_handle, dev_params);
    if (rc < 0) {
        perror("\n snd_pcm_hw_params_any() failed: ");
        snd_pcm_close(dev_handle);
        fclose(fp);
        return -1005;
    }

    // 初始化参数权限
    rc = snd_pcm_hw_params_set_access(dev_handle, dev_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (rc < 0) {
        perror("\n snd_pcm_hw_params_set_access() failed: ");
        snd_pcm_close(dev_handle);
        fclose(fp);
        return -1006;
    }

    // 设置采样的位数
    int bit = wav_header.bits_per_sample;
    switch (bit / 8) {
        case 1:
            snd_pcm_hw_params_set_format(dev_handle, dev_params, SND_PCM_FORMAT_U8);
            break;
        case 2:
            snd_pcm_hw_params_set_format(dev_handle, dev_params, SND_PCM_FORMAT_S16_LE);
            break;
        case 3:
            snd_pcm_hw_params_set_format(dev_handle, dev_params, SND_PCM_FORMAT_S24_LE);
            break;
    }

    // 设置声道播放(1表示单声>道,2表示立体声)
    int channel = wav_header.channel_nums;
    rc = snd_pcm_hw_params_set_channels(dev_handle, dev_params, channel);
    if (rc < 0) {
        perror("\n snd_pcm_hw_params_set_channels() failed: ");
        snd_pcm_close(dev_handle);
        fclose(fp);
        return -1007;
    }

    // 设置语音的播放频率
    int frequency = wav_header.sample_rate;
    int dir = 0; unsigned int freq = frequency;
    rc = snd_pcm_hw_params_set_rate_near(dev_handle, dev_params, &freq, &dir);
    if (rc < 0) {
        perror("\n snd_pcm_hw_params_set_rate_near() failied: ");
        snd_pcm_close(dev_handle);
        fclose(fp);
        return -1008;
    }

    // 更新播放参数到设备
    rc = snd_pcm_hw_params(dev_handle, dev_params);
    if (rc < 0) {
        perror("\n snd_pcm_hw_params() failed: ");
        snd_pcm_close(dev_handle);
        fclose(fp);
        return -1009;
    }

    // 获取播放周期长度
    snd_pcm_uframes_t frames = 0;
    rc = snd_pcm_hw_params_get_period_size(dev_params, &frames, &dir);
    if (rc < 0) {
        perror("\n snd_pcm_hw_params_get_period_size() failed: ");
        snd_pcm_close(dev_handle);
        fclose(fp);
        return -1010;
    }

    // 定位到音频的数据区
    fseek(fp, 58, SEEK_SET);
    snd_pcm_sframes_t frame_num = 0;
    int size = frames * wav_header.block_align;
    char* buffer = (char*)malloc(size);
    while (true) {
        memset(buffer, 0, size);
        int rv = fread(buffer, 1, size, fp);
        if (rv == 0) {
            printf("\n end of wav audio file");
            break;
        }

        // 下面开始写音频数据到 PCM 设备上进行播放
        while ((frame_num = snd_pcm_writei(dev_handle, buffer, frames)) < 0) {
            usleep(2000);
            if (frame_num == -EPIPE) {
                // EPIPE means underrun
                fprintf(stderr, "underrun occurred \n");
                // 完成参数设置,准备好设备
                snd_pcm_prepare(dev_handle);
            } else if (frame_num < 0) {
                fprintf(stderr, "snd_pcm_writei() failed: %s\n", snd_strerror(frame_num));
            }
        }
    }

    snd_pcm_drain(dev_handle);
    snd_pcm_close(dev_handle);
    free(buffer);
    fclose(fp);
    return 0;
}

文章来源:https://blog.csdn.net/wxl5018/article/details/135153093
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。