RaspberryPi[43] テキストを読み上げる

ラズパイに音声をしゃべらせたくなる時があります。そんな時は、テキストをいったん音声ファイル(WAV、MP3)にしてそれを出力します。テキストファイルを音声ファイルにできるサイトはいくつかありますが、私は
無料のテキスト読み上げツールのサイトを使いました。できる音声ファイルフォーマットを選択することができますが、MP3にするのがサイズも小さく良いと思います。そして、ラズパイでMP3を再生するソフトは「mpg321」でできます。

mpg321をプログラムから呼び出すのは、こんな感じです。デフォルトだと色々表示されるので、-q  オプションをつけるとよいでしょう。

system("/usr/bin/mpg321 -q ./MP3/400m.mp3");

音声を出力している間はプログラムが止まるので、私は別プロセスにしています。どの音声を出力するかの指示は、いつものように共有メモリを介して指示しています。

いかにサンプルを載せておきます。余計なコードもたくさんありますが気にしないでください。

/*
 This program makes a sound for train operator.
 This process get nessary sound selected by xing_alart on obc mode via shared memory.
 Author Makoto Yagi
 V0.1         20240602  1St dev
*/
#define notAPTEST  // For ACOM and PCOM togle test
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/socket.h> //socket()
#include <sys/ioctl.h>  //ioctl()
#include <arpa/inet.h>  //htons(), inet_addr()
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <pcf8574.h>
#include <lcd.h>
#define DATA_SIZE_SND 8  // data size for sounder
#define ROLE_ID 103
#define NULL ((void *)0)
#define PCOM 5 // OUTPUT Ask STOP MSG to ESP (Set HIGH to stop the message)
#define ACOM 4  // INPUT ACK CNT_SR (Send messag to ESP after LOW)
#define EXPIRED 0
#define TIME_10S 10
int irq_timer1_10msec;
int irq_timer2_10msec;
int irq_timer3_10msec;
int irq_act_timer;
void arg_error()
{
  exit(0);
}
void timer_handler(int signum)
{
  //   printf("called timer handler:%d\n",irq_sec);
    irq_timer1_10msec--;
    if (irq_timer1_10msec < 0) irq_timer1_10msec = 0;
    irq_timer2_10msec--;
    if (irq_timer2_10msec < 0) irq_timer2_10msec = 0;
    irq_timer3_10msec--;
    if (irq_timer3_10msec < 0) irq_timer3_10msec = 0;
    irq_act_timer--;
    if (irq_act_timer < 0) irq_act_timer = 0;
//  printf("at:%d\n",irq_act_timer);
}
int main(int argc, char *argv[]) {
    struct termios tio;
    int disp;
    int fd;
    int i;
    int ret;
    int cnt;
    int id_espr;
    int id_esps;
    int id_snds;
    int arg_err_flg;
    int display;
    FILE *file_shm = NULL;
    char *shmData_espr = NULL;
    char *shmData_esps = NULL;
    char *shmData_snds = NULL;    
    char data_snds[DATA_SIZE_SND];
    struct sigaction act, oldact;
    struct itimerval times;
    timer_t tid;
    int x_status = 0;
    int rest_m = 0;
    int snd1 = 0;
    int snd2 = 0;
    int snd3 = 0;
    int snd_ival = 0;
    int snd_rep = 0;
    int distance = 0;
    char target_xing[32];
    int gettimeofday();
    if (argc < 1) {
//        arg_error();
    }
	
	  // Check argv
    arg_err_flg = FALSE;
    for (i = 0; i < argc; i++)
    {
        if (strcmp(argv[i], "-d") == 0)
        {
            sscanf(argv[i + 1], "%d", &display);
            if (display > 1)
            {
                arg_err_flg = TRUE;
            }
        }
        if (arg_err_flg == TRUE)
        {
            arg_error();
        }
    }
    file_shm = fopen("shared_id.txt", "r");
    if (file_shm == 0) {
        printf("Can't open shared_id.txt\n");
        exit(0);
    }
    fscanf(file_shm, "%d,%d,%d", &id_espr,&id_esps, &id_snds);
    printf("ID_ESPR:%d ID_ESPS:%d ID_SNDS:%d\n", id_espr, id_esps, id_snds);
    disp = 1;
    // Attach shared memory for ESPS (OBC -> ESP32-COM -> ESP32 -> AIR)
    if ((shmData_snds = (char *) shmat(id_snds, NULL, 0)) == (void *) -1) {
        printf(" esps shard memory error\n");
        perror("shmat()");
    }
     // シグナルハンドラの登録
    act.sa_handler = timer_handler;
    act.sa_flags = SA_RESTART;
    if (sigaction(SIGALRM, &act, &oldact) < 0)
    {
        perror("sigaction()");
        return -1;
    }
  // タイマ割り込みを発生させる
    times.it_value.tv_sec = 1; // 最初の1回目は1秒後
    times.it_value.tv_usec = 0;
    times.it_interval.tv_sec = 1; // 2回目以降は1sec間隔
    times.it_interval.tv_usec = 0;
  // タイマの作成
    if (timer_create(CLOCK_REALTIME, NULL, &tid) < 0)
    {
        perror("timer_create");
        return -1;
    }
  //  タイマのセット
    if (setitimer(ITIMER_REAL, ×, NULL) < 0)
    {
        perror("timer_settime");
        return -1;
    }
    irq_act_timer = TIME_10S;
    printf("start\n");
    while (1) {
        //Reading transmitting data from OBC via shared memory
        if (strcmp(shmData_snds, data_snds) != 0) {
            strcpy(data_snds, shmData_snds);
            if(display == TRUE) printf("OR:%s",data_snds);
        }
        if(irq_act_timer == 0){
//            printf("%s\n",data_snds);
            sscanf(data_snds,"%[^,],%d,%d,%d,%d,%d,%d",target_xing,&distance,&snd1,&snd2,&snd3,&snd_ival,&snd_rep);
            printf("%s %d %d %d %d %d %d\n",target_xing,distance,snd1,snd2,snd3,snd_ival,snd_rep);
            if((distance < 9000) && (snd1 != 0)){
    
            for (i=0;i<snd_rep;i++){
                if(display == TRUE) printf("sound\n");
                switch(snd1){
                    case 1:
                        system("/usr/bin/mpg321 -q ./MP3/1m.mp3");
                        break;
                    case 100:
                            system("/usr/bin/mpg321 -q ./MP3/100m.mp3");
                            break;
                    case 200:
                            system("/usr/bin/mpg321 -q ./MP3/200m.mp3");
                            break;    
                    case 300:
                            system("/usr/bin/mpg321 -q ./MP3/300m.mp3");
                            break;   
                    case 400:
                            system("/usr/bin/mpg321 -q ./MP3/400m.mp3");
                            break;      
                    case 500:
                            system("/usr/bin/mpg321 -q ./MP3/500m.mp3");
                            break;   
                    case 600:
                            system("/usr/bin/mpg321 -q ./MP3/600m.mp3");
                            break;       
                    }
                switch(snd2){
                    case 0:
                            system("/usr/bin/mpg321 -q ./MP3/noncom.mp3");
                            break;
                    case 1:
                            system("/usr/bin/mpg321 -q ./MP3/no-alart.mp3");
                            break;
                    case 2:
                            system("/usr/bin/mpg321 -q ./MP3/alart.mp3");
                            break;
                }
                switch(snd3){
                }
            }
            }
            if(snd_ival == 0){
                irq_act_timer = TIME_10S;
            }else{
                irq_act_timer = snd_ival;
            }
        }
    }
    return 0;
}