# Get duration of audio

**URL:** https://discourse.nodered.org/t/get-duration-of-audio/30870
**Category:** General
**Created:** [29 July 2020 12:25 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870 "2020-07-29T12:25:55Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![DerT94](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@DerT94](https://discourse.nodered.org/u/DerT94)
#### Post date: [29 July 2020 12:25 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/1 "2020-07-29T12:25:56Z")

</div>

Hi,

I have realized a connection of the Amazon Polly Service via Node-Red.  
From polly I get an audio buffer back, I enrich it with wav headers to output it as audio.  
I would like to be able to determine the duration from the buffer.

For example:  
Polly transmits the buffer for the sentence "This is a test"  
I want to pause the music while the sentence is spoken.  
But for this I need to know how long the sentence is spoken.

---

<div class="post-metadata">

### Author: ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)
#### Post date: [29 July 2020 12:47 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/2 "2020-07-29T12:47:30Z")

</div>

you don't say how you are playing it - but if you call out to something like aplay then the call will return once it has finished so that would let you trigger an unpause.

---

<div class="post-metadata">

### Author: ![DerT94](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@DerT94](https://discourse.nodered.org/u/DerT94)
#### Post date: [29 July 2020 14:06 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/3 "2020-07-29T14:06:39Z")

</div>

I do not play the audio directly with node-red.  
I have a Raspberry Pi with [rhasspy](https://github.com/synesthesiam/rhasspy). Via an API I transfer the WAV file from Node-Red to rhasspy and rhasspy does the rest.

---

<div class="post-metadata">

### Author: ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)
#### Post date: [29 July 2020 14:08 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/4 "2020-07-29T14:08:43Z")

</div>

Maybe a search on StackOverflow may be helpful as this isn't specific to Node-RED - [https://stackoverflow.com/questions/6474765/how-can-i-determine-the-length-of-a-local-wav-file-in-javascript](https://stackoverflow.com/questions/6474765/how-can-i-determine-the-length-of-a-local-wav-file-in-javascript)

---

<div class="post-metadata">

### Author: ![DerT94](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@DerT94](https://discourse.nodered.org/u/DerT94)
#### Post date: [7 August 2020 13:22 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/5 "2020-08-07T13:22:54Z")

</div>

Thank you but how can I use "loadModule" in Node-Red?

I have found an NPM module that does exactly what I want.

In my nodejs test environment it also works with this code

```auto
const { getAudioDurationInSeconds } = require('get-audio-duration');

// From a local path...
getAudioDurationInSeconds('D:/Users/TA/_data/wakeword.wav').then((duration) => {
    //console.log(duration);
    console.log (duration)
});

```

But I don't know how to pass on the promise to the next node

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [7 August 2020 14:11 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/6 "2020-08-07T14:11:46Z")

</div>

The node red docs page on [writing functions](https://nodered.org/docs/user-guide/writing-functions) has a section Loading Additional Modules which tells you how to do it.

---

<div class="post-metadata">

### Author: ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)
#### Post date: [7 August 2020 14:18 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/7 "2020-08-07T14:18:52Z")

</div>

Hi @DerT94,  
Not exactly an answer to your last question, but 'perhaps' there is an easier way to do this:

1. Get the WAV header with my [node-red-contrib-wav](https://flows.nodered.org/node/node-red-contrib-wav) node.

2. Calculate the duration in a function node, based on the information in that WAV header (similar like the [wav-audio-length](https://github.com/taku-o/wav-audio-length/blob/39286d32e29d03213a1ca5037ec669047021a84d/duration.js#L24) npm module does it). Something like this:

P.S. I have not been able to test whether this code is syntax free, or whether the calculated duration is correct!

_ **EDIT** _: if you get this working, I will add it as an extra output to my node...

Bart

---

<div class="post-metadata">

### Author: ![BartButenaers](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bartbutenaers/32/10476_2.png) [@BartButenaers](https://discourse.nodered.org/u/BartButenaers)
#### Post date: [13 August 2020 07:02 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/8 "2020-08-13T07:02:05Z")

</div>

Hello @DerT94,

You have not given any feedback to our suggestions, which makes it hard for us and other users to learn from this discussion ...

Anyway I have published a new _ **1.0.1** _ version of the node-red-contrib-wav node on NPM:

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/5/2/527f433f98a432ac700a00215fdb2625389d8bfd.png)

This version _ **calculates the duration** _ of the WAV chunk based on the WAV headers.  
Like in this example flow:

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/4/5/451d330249fc8c6e92d42e8fbe835d03a369dc40.png)

```auto
[{"id":"e63858e1.7a7698","type":"wav-headers","z":"4142483e.06fca8","name":"","action":"get","channels":"1","samplerate":"22050","bitwidth":"16","x":670,"y":580,"wires":[[],["9420840.1c8fb8"]]},{"id":"3c4e2aed.d61106","type":"http request","z":"4142483e.06fca8","name":"Download WAV file","method":"GET","ret":"bin","paytoqs":false,"url":"https://file-examples-com.github.io/uploads/2017/11/file_example_WAV_10MG.wav","tls":"","persist":false,"proxy":"","authType":"basic","x":450,"y":580,"wires":[["e63858e1.7a7698"]]},{"id":"94486c9c.e89ed","type":"inject","z":"4142483e.06fca8","name":"Start test","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":260,"y":580,"wires":[["3c4e2aed.d61106"]]},{"id":"9420840.1c8fb8","type":"debug","z":"4142483e.06fca8","name":"Headers","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":860,"y":580,"wires":[]}]

```

Which downloads a WAV file from the web, gets the WAV headers and calculates the WAV file duration (in seconds, rounded at 2 decimals):

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/e/7/e779bfee08cd8ebbd17407f4616747d98ee0ef5f.png)

My first calculation - which I had proposed in the above discussion - did _ **not** _ work!  
With this new calculation, I seem to get similar durations as when I open the same WAV file e.g. with the VLC media player:

![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/2/d/2d4b8273e07be57223a309028dcef6025c022874.png)

Would be nice to know if this calculation is correct, and whether it can solve your issue.  
Bart

---

<div class="post-metadata">

### Author: ![DerT94](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@DerT94](https://discourse.nodered.org/u/DerT94)
#### Post date: [13 August 2020 10:42 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/9 "2020-08-13T10:42:37Z")

</div>

Sorry  
I have not answered yet as I have not been able to test.  
But today I got around to it.

Thanks for the quick implementation.

I can confirm that the calculated time is identical to the display in VLC Meida Player.  
But there is a problem with my implementation.  
In the initial message that starts the flow there is an ID that I need together with the duration.  
There is certainly a way to solve this with global variables.  
But I'm not sure how to do that

Here is an example flow for my implementation

```auto
[
    {
        "id": "48ce9130.1e2f8",
        "type": "function",
        "z": "923f92f6.5d9f2",
        "name": "SetHeadersAndText",
        "func": "const val = msg.payload.val\nlet newMsg = {};\nnewMsg.filename = `/usr/local/bin/iobroker/rhasspy/wav/${val.filename}`\nnewMsg.headers = {};\nnewMsg.headers['User-Agent'] = 'ioBroker';\nnewMsg.headers['Content-Type'] = 'audio/wav';\nnewMsg.url = `http://${val.host}:${val.port}/api/play-wav`\nreturn newMsg",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 400,
        "y": 320,
        "wires": [
            [
                "55db20e5.403e1"
            ]
        ]
    },
    {
        "id": "55db20e5.403e1",
        "type": "file in",
        "z": "923f92f6.5d9f2",
        "name": "GetFile",
        "filename": "",
        "format": "",
        "chunk": false,
        "sendError": false,
        "encoding": "none",
        "x": 640,
        "y": 320,
        "wires": [
            [
                "f80f3dca.3cf5f"
            ]
        ]
    },
    {
        "id": "f80f3dca.3cf5f",
        "type": "wav-headers",
        "z": "923f92f6.5d9f2",
        "name": "",
        "action": "get",
        "channels": 1,
        "samplerate": 22050,
        "bitwidth": 16,
        "x": 890,
        "y": 320,
        "wires": [
            [
                "b0763f2d.85a44"
            ],
            [
                "dee21ce6.8dcc1"
            ]
        ]
    },
    {
        "id": "dee21ce6.8dcc1",
        "type": "debug",
        "z": "923f92f6.5d9f2",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1190,
        "y": 420,
        "wires": []
    },
    {
        "id": "b0763f2d.85a44",
        "type": "http request",
        "z": "923f92f6.5d9f2",
        "name": "-> rhasspy",
        "method": "POST",
        "ret": "bin",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 1170,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "4d1fa247.ca4e2c",
        "type": "inject",
        "z": "923f92f6.5d9f2",
        "name": "",
        "props": [
            {
                "p": "payload.val",
                "v": "{\"filename\":\"beep_error.wav\",\"host\":\"testpi.example.loc\",\"port\":\"12101\",\"siteid\":\"testpi\"}",
                "vt": "json"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 200,
        "y": 320,
        "wires": [
            [
                "48ce9130.1e2f8"
            ]
        ]
    },
    {
        "id": "4ce99781.a51998",
        "type": "comment",
        "z": "923f92f6.5d9f2",
        "name": "comment",
        "info": "Here I need the rhasspySiteId from the initial message",
        "x": 1200,
        "y": 460,
        "wires": []
    }
]

```

---

<div class="post-metadata">

### Author: ![DerT94](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@DerT94](https://discourse.nodered.org/u/DerT94)
#### Post date: [13 August 2020 11:47 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/10 "2020-08-13T11:47:36Z")

</div>

Got it 🙂

```auto
[{"id":"48ce9130.1e2f8","type":"function","z":"923f92f6.5d9f2","name":"SetHeadersAndText","func":"const val = msg.payload.val\n\nflow.set(\"rhasspySiteId\", val.rhasspySiteId)\n\nlet newMsg = {};\nnewMsg.filename = `/usr/local/bin/iobroker/rhasspy/wav/${val.filename}`\nnewMsg.headers = {};\nnewMsg.headers['User-Agent'] = 'ioBroker';\nnewMsg.headers['Content-Type'] = 'audio/wav';\nnewMsg.url = `http://${val.host}:${val.port}/api/play-wav`\nreturn newMsg","outputs":1,"noerr":0,"initialize":"","finalize":"","x":400,"y":320,"wires":[["55db20e5.403e1"]]},{"id":"55db20e5.403e1","type":"file in","z":"923f92f6.5d9f2","name":"GetFile","filename":"","format":"","chunk":false,"sendError":false,"encoding":"none","x":640,"y":320,"wires":[["f80f3dca.3cf5f"]]},{"id":"f80f3dca.3cf5f","type":"wav-headers","z":"923f92f6.5d9f2","name":"","action":"get","channels":1,"samplerate":22050,"bitwidth":16,"x":890,"y":320,"wires":[["92701f9.d1139e"],["b0ecf8e0.5cb4b8"]]},{"id":"b0763f2d.85a44","type":"http request","z":"923f92f6.5d9f2","name":"-> rhasspy","method":"POST","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":1350,"y":320,"wires":[[]]},{"id":"92701f9.d1139e","type":"delay","z":"923f92f6.5d9f2","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1120,"y":320,"wires":[["b0763f2d.85a44"]]},{"id":"ae3e18ad.93ee98","type":"function","z":"923f92f6.5d9f2","name":"","func":"const rhasspySiteId = flow.get(\"rhasspySiteId\")\n\nlet newMsg = {};\nnewMsg.payload = { \n \"id\": msg.uuid,\n \"timeout\": msg.payload.duration+2,\n \"rhasspySiteId\": rhasspySiteId\n}\nnewMsg.topic = `0_userdata.0.Rhasspy.ScriptStates.${rhasspySiteId}.MopidyPaused2`\nreturn newMsg","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1260,"y":400,"wires":[["5195dc22.52ab64"]]},{"id":"b0ecf8e0.5cb4b8","type":"uuid","z":"923f92f6.5d9f2","uuidVersion":"v4","namespaceType":"","namespace":"","namespaceCustom":"","name":"","field":"uuid","fieldType":"msg","x":1090,"y":400,"wires":[["ae3e18ad.93ee98"]]},{"id":"4d1fa247.ca4e2c","type":"inject","z":"923f92f6.5d9f2","name":"","props":[{"p":"payload.val","v":"{\"filename\":\"beep_error.wav\",\"host\":\"testpi.example.loc\",\"port\":\"12101\",\"rhasspySiteId\":\"testpi\"}","vt":"json"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":200,"y":320,"wires":[["48ce9130.1e2f8"]]},{"id":"715a700d.9b08b","type":"comment","z":"923f92f6.5d9f2","name":"","info":"play wav with 1sec delay\n\nThe delay is intended so that the music can be paused before","x":1350,"y":280,"wires":[]},{"id":"6a30d5b7.a0167c","type":"comment","z":"923f92f6.5d9f2","name":"","info":"use ioBroker to pause mopidy and play after the duration +2 seconds","x":1450,"y":440,"wires":[]},{"id":"5195dc22.52ab64","type":"debug","z":"923f92f6.5d9f2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1430,"y":400,"wires":[]}]

```

**Required modules**

- node-red-contrib-wav
- node-red-contrib-uuid

Also, my node-red runs inside an ioBroker.  
The rest of the logic for pausing and the actual trigger comes from ioBroker.  
But this logic will be of no interest to most people in this forum.  
In case someone needs it. Just report it here 🙂

Thanks for your help

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)
#### Post date: [12 October 2020 11:47 UTC](https://discourse.nodered.org/t/get-duration-of-audio/30870/11 "2020-10-12T11:47:39Z")

</div>

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.
