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];
117 // 10 elements is a reasonable starting point
118 oscServices
= [[NSMutableArray alloc
] initWithCapacity
: 10];
123 - (void)findOSCServices
125 for (int serviceType
= 0; serviceType
< kNumOSCServiceTypes
; serviceType
++) {
126 [browsers
[serviceType
] searchForServicesOfType
: kOSCServiceTypeStrings
[serviceType
] inDomain
: @
""];
130 - (unsigned)numberOfOSCServices
132 return [oscServices count
];
135 - (OSCService
*)oscServiceAtIndex
:(unsigned)index
137 if ((index
>= [oscServices count
]))
139 return [oscServices objectAtIndex
: index
];
142 - (void)resolveAllServices
144 // See the comment on re-entrancy in resolveNextService
146 // begin with the first service, and let them each hand off to the next
147 // (see the implementation of netServiceDidResolveAddress:)
148 if ([oscServices count
] < 1)
151 indexOfServiceBeingResolved
= 0;
152 [self resolveNextService
];
155 - (void)resolveNextService
157 // NOTE ON RE-ENTRANCY OF THIS CODE
158 // Theoretically, there should be locks between resolveAllServices, resolveNextService, and
159 // netServiceDidResolveAddress, but in practice it is not necessary because all these run on the
160 // same thread, so it is not possible, e.g., for resolveNextService to have simultaneous, interleaved
161 // calls. Thus, there is no need for locking.
163 // find something to resolve
164 for (; indexOfServiceBeingResolved
< [oscServices count
]; indexOfServiceBeingResolved
++) {
165 serviceBeingResolved
= [oscServices objectAtIndex
: indexOfServiceBeingResolved
];
166 if (!serviceBeingResolved
->isResolved
)
170 // if we didn't find anything, we are done
171 if (!(indexOfServiceBeingResolved
< [oscServices count
])) {
173 serviceBeingResolved
= nil;
174 indexOfServiceBeingResolved
= 0;
179 [serviceBeingResolved
->netService setDelegate
: self];
180 [serviceBeingResolved
->netService resolveWithTimeout
:5]; // Deprecated; deprecator auto uses use resolveWithTimeout 5, should probably shorten
183 // Rendezvous implementation methods
184 - (void)netServiceBrowserWillSearch
:(NSNetServiceBrowser
*)aNetServiceBrowser
189 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didFindService
:(NSNetService
*)aNetService moreComing
:(BOOL)moreComing
191 if (nil == aNetService
)
193 // I don't think this should happen...
197 // see if it is a duplicate
198 unsigned arrayCount
= [oscServices count
];
200 OSCService
* potentialDuplicate
;
201 for(i
= 0; i
< arrayCount
; i
++)
203 potentialDuplicate
= [oscServices objectAtIndex
: i
];
204 if ([aNetService isEqual
: potentialDuplicate
->netService
])
206 (potentialDuplicate
->refCount
)++;
211 // allocate an OSCService object for this NSService
212 // (autorelease it because we are going to put it into an array)
213 OSCService
* service
= [[[OSCService alloc
] init
] autorelease
];
214 service
->netService
= aNetService
;
215 [service
->netService retain
];
217 service
->isResolved
= NO
;
219 // add this to the list of known services
220 [oscServices addObject
: service
];
222 // if there's no more coming, then it's time to resolve all the services
224 [self resolveAllServices
];
228 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didNotSearch
:(NSDictionary
*)errorDict
230 post("Can't search for OSC Services\n");
233 - (void)netBrowserDidStopSearch
:(NSNetServiceBrowser
*)aNetServiceBrowser
238 - (void)netServiceWillResolve
:(NSNetService
*)sender
243 - (void)netServiceDidResolveAddress
:(NSNetService
*)sender
245 if (!serviceBeingResolved
) {
246 // this is odd... I got a resolution I didn't ask for
247 post("Resolved OSC Service %s without requesting it.\n", [[sender name
] cString
]);
253 NSArray
* addresses
= [sender addresses
];
254 if (nil == addresses
)
255 return; // I don't think this should happen
257 // just pick one of them
258 NSData
* address
= [addresses lastObject
];
260 return; // I don't think this should happen
262 const struct sockaddr_in
* sockaddr
= (const struct sockaddr_in
*) [address bytes
];
263 serviceBeingResolved
->netService
= sender
;
264 serviceBeingResolved
->sockaddr
= sockaddr
;
265 serviceBeingResolved
->hostAddress
= sockaddr
->sin_addr.s_addr
;
266 serviceBeingResolved
->port
= sockaddr
->sin_port
;
268 struct hostent
* hostent
= gethostbyaddr((char*) &(sockaddr
->sin_addr
), 4, AF_INET
);
271 serviceBeingResolved
->hostName
= [[NSString alloc
] initWithCString
: hostent
->h_name
];
273 // Couldn't find a host name for the address.
274 // Convert the address to a string for the hostname
275 char* addrOctets
= inet_ntoa(sockaddr
->sin_addr
);
276 serviceBeingResolved
->hostName
= [[NSString alloc
] initWithCString
: addrOctets
];
278 serviceBeingResolved
->isResolved
= YES
;
281 [self resolveNextService
];
284 - (void)netService
:(NSNetService
*)sender didNotResolve
:(NSDictionary
*)errorDict
286 post("Could not resolve the address for a discovered OSC Service\n");
289 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didRemoveDomain
:(NSString
*)domainString moreComing
:(BOOL)moreComing
291 // Ignore this -- I don't think it happens, as I never triger a removal of a domain
294 - (void)netServiceBrowser
:(NSNetServiceBrowser
*)aNetServiceBrowser didRemoveService
:(NSNetService
*)aNetService moreComing
:(BOOL)moreComing
298 // remove this from the list of services
299 unsigned arrayCount
= [oscServices count
];
301 for(i
= 0; i
< arrayCount
; i
++)
303 service
= [oscServices objectAtIndex
: i
];
304 if ([aNetService isEqual
: service
->netService
])
307 if (service
->refCount
< 1) {
308 [service
->hostName release
];
309 // removing it from the array will release the service object
311 [oscServices removeObjectAtIndex
: i
];
320 @implementation OSCService
324 // SuperCollider glue
325 int prNumOSCServices(struct VMGlobals
*g
, int numArgsPushed
);
326 int prNumOSCServices(struct VMGlobals
*g
, int numArgsPushed
)
328 unsigned numServices
= [[RendezvousClient sharedClient
] numberOfOSCServices
];
329 // set the result of the call to the numServices
330 SetInt(g
->sp
, numServices
);
334 int prInitOSCService(struct VMGlobals
*g
, int numArgsPushed
);
335 int prInitOSCService(struct VMGlobals
*g
, int numArgsPushed
)
337 PyrSlot
*serverSlot
= g
->sp
-1;
338 PyrSlot
*indexSlot
= g
->sp
;
341 slotIntVal(indexSlot
, &index
);
343 OSCService
* service
= [[RendezvousClient sharedClient
] oscServiceAtIndex
: index
];
347 // set the server name
348 NSString
* name
= [service
->netService name
];
349 PyrString
*serverNameString
= newPyrString(g
->gc
, [name cString
] , 0, true);
350 PyrObject
*serverObject
= slotRawObject(serverSlot
);
351 SetObject(&serverObject
->slots
[0], serverNameString
);
353 if (!service
->isResolved
) {
354 PyrString
*hostNameString
= newPyrString(g
->gc
, "" , 0, true);
355 SetObject(&serverObject
->slots
[1], hostNameString
);
359 SetInt(&serverObject
->slots
[2], port
);
364 NSString
* hostName
= service
->hostName
;
365 PyrString
*hostNameString
= newPyrString(g
->gc
, [hostName cString
] , 0, true);
366 SetObject(&serverObject
->slots
[1], hostNameString
);
369 int port
= (int) service
->port
;
370 SetInt(&serverObject
->slots
[2], port
);
373 NSString
* type
= [service
->netService type
];
374 PyrSlot
* protoSlot
= &serverObject
->slots
[3];
375 if ([type isEqualToString
: kOSCServiceTypeStrings
[kOSCServiceUDP
]]) {
376 SetSymbol(protoSlot
, getsym("udp"));
377 } else if ([type isEqualToString
: kOSCServiceTypeStrings
[kOSCServiceTCP
]]) {
378 SetSymbol(protoSlot
, getsym("tcp"));
384 // if OSCService is changed to hold onto a netAddr, use this to set the netAddr fields
385 PyrSlot *netAddrSlot = &serverObject->slots[1];
386 PyrObject *netAddrObject = slotRawObject(netAddrSlot);
389 int addr = (int) service->hostAddress;
390 SetInt(&netAddrObject->slots[0], addr);
393 int port = (int) service->port;
394 SetInt(&netAddrObject->slots[1], port);
397 NSString* hostName = service->hostName;
398 PyrString *hostNameString = newPyrString(g->gc, [hostName cString] , 0, true);
399 SetObject(&serverObject->slots[2], hostNameString);
405 void initRendezvousPrimitives()
408 base
= nextPrimitiveIndex();
410 definePrimitive(base
, index
++, "_NumOSCServices", prNumOSCServices
, 1, 0);
411 definePrimitive(base
, index
++, "_InitOSCService", prInitOSCService
, 3, 0);