# Share Audio Clips Between Gateway and Clients

**URL:** <https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286>\
**Category:** Ignition\
**Tags:** ignition81, vision, scripting\
**Created:** [August 28, 2024, 5:14pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286 "2024-08-28T17:14:45Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 28, 2024, 5:14pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/1 "2024-08-28T17:14:45Z")

</div>

Didn't want to revive an old [thread](https://forum.inductiveautomation.com/t/play-an-mp3-file-yes-yes-you-can/15219/9), but have the same question now.

The end goal is to repeatedly play this sound based on an expression tag, but for now I want to test it with a button. I've got the script:

```python
from javafx.scene.media import AudioClip

def demoAlarmSound(audioPath):
	notify = AudioClip(audioPath)
	notify.play()

```

But I get the error that `javafx` is unknown.

How can I play mp3 in Ignition 8.1 ?

edit: this thread got pretty long on the first step of the process, so I've renamed it and will create other threads as needed.

---

<div class="post-metadata">

**Author:** ![MMaynard](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mmaynard/32/3366_2.png) [@MMaynard](https://forum.inductiveautomation.com/u/MMaynard)\
**Post date:** [August 28, 2024, 5:40pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/2 "2024-08-28T17:40:07Z")

</div>

We had to switch over to utilizing the system.util.playSoundClip function and only use WAV files.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 28, 2024, 5:41pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/3 "2024-08-28T17:41:07Z")

</div>

So that's the only option?

---

<div class="post-metadata">

**Author:** ![MMaynard](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mmaynard/32/3366_2.png) [@MMaynard](https://forum.inductiveautomation.com/u/MMaynard)\
**Post date:** [August 28, 2024, 5:44pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/4 "2024-08-28T17:44:06Z")

</div>

No idea honestly, 99.9% of our alarm files were already WAV.  
We just converted any that weren't.

Someone else might have another idea for how to play mp3 files.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 28, 2024, 7:16pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/5 "2024-08-28T19:16:20Z")

</div>

Alright, storage space is not really a big concern so I'll just use WAV files to make my life easier.

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [August 28, 2024, 7:28pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/6 "2024-08-28T19:28:08Z")

</div>

I moved to using VLC for our andons.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 28, 2024, 7:29pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/7 "2024-08-28T19:29:43Z")

</div>

JavaFX isn't bundled with the Java runtime anymore, so yeah, the `javafx` imports won't be an option on 8.x versions of Ignition.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 28, 2024, 8:01pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/8 "2024-08-28T20:01:31Z")

</div>

Where does the wav file have to be? I have it on the gateway and get an unable to open file error when I try to play it.

```python
Unable to open file/url: C:\SCADA\Alarm Sounds\Alarm.wav

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 28, 2024, 8:37pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/9 "2024-08-28T20:37:18Z")

</div>

Are you trying to play it in Vision? Just use the sound player component, it'll embed the audio clip.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 28, 2024, 8:40pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/10 "2024-08-28T20:40:40Z")

</div>

There are 4 options for sounds and I'm trying to let the user test the different sounds with a button. I was going to create the correct path based on the selected option.

~~I'll also need to use the system function to repeatedly play the sound based on a tag, once I make it that far.~~

Nevermind, I'll just do it on the docked view that's always open. Quick question, what might happen if there are multiple instances of the same docked view with this sound component? I'd imagine I'll get duplicate sounds and it won't be pretty. How can I avoid that?

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [August 28, 2024, 9:06pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/11 "2024-08-28T21:06:26Z")

</div>

I would have to go back and look, but I believe I only trigger mine on "Desktop-0". The other thing you'll have an issue with most likely is if a user logs in and the sound is playing, it will trigger a second time and play over the top of itself. I had a fix for it too, but need to look up what I did again.

Edit: Just looked it up, and on the docked window, I have a custom property (could be anywehre really, that has a DesktopIndex custom property that gets set to the index of the desktop (0 being primary). Then on the Sound Player component, I created a custom property named `playSound` that is bound to the tag I want to trigger the sound to play on, then have a propertyChange script on the component with this text:

```python
# Special script to only allow the sound player to play the sound once
# Issue with logging on/off causing it to trigger multiple times
# Also only allows the primary display to play the sound
if event.propertyName == 'playSound' and event.source.parent.DesktopIndex == 0:
	if event.source.trigger != event.newValue and event.newValue != None:
		event.source.trigger = event.newValue
elif event.propertyName == 'playSound':
	event.source.trigger = False

```

As a bonus, I actually have a script I call that lets each operator select from any number of wav files on the server, and when they select a different one, it pulls the wav file raw bytes from the server (my soundData property is bound to a `runScript` . I let technicians/admins upload sounds, and operators can then pick from the list of sounds they want for their alarms.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 28, 2024, 9:41pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/12 "2024-08-28T21:41:59Z")

</div>

Well, in that case I might actually want to do it with the system function.

So, back to that question.

Can I play a WAV file at the client-level using a client event script to continuously play the WAV file that is hosted on the gateway?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 28, 2024, 9:53pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/13 "2024-08-28T21:53:50Z")

</div>

Not without downloading it to the client in some way.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 28, 2024, 9:54pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/14 "2024-08-28T21:54:42Z")

</div>

That's doable. Can I script the "download" (transfer) to the client if the file doesn't exist?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [August 28, 2024, 9:55pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/15 "2024-08-28T21:55:06Z")

</div>

> [@amarks](#):
>
> Can I play a WAV file at the client-level using a client event script to continuously play the WAV file that is hosted on the gateway?

No, not in either Vision or Perspective. (It sounds like you are using Perspective, but your earlier comments implied Vision... 🤷‍♂️ )

In Vision, the file needs to be present in the client filesystem. Vision can easily use `system.util.sendRequest()` to retrieve such from the gateway and write it locally. Perhaps cached.

In Perspective, the sound player needs a URL to the sound file. That URL **cannot** point at a local file. That means the WebDev module (if you must use a file), or perhaps my Blob Server module (if you can put the sound in a database as a blob.)

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 29, 2024, 1:44pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/16 "2024-08-29T13:44:52Z")

</div>

There are 4 files. Will I need to send a request for each one? What sort of data will be returned from the WAV files and how can I write it after receiving it?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [August 29, 2024, 2:06pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/17 "2024-08-29T14:06:51Z")

</div>

Yes. As long as the alarm files are not very large (tens of megabytes), you can use `system.util.globals` as a JVM local cache of the audio bytes. You could asynchronously fetch and build that local cache on client startup. Or you could cache to a local file, but that does get more complex.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 29, 2024, 2:38pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/18 "2024-08-29T14:38:22Z")

</div>

I'm not opposed to either solution, but I'm a bit lost on how to start in either case. I've not used the JVM local cache or `system.util.globals` before.

My original idea was to go the local file route. Right now I have a memory (gateway) tag that holds the name of the selected alarm sound. The idea was to truncate this at the end of the "sound path" (C:/SCADA/Alarm Sounds/) and play it that way.

I was going to try and make it as "automatic" as possible. Just add a file to the folder and it becomes available as an option for the alarm sound. Getting the file names into a dataset for use with a dropdown box using `os.listDir()`.

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [August 29, 2024, 3:06pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/19 "2024-08-29T15:06:36Z")

</div>

That's very similar to what I'm doing, but not using `system.util.globals` (maybe I should be?)

I have a tag named `BasePath` that stores the base file path on the server `C:\SCADA` in your instance, then I have a few gateway message handlers in my gateway scripting project as follows:

**GetAlarmSoundList**

```python
def handleMessage(payload):
	import os
	import errno
	
	folderPath = system.tag.readBlocking('BasePath')[0].value + '\AlarmSounds'
	try:
		os.makedirs(folderPath)
	except OSError as e:
		if errno.EEXIST != e.errno:
			raise
			
	fileList = []
	files = os.listdir(folderPath)
	for fileName in files:
		if fileName.upper().endswith('.WAV'):
			fileList.append(fileName)
			
	return fileList

```

**GetAlarmSound**

```python
def handleMessage(payload):
	if payload['fileName']:
		filePath = system.tag.readBlocking('BasePath')[0].value + '\AlarmSounds\%s' % payload['fileName']
		if system.file.fileExists(filePath):
			bytes = system.file.readFileAsBytes(filePath)
			return bytes

```

On my project, I have a library:  
**Vision.Sound**

```python
def getSoundBytes(fileName):
	bytes = system.util.sendRequest('GatewayScripts', 'GetAlarmSound', {'fileName': fileName})
	return bytes
	
def getAvailableAlarmSounds():
	fileList = system.util.sendRequest('GatewayScripts', 'GetAlarmSoundList')
	fileDS = []
	for fileName in fileList:
		fileDS.append([fileName, fileName])
	
	return system.dataset.toDataSet(['Value', 'Label'], fileDS)

```

Then the binding on my `Sound Player` component's `soundData` property is:

```python
runScript('Vision.Sound.getSoundBytes',0,{[client]AlarmSound})

```

For sound selection, I have a client settings popup that has a dropdown with the `data` property set to this binding:

```python
runScript('Vision.Sound.getAvailableAlarmSounds', 0)

```

And the `selectedStringValue` property bound to my `[client]AlarmSound` tag.

I guess I maybe assumed that the `Sound Player` component essentially caches the sound bytes whenever the component is loaded or the sound file is changed. You could use a non-client tag also if you wanted it to change globally for everyone.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [August 29, 2024, 3:14pm UTC](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286/20 "2024-08-29T15:14:31Z")

</div>

Thanks for the direction! I had actually just finished changing over to client tags instead of gateway tags, since the files have to be stored on the client anyway, the individual clients may as well be able to select individual sounds.

[Next page](https://forum.inductiveautomation.com/t/share-audio-clips-between-gateway-and-clients/95286.md?page=2)
