5 // Created by C. Ramakrishnan on Mon Feb 24 2003.
6 // Copyright (c) 2003 __MyCompanyName__. All rights reserved.
10 SuperCollider real time audio synthesis system
11 Copyright (c) 2002 James McCartney. All rights reserved.
12 http://www.audiosynth.com
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 #import "RendezvousClient.h"
32 // for the locking mechanism
33 #import <Foundation/NSLock.h>
35 // SC headers for primitives
36 #include "PyrPrimitive.h"
37 #include "PyrObject.h"
38 #include "PyrKernel.h"
39 #include "VMGlobals.h"
44 #include <sys/socket.h>
45 #include <arpa/inet.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
57 @interface RendezvousClient (RendezvousClientNetServiceBrowserCallbacks
)
58 // methods for callbacks from the NetServiceBrowser API
60 - (void)netServiceBrowserWillSearch
:(NSNetServiceBrowser
*)aNetServiceBrowser
;
61 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didFindService
:(NSNetService
*)aNetService moreComing
:(BOOL)moreComing
;
62 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didNotSearch
:(NSDictionary
*)errorDict
;
63 - (void)netBrowserDidStopSearch
:(NSNetServiceBrowser
*)aNetServiceBrowser
;
65 - (void)netServiceWillResolve
:(NSNetService
*)sender
;
66 - (void)netServiceDidResolveAddress
:(NSNetService
*)sender
;
67 - (void)netService
:(NSNetService
*)sender didNotResolve
:(NSDictionary
*)errorDict
;
69 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didRemoveDomain
:(NSString
*)domainString moreComing
:(BOOL)moreComing
;
71 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didRemoveService
:(NSNetService
*)aNetService moreComing
:(BOOL)moreComing
;
76 @interface RendezvousClient (RendezvousClientPrivate
)
77 // private methods for internal use
78 - (void)resolveAllServices
;
79 - (void)resolveNextService
;
84 void initRendezvousPrimitives();
86 static NSString
* kOSCServiceTypeStrings
[kNumOSCServiceTypes
] = {
91 static RendezvousClient
* sharedRendezvousClient
= nil; // shared instance of the rendezvous client
92 static OSCService
* serviceBeingResolved
= nil; // placeholder for what I'm currently resolving
93 static unsigned indexOfServiceBeingResolved
= 0; // which service am I currently resolving?
96 @implementation RendezvousClient
98 + (RendezvousClient
*)sharedClient
100 if (nil == sharedRendezvousClient
)
102 sharedRendezvousClient
= [[RendezvousClient alloc
] init
];
105 return sharedRendezvousClient
;
112 for (int serviceType
= 0; serviceType
< kNumOSCServiceTypes
; serviceType
++) {
113 browsers
[serviceType
] = [[NSNetServiceBrowser alloc
] init
];
114 [browsers
[serviceType
] setDelegate
: self];
116 // 10 elements is a reasonable starting point
117 oscServices
= [[NSMutableArray alloc
] initWithCapacity
: 10];
122 - (void)findOSCServices
124 for (int serviceType
= 0; serviceType
< kNumOSCServiceTypes
; serviceType
++) {
125 [browsers
[serviceType
] searchForServicesOfType
: kOSCServiceTypeStrings
[serviceType
] inDomain
: @
""];
129 - (unsigned)numberOfOSCServices
131 return [oscServices count
];
134 - (OSCService
*)oscServiceAtIndex
:(unsigned)index
136 if ((index
>= [oscServices count
]))
138 return [oscServices objectAtIndex
: index
];
141 - (void)resolveAllServices
143 // See the comment on re-entrancy in resolveNextService
145 // begin with the first service, and let them each hand off to the next
146 // (see the implementation of netServiceDidResolveAddress:)
147 if ([oscServices count
] < 1)
150 indexOfServiceBeingResolved
= 0;
151 [self resolveNextService
];
154 - (void)resolveNextService
156 // NOTE ON RE-ENTRANCY OF THIS CODE
157 // Theoretically, there should be locks between resolveAllServices, resolveNextService, and
158 // netServiceDidResolveAddress, but in practice it is not necessary because all these run on the
159 // same thread, so it is not possible, e.g., for resolveNextService to have simultaneous, interleaved
160 // calls. Thus, there is no need for locking.
162 // find something to resolve
163 for (; indexOfServiceBeingResolved
< [oscServices count
]; indexOfServiceBeingResolved
++) {
164 serviceBeingResolved
= [oscServices objectAtIndex
: indexOfServiceBeingResolved
];
165 if (!serviceBeingResolved
->isResolved
)
169 // if we didn't find anything, we are done
170 if (!(indexOfServiceBeingResolved
< [oscServices count
])) {
172 serviceBeingResolved
= nil;
173 indexOfServiceBeingResolved
= 0;
178 [serviceBeingResolved
->netService setDelegate
: self];
179 [serviceBeingResolved
->netService resolveWithTimeout
:5]; // Deprecated; deprecator auto uses use resolveWithTimeout 5, should probably shorten
182 // Rendezvous implementation methods
183 - (void)netServiceBrowserWillSearch
:(NSNetServiceBrowser
*)aNetServiceBrowser
188 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didFindService
:(NSNetService
*)aNetService moreComing
:(BOOL)moreComing
190 if (nil == aNetService
)
192 // I don't think this should happen...
196 // see if it is a duplicate
197 unsigned arrayCount
= [oscServices count
];
199 OSCService
* potentialDuplicate
;
200 for(i
= 0; i
< arrayCount
; i
++)
202 potentialDuplicate
= [oscServices objectAtIndex
: i
];
203 if ([aNetService isEqual
: potentialDuplicate
->netService
])
205 (potentialDuplicate
->refCount
)++;
210 // allocate an OSCService object for this NSService
211 // (autorelease it because we are going to put it into an array)
212 OSCService
* service
= [[[OSCService alloc
] init
] autorelease
];
213 service
->netService
= aNetService
;
214 [service
->netService retain
];
216 service
->isResolved
= NO
;
218 // add this to the list of known services
219 [oscServices addObject
: service
];
221 // if there's no more coming, then it's time to resolve all the services
223 [self resolveAllServices
];
227 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didNotSearch
:(NSDictionary
*)errorDict
229 post("Can't search for OSC Services\n");
232 - (void)netBrowserDidStopSearch
:(NSNetServiceBrowser
*)aNetServiceBrowser
237 - (void)netServiceWillResolve
:(NSNetService
*)sender
242 - (void)netServiceDidResolveAddress
:(NSNetService
*)sender
244 if (!serviceBeingResolved
) {
245 // this is odd... I got a resolution I didn't ask for
246 post("Resolved OSC Service %s without requesting it.\n", [[sender name
] cStringUsingEncoding
:[NSString defaultCStringEncoding
]]);
252 NSArray
* addresses
= [sender addresses
];
253 if (nil == addresses
)
254 return; // I don't think this should happen
256 // just pick one of them
257 NSData
* address
= [addresses lastObject
];
259 return; // I don't think this should happen
261 const struct sockaddr_in
* sockaddr
= (const struct sockaddr_in
*) [address bytes
];
262 serviceBeingResolved
->netService
= sender
;
263 serviceBeingResolved
->sockaddr
= sockaddr
;
264 serviceBeingResolved
->hostAddress
= sockaddr
->sin_addr.s_addr
;
265 serviceBeingResolved
->port
= sockaddr
->sin_port
;
267 struct hostent
* hostent
= gethostbyaddr((char*) &(sockaddr
->sin_addr
), 4, AF_INET
);
270 serviceBeingResolved
->hostName
= [[NSString alloc
] initWithCString
: hostent
->h_name encoding
:NSASCIIStringEncoding
];
272 // Couldn't find a host name for the address.
273 // Convert the address to a string for the hostname
274 char* addrOctets
= inet_ntoa(sockaddr
->sin_addr
);
275 serviceBeingResolved
->hostName
= [[NSString alloc
] initWithCString
: addrOctets encoding
:NSASCIIStringEncoding
];
277 serviceBeingResolved
->isResolved
= YES
;
280 [self resolveNextService
];
283 - (void)netService
:(NSNetService
*)sender didNotResolve
:(NSDictionary
*)errorDict
285 post("Could not resolve the address for a discovered OSC Service\n");
288 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didRemoveDomain
:(NSString
*)domainString moreComing
:(BOOL)moreComing
290 // Ignore this -- I don't think it happens, as I never triger a removal of a domain
293 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didRemoveService
:(NSNetService
*)aNetService moreComing
:(BOOL)moreComing
297 // remove this from the list of services
298 unsigned arrayCount
= [oscServices count
];
300 for(i
= 0; i
< arrayCount
; i
++)
302 service
= [oscServices objectAtIndex
: i
];
303 if ([aNetService isEqual
: service
->netService
])
306 if (service
->refCount
< 1) {
307 [service
->hostName release
];
308 // removing it from the array will release the service object
310 [oscServices removeObjectAtIndex
: i
];
319 @implementation OSCService
323 // SuperCollider glue
324 int prNumOSCServices(struct VMGlobals
*g
, int numArgsPushed
);
325 int prNumOSCServices(struct VMGlobals
*g
, int numArgsPushed
)
327 unsigned numServices
= [[RendezvousClient sharedClient
] numberOfOSCServices
];
328 // set the result of the call to the numServices
329 SetInt(g
->sp
, numServices
);
333 int prInitOSCService(struct VMGlobals
*g
, int numArgsPushed
);
334 int prInitOSCService(struct VMGlobals
*g
, int numArgsPushed
)
336 PyrSlot
*serverSlot
= g
->sp
-1;
337 PyrSlot
*indexSlot
= g
->sp
;
340 slotIntVal(indexSlot
, &index
);
342 OSCService
* service
= [[RendezvousClient sharedClient
] oscServiceAtIndex
: index
];
346 // set the server name
347 NSString
* name
= [service
->netService name
];
348 PyrString
*serverNameString
= newPyrString(g
->gc
, [name cStringUsingEncoding
:[NSString defaultCStringEncoding
]] , 0, true);
349 PyrObject
*serverObject
= slotRawObject(serverSlot
);
350 SetObject(&serverObject
->slots
[0], serverNameString
);
352 if (!service
->isResolved
) {
353 PyrString
*hostNameString
= newPyrString(g
->gc
, "" , 0, true);
354 SetObject(&serverObject
->slots
[1], hostNameString
);
358 SetInt(&serverObject
->slots
[2], port
);
363 NSString
* hostName
= service
->hostName
;
364 PyrString
*hostNameString
= newPyrString(g
->gc
, [hostName cStringUsingEncoding
:[NSString defaultCStringEncoding
]] , 0, true);
365 SetObject(&serverObject
->slots
[1], hostNameString
);
368 int port
= (int) service
->port
;
369 SetInt(&serverObject
->slots
[2], port
);
372 NSString
* type
= [service
->netService type
];
373 PyrSlot
* protoSlot
= &serverObject
->slots
[3];
374 if ([type isEqualToString
: kOSCServiceTypeStrings
[kOSCServiceUDP
]]) {
375 SetSymbol(protoSlot
, getsym("udp"));
376 } else if ([type isEqualToString
: kOSCServiceTypeStrings
[kOSCServiceTCP
]]) {
377 SetSymbol(protoSlot
, getsym("tcp"));
383 // if OSCService is changed to hold onto a netAddr, use this to set the netAddr fields
384 PyrSlot *netAddrSlot = &serverObject->slots[1];
385 PyrObject *netAddrObject = slotRawObject(netAddrSlot);
388 int addr = (int) service->hostAddress;
389 SetInt(&netAddrObject->slots[0], addr);
392 int port = (int) service->port;
393 SetInt(&netAddrObject->slots[1], port);
396 NSString* hostName = service->hostName;
397 PyrString *hostNameString = newPyrString(g->gc, [hostName cString] , 0, true);
398 SetObject(&serverObject->slots[2], hostNameString);
404 void initRendezvousPrimitives()
407 base
= nextPrimitiveIndex();
409 definePrimitive(base
, index
++, "_NumOSCServices", prNumOSCServices
, 1, 0);
410 definePrimitive(base
, index
++, "_InitOSCService", prInitOSCService
, 3, 0);