Playing a Sound with ASObjC

Continuing the discussion from How Do I Select "Favorites" on Emoji & Symbols Window?:

Thanks to Jonas (@ionah) for his script:

I’ve done some research and refactored that into a handler:

use framework "Foundation"

my playSound("Purr")

on playSound(pSoundNameStr)
  
  ## REQUIRES
  #    use framework "Foundation"
  
  (* 
  Searches for sound file in these locations, in this order:
  application's bundle, ~/Library/Sounds, /Library/Sounds, /Network/Library/Sounds, /System/Library/Sounds
  
   Choices from /System/Library/Sounds Folder:
     Basso, Blow, Bottle, Frog, Funk, Glass, Hero, 
     Morse, Ping, Pop, Purr, Sosumi, Submarine, Tink
  *)
  
  local oSound, curVol, oNSSound
  
  set oSound to (current application's NSSound's soundNamed:pSoundNameStr)
  
  ### How do I get CURRENT Sound Volume, so I can restore? ###
  -- I tried oSound's getVolume, but that did NOT work
  
  oSound's setVolume:(1.0)
  oSound's play()
  
end playSound

Any ideas on how to get the current sound volume?

Actually I’m finding this has NO effect on volume.
If I have manually muted sound or set at a very low level, it does NOT change when the script runs.

What am I doing wrong? Or are we unable to override the user’s sound level?

From the docs for the volume property:

The value of this property does not affect the systemwide volume.

There’s another, more complicated, way to play sounds:

use framework "Foundation"
use framework "AVFoundation"
use scripting additions

set soundFolderURL to current application's |NSURL|'s fileURLWithPath:"/System/Library/Sounds"
set {thePlayer, theError} to current application's AVAudioPlayer's alloc()'s initWithContentsOfURL:(soundFolderURL's URLByAppendingPathComponent:"Purr.aiff") |error|:(reference)
thePlayer's play()

The advantage is that it gives you some extra control. For example:

use framework "Foundation"
use framework "AVFoundation"
use scripting additions

set soundFolderURL to current application's |NSURL|'s fileURLWithPath:"/System/Library/Sounds"
set {thePlayer, theError} to current application's AVAudioPlayer's alloc()'s initWithContentsOfURL:(soundFolderURL's URLByAppendingPathComponent:"Basso.aiff") |error|:(reference)
thePlayer's setNumberOfLoops:2
thePlayer's setEnableRate:true
thePlayer's setRate:0.5
thePlayer's play()

It even lets you coordinate sounds:

use framework "Foundation"
use framework "AVFoundation"
use scripting additions

set soundFolderURL to current application's |NSURL|'s fileURLWithPath:"/System/Library/Sounds"
set {thePlayer1, theError} to current application's AVAudioPlayer's alloc()'s initWithContentsOfURL:(soundFolderURL's URLByAppendingPathComponent:"Frog.aiff") |error|:(reference)
thePlayer1's prepareToPlay()
thePlayer1's setEnableRate:true
thePlayer1's setRate:1.0
set {thePlayer2, theError} to current application's AVAudioPlayer's alloc()'s initWithContentsOfURL:(soundFolderURL's URLByAppendingPathComponent:"Glass.aiff") |error|:(reference)
thePlayer2's prepareToPlay()
thePlayer2's setEnableRate:true
thePlayer2's setVolume:0.25
thePlayer2's setRate:0.5
set {thePlayer3, theError} to current application's AVAudioPlayer's alloc()'s initWithContentsOfURL:(soundFolderURL's URLByAppendingPathComponent:"Submarine.aiff") |error|:(reference)
thePlayer3's prepareToPlay()
thePlayer1's playAtTime:(0.1 + (thePlayer1's deviceCurrentTime()))
thePlayer2's playAtTime:(0.1 + (thePlayer2's deviceCurrentTime()))
thePlayer3's playAtTime:(0.1 + (thePlayer3's deviceCurrentTime()))

Thanks, Shane. That actually answers a question I was about to ask:
How can I play this sound:
/System/Library/PrivateFrameworks/ToneLibrary.framework/Versions/A/Resources/AlertTones/sms-received1.caf

There are some cool sounds in this folder.

Yep.

use framework "Foundation"
use framework "AVFoundation"

set theURL to current application's |NSURL|'s fileURLWithPath:"/System/Library/PrivateFrameworks/ToneLibrary.framework/Resources/Ringtones/Motorcycle.m4r"
set {thePlayer, theError} to current application's AVAudioPlayer's alloc()'s initWithContentsOfURL:theURL |error|:(reference)
thePlayer's play()
1 Like

is this the more reliable method?

using

(current application's NSSound's soundNamed:"Basso")'s play()

seems to fail often?

I’m surprised to hear that using NSSound fails. Why not give the alternative a try?

Very strange but AVAudioPlayer is much more reliable. Atleast in the code I’m working on.