7

I am writing an Applescript for Spotify that is supposed to do two things:

  1. Log every new song played.
  2. Every N seconds check what sound device is used.

I do (1) by a simple loop that check how much of the track remains and then use that value in a delay statement in the loop (unnecessary to run the loop at full speed). However, N usually is much lower than the remaining time of the current track. Hence, I would like task (2) to run in a separate loop with N-0.5 as delay value. Basically I want to loops to run simultaneously from the same script. Is this possible?

(Of course I could use two separate scripts but I need to distribute this solution when it is done, therefore less visual complexity - i.e., two scripts running simultaneously taking up space in Dock, needing to be launched etc - is better.)

  • In this cas look at events so starting a new song kicks off an applescript - also event whenb sound device is chnaged – user151019 Oct 10 '17 at 23:11
  • How do I look at events? I'm interested in this not only for Spotify but for several other applications, including iTunes, Pages and Chrome. – d-b Oct 11 '17 at 12:44
2

Not easily. The concept of processes and threads isn't contained in AppleScript.

You'll want another tool for that job. Swift might be a good one unless you're looking for python or something a bit more established.

2

Language Limits

As of macOS 10.13, AppleScript does not trivially support multithreading within a single script. It appears possible to use NSThread within an AppleScript but this is complex and leads to a second limitation, that of the application being interacted with.

Application Limits

In addition to the AppleScript language itself not natively representing threads, the called application is unlikely to deal with AppleEvents in a thread optimal manner. AppleEvents are queued by macOS in a First-In First-Out (FIFO) order for processing by the application.

The application can defer responses to long running AppleEvents but this requires additional engineering that few applications implement. Most applications will block until the AppleEvent is processed before looking at the next AppleEvent.

This means a multi-threaded AppleScript talking to single application would likely not see the performance gains expected for the effort.

Single Event Loop?

You could approach the problem with a single loop dealing with multiple events. The single loop could sleep for N seconds, wake and deal with the pending appropriate tasks.

Piyomaru's answer shows one way to implement an event based approach. This approach is not multi-threaded or parallel; it is an event driven loop.

  • AppleScript now supports NSThread natively. Script Editor says "AppleScript 2.5 (Thread Safe)" on its OSA language popup selector (this is be called in multi-thread). – Piyomaru Oct 13 '17 at 11:59
  • Supporting NSThread does not mean the script is threaded; see Thread Safety for more details. – Graham Miln Oct 13 '17 at 14:37
  • In fact, Now AppleScript can generate threads via NSThread like this. piyocast.com/as/archives/4897 – Piyomaru Oct 15 '17 at 11:04
  • That is good to know. I have updated my answer. Your suggested script does not use NSThread, can it be modified to be threaded? – Graham Miln Oct 15 '17 at 15:07
  • Mac OS X and its API Cocoa is designed to use notification in such case. There is no use to use thread. – Piyomaru Oct 16 '17 at 1:22
1

AppleScript now supports multi-threading (macOS 10.10 or later). And originally it has timer interrupt function since 1993 (on idle timer event handler works concurrently within single AppleScript).

I wrote some AppleScripts with iTunes like you wrote. AppleScript can make delegates and receive other application's event.

This sample AppleScript can receive iTunes's playing state change. Once, the script run on Script Editor, it receive the change event of iTunes playing and displays name of track using macOS notification dialog.

-- Created 2017-10-13 by Takaaki Naganoya
-- 2017 Piyomaru Software

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSDistributedNotificationCenter : a reference to current application's NSDistributedNotificationCenter

on run
    NSDistributedNotificationCenter's defaultCenter()'s addObserver:me selector:"statusChanged:" |name|:"com.apple.iTunes.playerInfo" object:(missing value)

end run

on statusChanged:sender
    tell application id "com.apple.iTunes"
        try
            tell current track
                set anAlbum to album
                set aName to name
                set anArtist to album artist
                set aRating to rating
            end tell
        on error
            return
        end try
    end tell
    display notification aName
end statusChanged:

So, the point is Spotify application can provide such a function like iTunes or not.

  • 1
    Thank you for your answer. Do you think you could add some comments to the code to explain how it works? It is not obvious/I am not good enough on Applescript to follow it in detail. – d-b Oct 13 '17 at 12:11
  • I am ready to offer various services suitable for your guarantee :-) – Piyomaru Oct 15 '17 at 16:01
0

I would suggest writing this in JavaScript rather than AppleScript. JavaScript is a supported language for Apple events scripting. JavaScript is single threaded, but it is a lot easier to write non-blocking asynchronous code.

setInterval(function() {
    console.log('Do something in the closure function here');
}, 3000);
  • 1
    Welcome to Ask Different! While using JavaScript may be a possibility, nonetheless IMO without providing a working example, what you posted is just a comment, not an answer. When suggesting the use of a language not mentioned in the OP or Tags, a good answer should typically demonstrate how to achieve the goal, not just suggest using a language not mentioned in the OP. – user3439894 Oct 3 '17 at 16:53
  • I don't speak Javascript (well, I can read it on an extremely basic level since I know Java and some C), can you get me going with an example? – d-b Oct 3 '17 at 19:12
  • You can use a setInterval function to run some task every three seconds, but it would be better to hook onto some event. It has been nearly 20 years since I have done any Apple Events scripting, but this should be possible. Here is a code example of setInterval; // Check every three seconds for something setInterval(function() { // The code inside of this function will check every time setInterval fires. console.log('checking something'); }, 3000); – David Fekke Oct 4 '17 at 17:03
  • 1
    Comments are not indexed by search engines and can and do at times get deleted, so it's better if you add relevant information to the actual answer using the edit link, bottom left of the answer, not in comments. – user3439894 Oct 4 '17 at 20:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .