Last active
November 30, 2021 08:15
-
-
Save iluvcapra/5208960 to your computer and use it in GitHub Desktop.
Pro Tools Import Apple Event via AEDebugSends=1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ 1 } 'aevt': Sd2a/SRgn (i386){ | |
return id: 315 (0x13b) | |
transaction id: 0 (0x0) | |
interaction level: 32 (0x20) | |
reply required: 1 (0x1) | |
remote: 0 (0x0) | |
for recording: 0 (0x0) | |
reply port: 0 (0x0) | |
target: | |
{ 1 } 'sign': 4 bytes { | |
'PTul' (Pro Tools) | |
} | |
fEventSourcePSN: { 0x0,0x2a02a } (Soundminer v4Pro) | |
optional attributes: | |
/* I don't know what these are, I assume they aren't a part of the event soundminer composes, and are attached by the | |
operating system post hoc */ | |
{ 1 } 'reco': - 1 items { | |
key 'shas' - | |
{ 1 } 'list': - 1 elements { | |
{ 1 } 'TEXT': 224 bytes { | |
"59d055578f770792f6f0097bfda6985211c5d6a5;00000000;0000000000000020;com.apple.app-sandbox.read-write;00000001;0e00000b;000000000032ec78;/users/mpe_wr/desktop/untitled/audio files/punch, face; light face punch_af08_76_04.m.wav" | |
} | |
} | |
} | |
event data: | |
{ 1 } 'aevt': - 7 items { | |
key 'FILE' - | |
{ 1 } 'alis': 564 bytes { | |
/* An alias to the file soundminer created and transferred to Pro Tools's "Audio Files" folder | |
Alias's aren't paths, they're Carbon objects that you create with FSNewAlias* and defined in <CoreServices/CoreServices.h>. | |
These are deprecated in 10.8 so it's unclear how this will work in the future, if they're ever dropped. | |
*/ | |
/Users/MPE_WR/Desktop/untitled/Audio Files/Punch, Face; Light Face Punch_AF08_76_04.M.wav | |
} | |
key 'Trak' - | |
{ 1 } 'shor': 2 bytes { | |
-99 (0xffffff9d) // always -99 | |
} | |
key 'FFrm' - | |
{ 1 } 'shor': 2 bytes { | |
1 (0x1) // always 1 | |
} | |
key 'TkOf' - | |
{ 1 } 'shor': 2 bytes { | |
0 (0x0) // as this increments, the insertion point steps down tracks | |
} | |
key 'SMSt' - | |
{ 1 } 'long': 4 bytes { | |
0 (0x0) // samples offset after the insertion point | |
} | |
key 'Strm' - | |
{ 1 } 'shor': 2 bytes { | |
1 (0x1) // always 1 | |
} | |
key 'Rgn ' - | |
{ 1 } 'reco': - 3 items { | |
key 'Star' - | |
{ 1 } 'long': 4 bytes { | |
0 (0x0) // region start from file | |
} | |
key 'Stop' - | |
{ 1 } 'long': 4 bytes { | |
5120 (0x1400) // region end from file | |
} | |
key 'Name' - | |
{ 1 } 'TEXT': 256 bytes { // always 256 bytes, the first byte is a length field a-la a Pascal string, | |
// the rest appears to be garbage | |
"*Punch, Face; Light Face Punch_AF08_76_04.M`??x???8???L | |
???Kg | |
/* This brings Pro Tools to the front */ | |
{ 1 } 'aevt': misc/actv (i386){ | |
return id: 316 (0x13c) | |
transaction id: 0 (0x0) | |
interaction level: 112 (0x70) | |
reply required: 0 (0x0) | |
remote: 0 (0x0) | |
for recording: 0 (0x0) | |
reply port: 0 (0x0) | |
target: | |
{ 2 } 'sign': 4 bytes { | |
'PTul' (Pro Tools) | |
} | |
fEventSourcePSN: { 0x0,0x2a02a } (Soundminer v4Pro) | |
optional attributes: | |
< empty record > | |
event data: | |
{ 1 } 'aevt': - 0 items { | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import <ApplicationServices/ApplicationServices.h> | |
#define kSpotToSelectedTrack (-99) | |
#define kSpotToRegionList (0) | |
/* JHSpotFileToProToolsInsertionPoint() | |
* url : URL of the file to spot | |
* spottedRegionName : name to use in session, will be flattened to ASCII and | |
* truncated to the first 255 characters | |
* sampleRange : region of `url` to spot into session | |
* | |
* spotTarget : Which track to spot the region on: | |
* - `kSpotToSelectedTrack` spots to the highest track | |
* that is selected | |
* - `kSpotToRegionList` spots to the region list | |
* - Any int > 0 spots to that track, counting from the | |
* top of the session (the top track is track 1). | |
* | |
* trackOffset : negative vertical offset for track, when | |
* spotTarget == kSpotToSelectedTrack | |
* "0" will spot on track with cursor, "1" will spot | |
* one track below the cursor, etc. | |
* | |
* cursorPostOffset : when spotTarget == kSpotToSelectedTrack, samples after | |
* insertion point to place region | |
* when spotTarget > 0, samples after session start to | |
* place region | |
* | |
* proToolsPID : kernel pid of Pro Tools | |
* | |
* replyDesc : Return descriptor from Apple Event to Pro Tools | |
*/ | |
OSStatus JHSpotFileToProToolsInsertionPoint(NSURL *url, | |
NSString *spottedRegionName, | |
NSRange sampleRange, | |
NSInteger spotTarget, | |
NSUInteger trackOffset, | |
NSUInteger cursorPostOffset, | |
pid_t proToolsPID, | |
NSAppleEventDescriptor **replyDesc); | |
/* implementation */ | |
/ | |
#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED | |
NSAppleEventDescriptor *JHCreateAliasDescriptorForURL(NSURL *aURL) { | |
NSAppleEventDescriptor *retObj = nil; | |
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 1080 | |
FSRef fsReference; | |
if (CFURLGetFSRef((__bridge CFURLRef)aURL, &fsReference)) { | |
AliasHandle aliasHandle = NULL; | |
OSStatus err = FSNewAliasMinimal(&fsReference, &aliasHandle); | |
if (err == noErr && aliasHandle != NULL) { | |
HLock((Handle)aliasHandle); | |
retObj = [NSAppleEventDescriptor descriptorWithDescriptorType:typeAlias | |
data:[NSData dataWithBytes:*aliasHandle | |
length:GetHandleSize((Handle)aliasHandle)]]; | |
HUnlock((Handle)aliasHandle); | |
DisposeHandle((Handle)aliasHandle); | |
} | |
} | |
#else | |
NSData *urlData = [[aURL absoluteString] dataUsingEncoding:NSUTF8StringEncoding]; | |
retObj = [NSAppleEventDescriptor descriptorWithDescriptorType:typeFileURL | |
data:urlData]; | |
retObj = [retObj coerceToDescriptorType:typeAlias]; | |
#endif | |
return retObj; | |
} | |
#endif | |
NSAppleEventDescriptor *JHCreateTEXTDescriptorForString(NSString *str) { | |
const NSUInteger textMaxLength = 255; | |
const NSUInteger bufferLength = textMaxLength + 1; | |
// this is for safety's sake, but it's worth an experiment to see if it'd work another way | |
NSData *stringData = [str dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; | |
stringData = [stringData subdataWithRange:NSMakeRange(0, MIN(textMaxLength, [stringData length]) )]; | |
char length = (char)[stringData length]; | |
char *buf = calloc(bufferLength,sizeof(char)); | |
buf[0] = length; | |
[stringData getBytes:buf+1 length:length]; | |
NSAppleEventDescriptor *retObj = [NSAppleEventDescriptor descriptorWithDescriptorType:typeChar | |
bytes:buf | |
length:bufferLength]; | |
return retObj; | |
} | |
NSAppleEventDescriptor *JHCreateDescriptorForLong(long int value) { | |
return [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt32 | |
bytes:&value | |
length:sizeof(value)]; | |
} | |
NSAppleEventDescriptor *JHCreateDescriptorForShort(short int value) { | |
return [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt16 | |
bytes:&value | |
length:sizeof(value)]; | |
} | |
NSAppleEventDescriptor *JHCreateDescriptorForRegion(NSString *regionName, NSRange sampleRange) { | |
NSAppleEventDescriptor *retObj = [NSAppleEventDescriptor recordDescriptor]; | |
[retObj setDescriptor:JHCreateDescriptorForLong(sampleRange.location) | |
forKeyword:'Star']; | |
[retObj setDescriptor:JHCreateDescriptorForLong(sampleRange.location + sampleRange.length) | |
forKeyword:'Stop']; | |
[retObj setDescriptor:JHCreateTEXTDescriptorForString(regionName) | |
forKeyword:'Name']; | |
return retObj; | |
} | |
OSStatus JHSpotFileToProToolsInsertionPoint( | |
NSURL *url, | |
NSString *spottedRegionName, | |
NSRange sampleRange, | |
NSInteger spotTarget, | |
NSUInteger trackOffset, | |
NSUInteger cursorPostOffset, | |
pid_t proToolsPID, | |
NSAppleEventDescriptor **replyDesc) { | |
NSAppleEventDescriptor *address = [[NSAppleEventDescriptor alloc] | |
initWithDescriptorType:typeKernelProcessID | |
bytes: &proToolsPID length:sizeof(proToolsPID)]; | |
NSAppleEventDescriptor *message = [NSAppleEventDescriptor | |
appleEventWithEventClass: 'Sd2a' | |
eventID: 'SRgn' | |
targetDescriptor: address | |
returnID: kAutoGenerateReturnID | |
transactionID: kAnyTransactionID | |
]; | |
[message setParamDescriptor:JHCreateAliasDescriptorForURL(url) | |
forKeyword:'FILE']; | |
// setting this to 0 will put the region in the clip list, 1-256 will insert | |
// it on a track counting from the top | |
[message setParamDescriptor:JHCreateDescriptorForShort(spotTarget & 0xff) | |
forKeyword:'Trak']; | |
// dunno | |
[message setParamDescriptor:JHCreateDescriptorForShort(1) | |
forKeyword:'FFrm']; | |
[message setParamDescriptor:JHCreateDescriptorForShort(trackOffset & 0xffff) | |
forKeyword:'TkOf']; | |
[message setParamDescriptor:JHCreateDescriptorForLong(cursorPostOffset & 0xffffffff) | |
forKeyword:'SMSt']; | |
// this selects the lane a mono file region will be dropped in | |
[message setParamDescriptor:JHCreateDescriptorForShort(1) | |
forKeyword:'Strm']; | |
[message setParamDescriptor:JHCreateDescriptorForRegion(spottedRegionName, sampleRange) | |
forKeyword:'Rgn ']; | |
AppleEvent reply; | |
OSStatus retVal = AESendMessage([message aeDesc], &reply, kAEWaitReply, kAEDefaultTimeout); | |
if (retVal == noErr) { | |
*replyDesc = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&reply]; | |
} else { | |
*replyDesc = nil; | |
} | |
return retVal; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is really cool!
Though I had to remove the '& 0xff' mask in line 163 to make spotting-to-cursor work... Obviously, because masking with 0xff would kill a negative number like -99 right?
Cheers!