1/*
2 * Copyright 2006, Haiku.
3 *
4 * Copyright (c) 2002-2004 Matthijs Hollemans
5 * Distributed under the terms of the MIT License.
6 *
7 * Authors:
8 *		Matthijs Hollemans
9 */
10
11#include "debug.h"
12#include <MidiConsumer.h>
13#include <MidiRoster.h>
14#include "MidiRosterLooper.h"
15#include "protocol.h"
16
17#include <pthread.h>
18
19
20using namespace BPrivate;
21
22// The midi_debug_level and midi_dispatcher_priority symbols
23// were exported by Be's libmidi2, and even though they do not
24// appear in the headers, some apps may still be using them.
25// For backwards compatibility's sake, we export those symbols
26// as well, even though we do not use them for anything.
27
28// Not used. For backwards compatibility only.
29int32 midi_debug_level = 0;
30
31// Not used. For backwards compatibility only.
32int32 midi_dispatcher_priority = B_REAL_TIME_PRIORITY;
33
34//------------------------------------------------------------------------------
35
36// The one and only BMidiRoster instance, which is created
37// the first time the client app calls MidiRoster(). It is
38// destroyed by the BMidiRosterKiller when the app quits.
39static BMidiRoster* sRoster = NULL;
40
41static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
42
43
44// Destroys the BMidiRoster instance when the app quits.
45namespace BPrivate
46{
47	static struct BMidiRosterKiller
48	{
49		~BMidiRosterKiller()
50		{
51			delete sRoster;
52		}
53
54		static void CreateRoster() {
55			sRoster = new BMidiRoster();
56		}
57	}
58	midi_roster_killer;
59}
60
61
62BMidiEndpoint*
63BMidiRoster::NextEndpoint(int32* id)
64{
65	BMidiEndpoint* endp = NULL;
66
67	if (id != NULL) {
68		BMidiRosterLooper* looper = MidiRoster()->fLooper;
69		if (looper->Lock()) {
70			endp = looper->NextEndpoint(id);
71			if (endp != NULL) {
72				endp->Acquire();
73			}
74			looper->Unlock();
75		}
76	}
77
78	return endp;
79}
80
81
82BMidiProducer*
83BMidiRoster::NextProducer(int32* id)
84{
85	BMidiEndpoint* endp;
86
87	while ((endp = NextEndpoint(id)) != NULL) {
88		if (endp->IsProducer()) {
89			return (BMidiProducer*) endp;
90		}
91		endp->Release();
92	}
93
94	return NULL;
95}
96
97
98BMidiConsumer*
99BMidiRoster::NextConsumer(int32* id)
100{
101	BMidiEndpoint* endp;
102
103	while ((endp = NextEndpoint(id)) != NULL) {
104		if (endp->IsConsumer()) {
105			return (BMidiConsumer*) endp;
106		}
107		endp->Release();
108	}
109
110	return NULL;
111}
112
113
114BMidiEndpoint*
115BMidiRoster::FindEndpoint(int32 id, bool localOnly)
116{
117	BMidiEndpoint* endp = NULL;
118
119	BMidiRosterLooper* looper = MidiRoster()->fLooper;
120	if (looper->Lock()) {
121		endp = looper->FindEndpoint(id);
122
123		if ((endp != NULL) && endp->IsRemote()) {
124			if (localOnly || !endp->IsRegistered()) {
125				endp = NULL;
126			}
127		}
128
129		if (endp != NULL) {
130			endp->Acquire();
131		}
132
133		looper->Unlock();
134	}
135
136	return endp;
137}
138
139
140BMidiProducer*
141BMidiRoster::FindProducer(int32 id, bool localOnly)
142{
143	BMidiEndpoint* endp = FindEndpoint(id, localOnly);
144
145	if ((endp != NULL) && !endp->IsProducer()) {
146		endp->Release();
147		endp = NULL;
148	}
149
150	return (BMidiProducer*) endp;
151}
152
153
154BMidiConsumer*
155BMidiRoster::FindConsumer(int32 id, bool localOnly)
156{
157	BMidiEndpoint* endp = FindEndpoint(id, localOnly);
158
159	if ((endp != NULL) && !endp->IsConsumer()) {
160		endp->Release();
161		endp = NULL;
162	}
163
164	return (BMidiConsumer*) endp;
165}
166
167
168void
169BMidiRoster::StartWatching(const BMessenger* msngr)
170{
171	if (msngr == NULL) {
172		WARN("StartWatching does not accept a NULL messenger")
173	} else {
174		BMidiRosterLooper* looper = MidiRoster()->fLooper;
175		if (looper->Lock()) {
176			looper->StartWatching(msngr);
177			looper->Unlock();
178		}
179	}
180}
181
182
183void
184BMidiRoster::StopWatching()
185{
186	BMidiRosterLooper* looper = MidiRoster()->fLooper;
187	if (looper->Lock()) {
188		looper->StopWatching();
189		looper->Unlock();
190	}
191}
192
193
194status_t
195BMidiRoster::Register(BMidiEndpoint* endp)
196{
197	if (endp != NULL) {
198		return endp->Register();
199	}
200
201	return B_BAD_VALUE;
202}
203
204
205status_t
206BMidiRoster::Unregister(BMidiEndpoint* endp)
207{
208	if (endp != NULL) {
209		return endp->Unregister();
210	}
211
212	return B_BAD_VALUE;
213}
214
215
216BMidiRoster*
217BMidiRoster::MidiRoster()
218{
219	pthread_once(&sInitOnce, BPrivate::BMidiRosterKiller::CreateRoster);
220
221	return sRoster;
222}
223
224
225BMidiRoster::BMidiRoster()
226{
227	TRACE(("BMidiRoster::BMidiRoster"))
228
229	fLooper = new BMidiRosterLooper();
230
231	if (!fLooper->Init(this))
232		return;
233
234	BMessage msg;
235	msg.what = MSG_REGISTER_APP;
236	msg.AddMessenger("midi:messenger", BMessenger(fLooper));
237	fServer = new BMessenger(MIDI_SERVER_SIGNATURE);
238
239	if (fServer->SendMessage(&msg, fLooper, TIMEOUT) != B_OK) {
240		WARN("Cannot send request to midi_server");
241		return;
242	}
243
244	// Although unlikely, we may receive the midi_server's
245	// "app registered" reply before we lock the semaphore.
246	// In that case, BMidiRosterLooper's MessageReceived()
247	// will bump the semaphore count, and our acquire_sem()
248	// can grab the semaphore safely (without blocking).
249
250	acquire_sem(fLooper->fInitLock);
251}
252
253
254BMidiRoster::~BMidiRoster()
255{
256	TRACE(("BMidiRoster::~BMidiRoster"))
257
258	if (fLooper != NULL) {
259		fLooper->Lock();
260		fLooper->Quit();
261	}
262	delete fServer;
263}
264
265
266void BMidiRoster::_Reserved1() { }
267void BMidiRoster::_Reserved2() { }
268void BMidiRoster::_Reserved3() { }
269void BMidiRoster::_Reserved4() { }
270void BMidiRoster::_Reserved5() { }
271void BMidiRoster::_Reserved6() { }
272void BMidiRoster::_Reserved7() { }
273void BMidiRoster::_Reserved8() { }
274
275
276void
277BMidiRoster::CreateLocal(BMidiEndpoint* endp)
278{
279	ASSERT(endp != NULL)
280
281	// We are being called from the BMidiLocalConsumer or
282	// BMidiLocalProducer constructor, so there is no need
283	// to lock anything, because at this point there cannot
284	// be multiple threads accessing the endpoint's data.
285
286	BMessage msg;
287	msg.what = MSG_CREATE_ENDPOINT;
288	msg.AddBool("midi:consumer", endp->fIsConsumer);
289	msg.AddBool("midi:registered", endp->fIsRegistered);
290	msg.AddString("midi:name", endp->Name());
291	msg.AddMessage("midi:properties", endp->fProperties);
292
293	if (endp->IsConsumer()) {
294		BMidiConsumer* consumer = (BMidiConsumer*) endp;
295		msg.AddInt32("midi:port", consumer->fPort);
296		msg.AddInt64("midi:latency", consumer->fLatency);
297	}
298
299	BMessage reply;
300	if (SendRequest(&msg, &reply) == B_OK) {
301		status_t res;
302		if (reply.FindInt32("midi:result", &res) == B_OK) {
303			if (res == B_OK) {
304				int32 id;
305				if (reply.FindInt32("midi:id", &id) == B_OK) {
306					endp->fId = id;
307
308					if (fLooper->Lock()) {
309						fLooper->AddEndpoint(endp);
310						fLooper->Unlock();
311					}
312				}
313			}
314		}
315	}
316
317	// There are many things that can go wrong when creating
318	// a new endpoint, but BMidiEndpoint has no InitCheck()
319	// method to check for this. (You can, however, see if the
320	// endpoint's ID is 0 after the constructor returns, or
321	// call IsValid().) In any case, you should still Release()
322	// the endpoint to delete the object. (This is different
323	// from Be's implementation, which bumps the refcount only
324	// when creation succeeded. If you call Release(), you'll
325	// trip an assertion, so you can't delete these endpoints.)
326}
327
328
329void
330BMidiRoster::DeleteLocal(BMidiEndpoint* endp)
331{
332	ASSERT(endp != NULL)
333
334	BMessage msg;
335	msg.what = MSG_DELETE_ENDPOINT;
336	msg.AddInt32("midi:id", endp->ID());
337
338	// Note: this is always called from BMidiLocalConsumer's
339	// or BMidiLocalProducer's destructor, so we don't expect
340	// a reply from the server. If something went wrong, the
341	// object will be destroyed regardless.
342
343	fServer->SendMessage(&msg, (BHandler*) NULL, TIMEOUT);
344
345	// If the endpoint was successfully created, we must remove
346	// it from the list of endpoints. If creation failed, then
347	// we didn't put the endpoint on that list. If the endpoint
348	// was connected to anything, we must also disconnect it.
349
350	if (endp->ID() > 0) {
351		if (fLooper->Lock()) {
352			fLooper->RemoveEndpoint(endp);
353			fLooper->Unlock();
354		}
355	}
356}
357
358
359status_t
360BMidiRoster::SendRequest(BMessage* msg, BMessage* reply)
361{
362	ASSERT(msg != NULL)
363	ASSERT(reply != NULL)
364
365	status_t err = fServer->SendMessage(msg, reply, TIMEOUT, TIMEOUT);
366
367	if (err != B_OK) {
368		WARN("Cannot send request to midi_server");
369	}
370
371	#ifdef DEBUG
372	if (err == B_OK) {
373		printf("REPLY "); reply->PrintToStream();
374	}
375	#endif
376
377	return err;
378}
379
380