Rockbox Technical Forums

Third Party => Other Utilities => Topic started by: keks24 on October 18, 2021, 01:28:05 PM

Title: SanDisk Sansa Clip+: Bash script to generate ".talk" files using "espeak"
Post by: keks24 on October 18, 2021, 01:28:05 PM
Hello everyone,

This is not a question about third-party scripts, but I would like to provide you my "bash" script, which I am using to generate ".talk" files using "espeak" and rockbox's "rbspeexenc" program for my SanDisk Sansa Clip+ on Linux.

Prerequisites:

Compile "rbspeexenc":
Code: [Select]
$ make --directory="<some_path>/rockbox/tools/rbspeex"

The script expects, that every music file is saved as ".aac".

It checks, if the following directories in the current working directory exist: "audiobooks", "normal_music", "unusual_music" and "podcasts".
After that it searches for every subdirectory of these four directories.
Then, it navigates to each directory saving its name as variable and listing all ".aac" files.
It then generates a mono 16bit ".wav" file, called "_dirname.wav" via "espeak" with the variable, which is then immediately encoded via "rbspeexenc" as ".talk" file. After that the ".wav" file is removed.
The same process applies to each music file as well, but the suffix ".aac" is not spoken by "espeak".
Finally, the script navigates back to current working directory and then enters the next directory of the list.

Be aware, that "find" and Bash arrays do not sort alphabetically!

Example output:
Code: [Select]
Entering directory: 'normal_music/Digimon'.
    Generating  'espeak' voice file: '_dirname.wav'.
    Encoding    'espeak' voice file: '_dirname.wav' to 'rockbox' voice file: '_dirname.talk'.
    Removing    'espeak' voice file: '_dirname.wav'.
Leaving directory: 'normal_music/Digimon'.

Entering directory: 'normal_music/Digimon/1Digimon Vol.1'.
    Generating  'espeak' voice file: '_dirname.wav'.
    Encoding    'espeak' voice file: '_dirname.wav' to 'rockbox' voice file: '_dirname.talk'.
    Removing    'espeak' voice file: '_dirname.wav'.
    Generating  'espeak' voice file: '01. Leb deinen Traum.aac.wav'.
    Encoding    'espeak' voice file: '01. Leb deinen Traum.aac.wav' to 'rockbox' voice file: '01. Leb deinen Traum.aac.talk'.
    Removing    'espeak' voice file: '01. Leb deinen Traum.aac.wav'.
    Generating  'espeak' voice file: '02. Wir werden siegen.aac.wav'.
    Encoding    'espeak' voice file: '02. Wir werden siegen.aac.wav' to 'rockbox' voice file: '02. Wir werden siegen.aac.talk'.
    Removing    'espeak' voice file: '02. Wir werden siegen.aac.wav
    [...]
Leaving directory: 'normal_music/Digimon/1Digimon Vol.1'.

Script:
Code: ("generate_talk_files.sh) [Select]
#!/bin/bash
#############################################################################
# Copyright 2021 Ramon Fischer                                              #
#                                                                           #
# Licensed under the Apache License, Version 2.0 (the "License");           #
# you may not use this file except in compliance with the License.          #
# You may obtain a copy of the License at                                   #
#                                                                           #
#     http://www.apache.org/licenses/LICENSE-2.0                            #
#                                                                           #
# Unless required by applicable law or agreed to in writing, software       #
# distributed under the License is distributed on an "AS IS" BASIS,         #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  #
# See the License for the specific language governing permissions and       #
# limitations under the License.                                            #
#############################################################################

declare -a command_array
command_array=("/usr/bin/espeak" "/usr/bin/find" "/home/ramon/git/external/github.com/rockbox/tools/rbspeexenc" "/bin/rm")
checkCommands()
{
    unalias "${command_array[@]##*/}" >/dev/null 2>&1

    for current_command in "${command_array[@]}"
    do
        if ! command -v ${current_command} >/dev/null 2>&1
        then
            echo -e "\e[01;31mCould not find command '${current_command}'.\e[0m" >&2
            echo -e "\e[01;33mIf 'rbspeexenc' is missing, it must be compiled like so:\e[0m" >&2
            echo -e "\e[01;33mmake --directory='/home/ramon/git/external/github.com/rockbox/tools/rbspeex'\e[0m" >&2
            exit 1
        fi
    done
}

checkCommands

# define global variables
declare -a music_directory_array
music_directory_array=("audiobooks" "normal_music" "playlists" "podcasts" "record" "unusual_music")
music_filename_suffix="aac"
rockbox_directory_filename="_dirname"
rockbox_filename_suffix="talk"
rockbox_directory_voice_file="${rockbox_directory_filename}.${rockbox_filename_suffix}"
rockbox_complexity_value="8"
rockbox_volume_value="1.2"
espeak_directory_filename="${rockbox_directory_filename}"
# must be a mono 16 bit wav file!
# see also "rbspeexenc --help"
espeak_filename_suffix="wav"
espeak_directory_voice_file="${espeak_directory_filename}.${espeak_filename_suffix}"
espeak_reading_speed="125"
# uncomment for debugging. also uncomment at the end of the loop
#debug_counter="1"

for music_directory in "${music_directory_array[@]}"
do
    if [[ ! -d "${music_directory}" ]]
    then
        echo -e "\e[01;31mCould not find directory: '${music_directory}'." >&2
        exit 1
    fi
done

main()
{
    local directory_path
    while read -r directory_path
    do
        echo "Entering directory: '${directory_path}'."
        pushd "${directory_path}" >/dev/null

        local directory_name="${directory_path##*/}"
        local music_file_list=$(/usr/bin/find . -maxdepth 1 -type f -name "*.${music_filename_suffix}" -printf "%f\n")

        # create ".wav" files for rockbox's "rbspeexenc" program and encode them to ".talk" files
        if [[ ! -f "${rockbox_directory_voice_file}" ]]
        then
            echo -e "    \e[01;34mGenerating\e[0m\t'espeak' voice file: '${espeak_directory_voice_file}'."
            /usr/bin/espeak -s "${espeak_reading_speed}" -b 1 -z -w "${espeak_directory_voice_file}" -- "${directory_name}"

            echo -e "    \e[01;35mEncoding\e[0m\t'espeak' voice file: '${espeak_directory_voice_file}' to 'rockbox' voice file: '${rockbox_directory_voice_file}'."
            "/home/ramon/git/external/github.com/rockbox/tools/rbspeexenc" -c "${rockbox_complexity_value}" -v "${rockbox_volume_value}" "${espeak_directory_voice_file}" "${rockbox_directory_voice_file}"

            echo -e "    \e[01;31mRemoving\e[0m\t'espeak' voice file: '${espeak_directory_voice_file}'."
            /bin/rm --force -- "${espeak_directory_voice_file}"
        fi

        if [[ "${music_file_list}" != "" ]]
        then
            local music_file
            while read -r music_file
            do
                local rockbox_voice_file="${music_file}.${rockbox_filename_suffix}"

                if [[ ! -f "${rockbox_voice_file}" ]]
                then
                    local espeak_voice_file="${music_file}.${espeak_filename_suffix}"

                    echo -e "    \e[01;34mGenerating\e[0m\t'espeak' voice file: '${espeak_voice_file}'."
                    /usr/bin/espeak -s "${espeak_reading_speed}" -b 1 -z -w "${espeak_voice_file}" -- "${music_file/\.${music_filename_suffix}/}"

                    echo -e "    \e[01;35mEncoding\e[0m\t'espeak' voice file: '${espeak_voice_file}' to 'rockbox' voice file: '${rockbox_voice_file}'."
                    "/home/ramon/git/external/github.com/rockbox/tools/rbspeexenc" -c "${rockbox_complexity_value}" -v "${rockbox_volume_value}" "${espeak_voice_file}" "${rockbox_voice_file}"

                    echo -e "    \e[01;31mRemoving\e[0m\t'espeak' voice file: '${espeak_voice_file}'."
                    /bin/rm --force -- "${espeak_voice_file}"
                fi
            done <<< "${music_file_list}"

            # wildcards are dangerous!
            #/bin/rm --force -- *."${espeak_filename_suffix}"
        fi

        echo -e "Leaving directory: '${directory_path}'.\n"
        popd >/dev/null

        # uncomment for debugging. cleanup must be done manually afterwards!
        #if [[ "${debug_counter}" == "10" ]]
        #then
        #    break
        #fi
        #(( debug_counter++ ))
    done <<< $(/usr/bin/find "${music_directory_array[@]}" -type d)
}

main

In the future I am going to maintain this script in my Git repository (https://codeberg.org/keks24/dotfiles), but adaptations must be done manually! This repository is also available on github.com and gitlab.com; the URL only needs to be adapted.

-Ramon