I’m having trouble executing the rec command for SOX via AS, so I’m trying to compile a basic code using AVAudioRecorder, but I’m struggling, not sure what I’m doing wrong, any pointers would be really appreciated.
use framework "Foundation"
use framework "AVFoundation"
use scripting additions
set theString to "file:///Users/dh/Music/Samples/.:tempSample/Untitled.m4a"
set theURL to current application's |NSURL|'s URLWithString:theString
set theData to current application's NSData's dataWithContentsOfURL:theURL
set soundRecorder to current application's AVAudioRecorder's alloc()'s initWithFormat:standard
if soundRecorder is missing value then error (theError's localizedDescription() as text)
tell soundRecorder
prepareToRecord()
record {forDuration, 7} -- or record()
end tell
use framework "Foundation"
use framework "AVFAudio"
use scripting additions
property ca : current application
set theFilename to "~/Desktop/Test.m4a"
set theURL to ca's |NSURL|'s fileURLWithPath:((ca's NSString's stringWithString:theFilename)'s stringByExpandingTildeInPath())
set theFormat to ca's AVAudioFormat's alloc()'s initStandardFormatWithSampleRate:44100 channels:2
set {theRecorder, theError} to ca's AVAudioRecorder's alloc()'s initWithURL:(theURL) format:theFormat |error|:(reference)
if theRecorder = missing value then error (theError's localizedDescription() as text)
theRecorder's |record|()
delay 7
theRecorder's |stop|()
To play use:
use framework "Foundation"
use framework "AVFAudio"
use scripting additions
property ca : current application
set theFilename to "~/Desktop/Test.m4a" -- or: POSIX path of (choose file with prompt "Choose an Audio file:")
set theURL to ca's |NSURL|'s fileURLWithPath:((ca's NSString's stringWithString:theFilename)'s stringByExpandingTildeInPath())
set {thePlayer, theError} to ca's AVAudioPlayer's alloc()'s initWithContentsOfURL:theURL |error|:(reference)
if thePlayer = missing value then error (theError's localizedDescription() as text)
thePlayer's play()
delay 7
thePlayer's |stop|()
So a few things about your code before referencing Apple’s Documentation
I’ve seen you’ve been posting before and have tested this.
set theString to "file:///Users/dh/Music/Samples/.:tempSample/Untitled.m4a"
Are you not having any issues with the “/.:tempSample” part of your path?
Or is creating a hidden folder named “:tempSample”?
set theData to current application's NSData's dataWithContentsOfURL:theURL
Why are you reading data from this URL?
This should be the path that you want to record to correct?
And you don’t use it below?
set soundRecorder to current application's AVAudioRecorder's alloc()'s initWithFormat:standard
You have not defined the variable “standard”.
See my next reply about apple doc’s how to initialize a AVAudioRecorder.
tell soundRecorder
prepareToRecord()
record {forDuration, 7} -- or record()
end tell
tell blocks are aimed for targerting script objects (which may be a scriptable application)
the soundRecorder is a subclass of NSObject and you should be using AsOBJc style (Objective-C)
method/function operations on this instance of AVAudioRecorder you’ve created.
See next post.
Note you may have to playground with the times and durations supplied not
sure how AsOBJc bridge’s those as they are expecting NSTimeIntervals
Declaration
typedef double NSTimeInterval;
Discussion
A NSTimeInterval value is always specified in seconds; it yields sub-millisecond precision over a range of 10,000 years.
Thanks for the replies guys sand to technomorph for taking the time to go into detail. i really appreciate it
Im having issues in script debugger using AVFoundation and AVFAudio in Catalina (as ideally i was looking for something i could write into an ASObjC script, it says the modules are not available. Im not sure where its looking for the framework but i cant find any executables in any of the folders
DB - Do you know what path your script editor is looking for the AVF framework in?
I haven’t checked to see if Xcode is having issues yet, just got back from the studio so gonna have a look now
You are right. AVFAudio cannot be loaded in Catalina - AVFoundation can. But the problem is the file extension. Apparently you can’t record to a compressed file with Applescript and AVAudioRecorder, because you can’t set the format. The AVFormatIDKey is an unsigned integer value and therefore the standard (Linear PCM, 16 bit) is always used.
Under Monterey you can specify “.m4a” as file extension. Under Catalina it must be “.wav”. This script works with Catalina:
use framework "Foundation"
use framework "AVFoundation"
use scripting additions
property ca : current application
set theFilename to "~/Desktop/Test.wav"
set theURL to ca's |NSURL|'s fileURLWithPath:((ca's NSString's stringWithString:theFilename)'s stringByExpandingTildeInPath())
set theFormat to ca's AVAudioFormat's alloc()'s initStandardFormatWithSampleRate:44100 channels:2
set {theRecorder, theError} to ca's AVAudioRecorder's alloc()'s initWithURL:(theURL) format:theFormat |error|:(reference)
if theRecorder = missing value then error (theError's localizedDescription() as text)
theRecorder's |record|()
delay 7
theRecorder's |stop|()
Here is another script that can be used to set the settings. This also works with Catalina, but I did not succeed to set the AVFormatIDKey to the kAudioFormatMPEG4AAC value. This is necessary to use the „m4a“ file extension under Catalina:
use framework "Foundation"
use framework "AVFoundation"
use scripting additions
property ca : current application
property kAudioFormatMPEG4AAC : a reference to ca's kAudioFormatMPEG4AAC
set theFilename to "~/Desktop/Test.wav"
set theURL to ca's |NSURL|'s fileURLWithPath:((ca's NSString's stringWithString:theFilename)'s stringByExpandingTildeInPath())
set theSettings to ca's NSMutableDictionary's dictionary()
theSettings's setObject:44100 forKey:"AVSampleRateKey"
theSettings's setObject:2 forKey:"AVNumberOfChannelsKey"
theSettings's setObject:16 forKey:"AVLinearPCMBitDepthKey"
theSettings's setObject:0 forKey:"AVLinearPCMIsBigEndianKey"
--theSettings's setObject:(kAudioFormatMPEG4AAC) forKey:"AVFormatIDKey" -- not working
--theSettings's setObject:(ca's kAudioFormatMPEG4AAC) forKey:"AVFormatIDKey" -- not working
--theSettings's setObject:192000 forKey:"AVEncoderBitRateKey"
set {theRecorder, theError} to ca's AVAudioRecorder's alloc()'s initWithURL:(theURL) settings:theSettings |error|:(reference)
if theRecorder = missing value then error (theError's localizedDescription() as text)
theRecorder's |record|()
delay 7
theRecorder's |stop|()
Maybe someone else has any idea how to set the format or you can do it with Xcode.
I sorted it (AVFAudio) and got your first script up and running, i looked in the Script Debugger manifest to check where the framework dependencies were set and placed copies of the framework including the executables into the respective folders and it works fine. Im not sure why it was looking in the home/library/framework folder for AVFoundation and AVFAudio, then System folder for Foundation.
I havent tried setting a format other than wav, though (annoying as it is that I cant add meta info to wavs) the frequency estimation engine will only accept wav files for processing.
It generates readings for frequency estimation over time with a certainty reading from .0 - 1.0, i think to strengthen the tonal content of the signal i’ll try to add a noise gate and some compression, which I guess would be called via AVAuioEngine, so hopefully I’ll be able to apply some similar code to initiate and arm those plugins whilst recording. I’m hoping this can all be achieved in AS and ASObjC
Success! i just tried wav, aif, ogg, even flac worked, additionally discovered at the end of the script the analysis engine processed all formats too!!! Neither AVFoundation or the NLP engine liked m4a or mp3 though. Perhaps you’d have to use AVAudioEngine for that
The ability to change the sample and bit rate in the latter scripts will certainly come in handy. Successfully recorded 8 bit linear pcm at 22050hz to 192000 in 32 bit wavs
Thank you
Now setting AVFormatIDKey and recording as m4a works:
use framework "Foundation"
use framework "AVFoundation"
use scripting additions
property ca : current application
(*
AVFormatIDKey (UInt32 value) for MPEG-4 AAC codec: 1633772320
see: https://docs.microsoft.com/en-us/dotnet/api/audiotoolbox.audioformattype?view=xamarin-ios-sdk-12
for other Audio format identifiers
*)
property kAudioFormatMPEG4AAC : a reference to 1.63377232E+9
set theFilename to "~/Desktop/Test.m4a"
set theURL to ca's |NSURL|'s fileURLWithPath:((ca's NSString's stringWithString:theFilename)'s stringByExpandingTildeInPath())
set theSettings to ca's NSMutableDictionary's dictionary()
theSettings's setObject:kAudioFormatMPEG4AAC forKey:"AVFormatIDKey"
theSettings's setObject:44100 forKey:"AVSampleRateKey"
theSettings's setObject:2 forKey:"AVNumberOfChannelsKey"
theSettings's setObject:16 forKey:"AVLinearPCMBitDepthKey"
theSettings's setObject:0 forKey:"AVLinearPCMIsBigEndianKey"
theSettings's setObject:192000 forKey:"AVEncoderBitRateKey"
set {theRecorder, theError} to ca's AVAudioRecorder's alloc()'s initWithURL:(theURL) settings:theSettings |error|:(reference)
if theRecorder = missing value then error (theError's localizedDescription() as text)
theRecorder's |record|()
delay 5
theRecorder's |stop|()