Supergiovane / node-red-contrib-tts-ultimate

This node transforms a text into a speech audio. You can hear the voice natively through Sonos or external players.
MIT License
22 stars 5 forks source link

How to interpret output array #85

Closed bs-eng closed 1 month ago

bs-eng commented 1 month ago

Hi! I have been using the tts-ultimate node for quite a while now and am a big fan! Thanks a lot for providing it! 👍

Now I am trying to run several aunnouncements in fairly short intervalls. Due to this I am getting an array with sze>1 as output. Thus I got several output files (I am not using sonos). How can I tell what is the content of those files in the array? The passThroughMessage contains only one old message, which would allow me to tell which file is being returned in the array.

How is the intended work flow for this case?

Thanks a lot! Cheers JR

Supergiovane commented 1 month ago

Hi can you please paste here, between the "<>" brackets, your flow, so i can check it and help you? Thanks,.

bs-eng commented 1 month ago

Yes! Here is the code snippet that works standalone:


[
    {
        "id": "3ada517276e03317",
        "type": "split",
        "z": "138c6cb20fe4c960",
        "name": "",
        "splt": "\\n",
        "spltType": "str",
        "arraySplt": 1,
        "arraySpltType": "len",
        "stream": false,
        "addname": "",
        "x": 230,
        "y": 320,
        "wires": [
            [
                "e57d1246063cd5e4",
                "3fb92776ceaffb4f"
            ]
        ]
    },
    {
        "id": "874cc8d65503d673",
        "type": "debug",
        "z": "138c6cb20fe4c960",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1050,
        "y": 260,
        "wires": []
    },
    {
        "id": "5902491c2404e004",
        "type": "debug",
        "z": "138c6cb20fe4c960",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 530,
        "y": 260,
        "wires": []
    },
    {
        "id": "c6d0552b029a5198",
        "type": "function",
        "z": "138c6cb20fe4c960",
        "name": "set filename F",
        "func": "\nif (msg.payload == true) {\n    if (msg.filesArray) {\n        msg.newPath = \"/home/nodered/audio/\"\n            + msg.passThroughMessage.audiofile.lang.substring(0, 2) + \"/\";\n        msg.newFilename = \"F\"\n            + msg.passThroughMessage.audiofile.filename;\n\n        // index at which to split path from filename\n        var index = msg.filesArray[0].file.lastIndexOf(\"/\");\n        msg.oldPath = msg.filesArray[0].file.substring(0, index);\n        msg.oldFilename = msg.filesArray[0].file.substring(index);\n\n        return msg;\n    }\n}\nreturn null;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 880,
        "y": 300,
        "wires": [
            [
                "874cc8d65503d673",
                "cb0a6189c31e0c5d"
            ]
        ]
    },
    {
        "id": "e940186b090d26ef",
        "type": "debug",
        "z": "138c6cb20fe4c960",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 850,
        "y": 260,
        "wires": []
    },
    {
        "id": "e57d1246063cd5e4",
        "type": "q-gate",
        "z": "138c6cb20fe4c960",
        "name": "",
        "controlTopic": "control",
        "defaultState": "queueing",
        "openCmd": "open",
        "closeCmd": "close",
        "toggleCmd": "toggle",
        "queueCmd": "queue",
        "defaultCmd": "default",
        "triggerCmd": "trigger",
        "flushCmd": "flush",
        "resetCmd": "reset",
        "peekCmd": "peek",
        "dropCmd": "drop",
        "statusCmd": "status",
        "maxQueueLength": "5000",
        "keepNewest": true,
        "qToggle": false,
        "persist": false,
        "storeName": "memory",
        "x": 370,
        "y": 320,
        "wires": [
            [
                "5902491c2404e004",
                "6fa82f70baba087d"
            ]
        ]
    },
    {
        "id": "61db4838fca47f1e",
        "type": "ttsultimate",
        "z": "138c6cb20fe4c960",
        "name": "DK-F",
        "voice": "da-DK-Wavenet-A#da-DK#FEMALE",
        "ssml": true,
        "sonosipaddress": "192.168.1.109",
        "sonosvolume": "30",
        "sonoshailing": "0",
        "config": "557d8082.eb5a8",
        "property": "payload",
        "propertyType": {},
        "rules": [],
        "playertype": "noplayer",
        "speakingrate": "1",
        "speakingpitch": "0",
        "unmuteIfMuted": false,
        "elevenlabsStability": "",
        "elevenlabsSimilarity_boost": "",
        "x": 710,
        "y": 320,
        "wires": [
            [
                "c6d0552b029a5198",
                "e940186b090d26ef",
                "d3176ccbf95e3c29"
            ],
            []
        ]
    },
    {
        "id": "3fb92776ceaffb4f",
        "type": "debug",
        "z": "138c6cb20fe4c960",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 370,
        "y": 260,
        "wires": []
    },
    {
        "id": "6fa82f70baba087d",
        "type": "change",
        "z": "138c6cb20fe4c960",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "audiofile",
                "pt": "msg",
                "to": "payload",
                "tot": "msg",
                "dc": true
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "audiofile.text",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 540,
        "y": 320,
        "wires": [
            [
                "61db4838fca47f1e",
                "619ffd80c61f2026"
            ]
        ]
    },
    {
        "id": "619ffd80c61f2026",
        "type": "debug",
        "z": "138c6cb20fe4c960",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 690,
        "y": 260,
        "wires": []
    },
    {
        "id": "cb0a6189c31e0c5d",
        "type": "fs-ops-copy",
        "z": "138c6cb20fe4c960",
        "name": "",
        "sourcePath": "oldPath",
        "sourcePathType": "msg",
        "sourceFilename": "oldFilename",
        "sourceFilenameType": "msg",
        "destPath": "newPath",
        "destPathType": "msg",
        "destFilename": "newFilename",
        "destFilenameType": "msg",
        "link": false,
        "overwrite": true,
        "x": 1060,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "2d054e419602e760",
        "type": "inject",
        "z": "138c6cb20fe4c960",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[{\"AudioID\":1943,\"FileID\":\"001\",\"lang\":\"DK\",\"filename\":\"DK001.mp3\",\"text\":\"Kogeplade foran til venstre. Sluk. \"},{\"AudioID\":1944,\"FileID\":\"002\",\"lang\":\"DK\",\"filename\":\"DK002.mp3\",\"text\":\"Foran til venstre, 5.\"},{\"AudioID\":1945,\"FileID\":\"003\",\"lang\":\"DK\",\"filename\":\"DK003.mp3\",\"text\":\"Foran til venstre, 6.\"}]",
        "payloadType": "json",
        "x": 90,
        "y": 320,
        "wires": [
            [
                "3ada517276e03317",
                "cd46f15c4aeedc91"
            ]
        ]
    },
    {
        "id": "cd46f15c4aeedc91",
        "type": "delay",
        "z": "138c6cb20fe4c960",
        "name": "initial trigger delay",
        "pauseType": "delay",
        "timeout": "2",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "outputs": 1,
        "x": 250,
        "y": 460,
        "wires": [
            [
                "d3176ccbf95e3c29"
            ]
        ]
    },
    {
        "id": "d3176ccbf95e3c29",
        "type": "change",
        "z": "138c6cb20fe4c960",
        "name": "trigger",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "trigger",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "control",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 430,
        "y": 460,
        "wires": [
            [
                "e57d1246063cd5e4"
            ]
        ]
    },
    {
        "id": "557d8082.eb5a8",
        "type": "ttsultimate-config",
        "name": "google cloud TTS",
        "noderedipaddress": "127.0.0.1",
        "noderedport": "1980",
        "purgediratrestart": "leave",
        "ttsservice": "googletts",
        "TTSRootFolderPath": ""
    }
]
Supergiovane commented 1 month ago

Hi the only way, is to check the filename, eventually along with the fileID property.

Cursor_e_Node-RED___Flow_1

What do you want to achieve? If you can tell me what would you like to do, i can hack your flow...

bs-eng commented 1 month ago

I would like to send multiple texts to TTS and name the resulting files. Then they need to be stored at a different place for use by another system. The system is handling the files by name. The TTS data is currently in "text", the filename ind "filename". The main ID, that would allow an external lookup is the AudioID, but that would require another db-access, that I would like to avoid.

Last night I noticed, that TTS-ultimate can in-/output arrays, but I cannot find a way how to match the TTS-ultimate name to the original filename.

Supergiovane commented 1 month ago

Hi

Please paste this code and let me know if you can solve with that:

Node-RED___Flow_1

[{"id":"61db4838fca47f1e","type":"ttsultimate","z":"1df6eef18a304d30","name":"DK-F","voice":"da-DK-Wavenet-A#da-DK#FEMALE","ssml":true,"sonosipaddress":"192.168.1.109","sonosvolume":"30","sonoshailing":"0","config":"557d8082.eb5a8","property":"payload","propertyType":{},"rules":[],"playertype":"noplayer","speakingrate":"1","speakingpitch":"0","unmuteIfMuted":false,"elevenlabsStability":"","elevenlabsSimilarity_boost":"","x":290,"y":180,"wires":[["4cb1c43a42cf3d3a"],[]]},{"id":"2d054e419602e760","type":"inject","z":"1df6eef18a304d30","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"AudioID\":1943,\"FileID\":\"001\",\"lang\":\"DK\",\"filename\":\"DK001.mp3\",\"text\":\"Kogeplade foran til venstre. Sluk. \"},{\"AudioID\":1944,\"FileID\":\"002\",\"lang\":\"DK\",\"filename\":\"DK002.mp3\",\"text\":\"Foran til venstre, 5.\"},{\"AudioID\":1945,\"FileID\":\"003\",\"lang\":\"DK\",\"filename\":\"DK003.mp3\",\"text\":\"Foran til venstre, 6.\"}]","payloadType":"json","x":130,"y":120,"wires":[["cef758cb1aea1c39"]]},{"id":"cef758cb1aea1c39","type":"function","z":"1df6eef18a304d30","name":"Save array into the global context and send the payloads.","func":"let msgJSON = msg.payload;\nlet globalInputMsgArray = [];\nfor (let index = 0; index < msgJSON.length; index++) {\n const element = msgJSON[index];\n globalInputMsgArray.push({input:element});\n}\nglobal.set(\"globalInputMsgArray\", globalInputMsgArray); // Store the array in the global context memory\nfor (let index = 0; index < globalInputMsgArray.length; index++) {\n const element = globalInputMsgArray[index];\n node.send({ payload: element.input.text }) \n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":120,"wires":[["61db4838fca47f1e"]]},{"id":"4cb1c43a42cf3d3a","type":"function","z":"1df6eef18a304d30","name":"Merge global variable and the TTS filename","func":"if (msg.payload === true) {\n let globalInputMsgArray = global.get(\"globalInputMsgArray\"); // Get the array in the global context memory\n for (let index = 0; index < globalInputMsgArray.length; index++) {\n let element = globalInputMsgArray[index];\n node.send({original:element,TTSFilename:msg.filesArray[index].file})\n }\n}\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":240,"wires":[["e8358c1938432123"]]},{"id":"e8358c1938432123","type":"debug","z":"1df6eef18a304d30","name":"debug 3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":680,"y":240,"wires":[]},{"id":"557d8082.eb5a8","type":"ttsultimate-config","name":"google cloud TTS","noderedipaddress":"127.0.0.1","noderedport":"1980","purgediratrestart":"purge","ttsservice":"googletts","TTSRootFolderPath":""}]

bs-eng commented 1 month ago

Hi! It seems to work.

So are you basically telling me that, if I input an array to the TTS-node:

  1. the node returns the complete result array in one array?
  2. the returned array has the exact same order of items as the original array?
  3. does this hold true for any amount of items AND significantly different lengths of texts? (for example if one audio takes 5s to be generated and all others take only a fraction of one second, there will be no side effects, like order in array?)
  4. if another instance of the TTS-node is triggered menawhile, do those points 1.-3. still hold true?

If I can rely on those points, then it would be quite easy using your example! Cheers JR

Supergiovane commented 1 month ago

Hello

  1. Yes, TTS Ultimate works in serial mode.
  2. Yes, see 2. The array is ordered in FiFo mode (first in, first out)
  3. You should try. Each TTS node coordinates with other TTS nodes and if a TTS node is running, the second TTS node will wait to the first to finish, preserving the input array order.
bs-eng commented 1 month ago

Thank you so much! That helps a lot and even makes my flow a lot more simple!

Just to make sure: point 1. is also always a given? one array in => one array out? Or do I have to be ready to handle multiple arrays out, in case the tts node flushes out results once in a while?

Supergiovane commented 1 month ago

Hi You should try, but it should work.

bs-eng commented 1 month ago

Just an update for those interested: I ran a test with 8000 objects and it went just fine. All serial. Still to test point 4.