1/*
2 * Copyright 2006, Haiku.
3 *
4 * Copyright (c) 2002-2003 Matthijs Hollemans
5 * Copyright (c) 2002 Jerome Leveque
6 * Distributed under the terms of the MIT License.
7 *
8 * Authors:
9 *		Matthijs Hollemans
10 *		Jérôme Leveque
11 */
12
13#include <MidiPort.h>
14#include <MidiProducer.h>
15#include <MidiRoster.h>
16#include <stdlib.h>
17
18#include "debug.h"
19#include "MidiGlue.h"
20
21using namespace BPrivate;
22
23
24BMidiPort::BMidiPort(const char* name)
25{
26	fPortName = NULL;
27	fDevices  = new BList;
28	fStatus   = B_ERROR;
29
30	fLocalSource = new BMidiLocalProducer("MidiPortGlue(out)");
31	fLocalSink   = new BMidiPortGlue(this, "MidiPortGlue(in)");
32	fLocalSource->Register();
33	fLocalSink->Register();
34
35	fRemoteSource = NULL;
36	fRemoteSink   = NULL;
37
38	ScanDevices();
39
40	if (name != NULL)
41		Open(name);
42}
43
44
45BMidiPort::~BMidiPort()
46{
47	Close();
48
49	EmptyDeviceList();
50	delete fDevices;
51
52	fLocalSource->Unregister();
53	fLocalSink->Unregister();
54	fLocalSource->Release();
55	fLocalSink->Release();
56}
57
58
59status_t
60BMidiPort::InitCheck() const
61{
62	return fStatus;
63}
64
65
66status_t
67BMidiPort::Open(const char* name)
68{
69	fStatus = B_ERROR;
70
71	if (name != NULL) {
72		Close();
73
74		for (int32 t = 0; t < fDevices->CountItems(); ++t) {
75			BMidiEndpoint* endp = (BMidiEndpoint*)fDevices->ItemAt(t);
76			if (strcmp(name, endp->Name()) != 0)
77				continue;
78			if (!endp->IsValid())  // still exists?
79				continue;
80			if (endp->IsProducer()) {
81				if (fRemoteSource == NULL)
82					fRemoteSource = (BMidiProducer*)endp;
83			} else {
84				if (fRemoteSink == NULL) {
85					fRemoteSink = (BMidiConsumer*)endp;
86					fLocalSource->Connect(fRemoteSink);
87				}
88			}
89		}
90
91		if (fRemoteSource != NULL) {
92			fPortName = strdup(fRemoteSource->Name());
93			fStatus = B_OK;
94		} else if (fRemoteSink != NULL) {
95			fPortName = strdup(fRemoteSink->Name());
96			fStatus = B_OK;
97		}
98	}
99
100	return fStatus;
101}
102
103
104void
105BMidiPort::Close()
106{
107	if (fRemoteSource != NULL) {
108		fRemoteSource->Disconnect(fLocalSink);
109		fRemoteSource = NULL;
110	}
111
112	if (fRemoteSink != NULL) {
113		fLocalSource->Disconnect(fRemoteSink);
114		fRemoteSink = NULL;
115	}
116
117	if (fPortName != NULL) {
118		free(fPortName);
119		fPortName = NULL;
120	}
121}
122
123
124const char*
125BMidiPort::PortName() const
126{
127	return fPortName;
128}
129
130
131void
132BMidiPort::NoteOff(
133	uchar channel, uchar note, uchar velocity, uint32 time)
134{
135	fLocalSource->SprayNoteOff(channel - 1, note, velocity, MAKE_BIGTIME(time));
136}
137
138
139void
140BMidiPort::NoteOn(
141	uchar channel, uchar note, uchar velocity, uint32 time)
142{
143	fLocalSource->SprayNoteOn(channel - 1, note, velocity, MAKE_BIGTIME(time));
144}
145
146
147void
148BMidiPort::KeyPressure(
149	uchar channel, uchar note, uchar pressure, uint32 time)
150{
151	fLocalSource->SprayKeyPressure(
152		channel - 1, note, pressure, MAKE_BIGTIME(time));
153}
154
155
156void
157BMidiPort::ControlChange(
158	uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
159{
160	fLocalSource->SprayControlChange(
161		channel - 1, controlNumber, controlValue, MAKE_BIGTIME(time));
162}
163
164
165void
166BMidiPort::ProgramChange(
167	uchar channel, uchar programNumber, uint32 time)
168{
169	fLocalSource->SprayProgramChange(
170		channel - 1, programNumber, MAKE_BIGTIME(time));
171}
172
173
174void
175BMidiPort::ChannelPressure(uchar channel, uchar pressure, uint32 time)
176{
177	fLocalSource->SprayChannelPressure(
178		channel - 1, pressure, MAKE_BIGTIME(time));
179}
180
181
182void
183BMidiPort::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
184{
185	fLocalSource->SprayPitchBend(channel - 1, lsb, msb, MAKE_BIGTIME(time));
186}
187
188
189void
190BMidiPort::SystemExclusive(void* data, size_t length, uint32 time)
191{
192	fLocalSource->SpraySystemExclusive(data, length, MAKE_BIGTIME(time));
193}
194
195
196void
197BMidiPort::SystemCommon(
198	uchar status, uchar data1, uchar data2, uint32 time)
199{
200	fLocalSource->SpraySystemCommon(status, data1, data2, MAKE_BIGTIME(time));
201}
202
203
204void
205BMidiPort::SystemRealTime(uchar status, uint32 time)
206{
207	fLocalSource->SpraySystemRealTime(status, MAKE_BIGTIME(time));
208}
209
210
211status_t
212BMidiPort::Start()
213{
214	status_t err = super::Start();
215
216	if ((err == B_OK) && (fRemoteSource != NULL)) {
217		return fRemoteSource->Connect(fLocalSink);
218	}
219
220	return err;
221}
222
223
224void
225BMidiPort::Stop()
226{
227	if (fRemoteSource != NULL) {
228		fRemoteSource->Disconnect(fLocalSink);
229	}
230
231	super::Stop();
232}
233
234
235int32
236BMidiPort::CountDevices()
237{
238	return fDevices->CountItems();
239}
240
241
242status_t
243BMidiPort::GetDeviceName(int32 n, char* name, size_t bufSize)
244{
245	BMidiEndpoint* endp = (BMidiEndpoint*)fDevices->ItemAt(n);
246	if (endp == NULL)
247		return B_BAD_VALUE;
248
249	size_t size = strlen(endp->Name());
250	if (size >= bufSize)
251		return B_NAME_TOO_LONG;
252
253	strcpy(name, endp->Name());
254	return B_OK;
255}
256
257
258void BMidiPort::_ReservedMidiPort1() { }
259void BMidiPort::_ReservedMidiPort2() { }
260void BMidiPort::_ReservedMidiPort3() { }
261
262
263void
264BMidiPort::Run()
265{
266	while (KeepRunning())
267		snooze(50000);
268}
269
270
271void
272BMidiPort::ScanDevices()
273{
274	EmptyDeviceList();
275
276	int32 id = 0;
277	BMidiEndpoint* endp;
278
279	while ((endp = BMidiRoster::NextEndpoint(&id)) != NULL) {
280		// Each hardware port has two endpoints associated with it, a consumer
281		// and a producer. Both have the same name, so we add only one of them.
282
283		bool addItem = true;
284		for (int32 t = 0; t < fDevices->CountItems(); ++t) {
285			BMidiEndpoint* other = (BMidiEndpoint*)fDevices->ItemAt(t);
286			if (strcmp(endp->Name(), other->Name()) == 0) {
287				addItem = false;
288				break;
289			}
290		}
291
292		if (addItem) {
293			fDevices->AddItem(endp);
294		} else {
295			endp->Release();
296		}
297	}
298}
299
300
301void
302BMidiPort::EmptyDeviceList()
303{
304	for (int32 t = 0; t < fDevices->CountItems(); ++t)
305		((BMidiEndpoint*)fDevices->ItemAt(t))->Release();
306
307	fDevices->MakeEmpty();
308}
309
310