1/*
2 * OpenSound media addon for BeOS and Haiku
3 *
4 * Copyright (c) 2007, Fran��ois Revol (revol@free.fr)
5 * Copyright (c) 2002, 2003 Jerome Duval (jerome.duval@free.fr)
6 * Distributed under the terms of the MIT License.
7 */
8
9
10#include "OpenSoundNode.h"
11
12#include <Autolock.h>
13#include <Buffer.h>
14#include <BufferGroup.h>
15#include <Debug.h>
16#include <MediaAddOn.h>
17#include <MediaRoster.h>
18#include <scheduler.h>
19#include <String.h>
20
21#include <new>
22#include <limits.h>
23#include <signal.h>
24#include <stdio.h>
25#include <string.h>
26
27#ifdef DEBUG
28  #define PRINTING
29#endif
30#include "debug.h"
31
32#include "OpenSoundDevice.h"
33#include "OpenSoundDeviceEngine.h"
34#include "OpenSoundDeviceMixer.h"
35#include "SupportFunctions.h"
36
37using std::nothrow;
38
39
40class FunctionTracer {
41public:
42	FunctionTracer(const char* functionName)
43		: fFunctionName(functionName)
44	{
45		printf("OpenSoundNode::%s()\n", fFunctionName.String());
46	}
47	 ~FunctionTracer()
48	{
49		printf("OpenSoundNode::%s() - leave\n", fFunctionName.String());
50	}
51	BString	fFunctionName;
52};
53
54
55// debugging
56#ifdef TRACE
57#	undef TRACE
58#endif
59#ifdef CALLED
60#	undef CALLED
61#endif
62//#define TRACE_OSS_NODE
63#ifdef TRACE_OSS_NODE
64#	define TRACE(x...)		printf(x)
65#	define CALLED(x...)		FunctionTracer _ft(__FUNCTION__)
66#	define PRINTING
67#else
68#	define TRACE(x...)
69#	define CALLED(x...)
70#endif
71
72
73class OpenSoundNode::NodeInput {
74public:
75	NodeInput(const media_input& input, int engineIndex, int ossFormatFlags,
76			OpenSoundNode* node)
77		: fNode(node),
78		  fEngineIndex(engineIndex),
79		  fRealEngine(NULL),
80		  fOSSFormatFlags(ossFormatFlags),
81
82		  fInput(input),
83		  fPreferredFormat(input.format),
84		  	// Keep a version of the original preferred format,
85		  	// in case we are re-connected and need to start "clean"
86
87		  fThread(-1),
88		  fBuffers(4),
89
90		  fTestTonePhase(0)
91	{
92		CALLED();
93
94		fInput.format.u.raw_audio.format
95			= media_raw_audio_format::wildcard.format;
96	}
97
98	~NodeInput()
99	{
100		CALLED();
101
102		fNode->_StopPlayThread(this);
103
104		RecycleAllBuffers();
105	}
106
107	status_t Write(void* data, size_t size)
108	{
109		CALLED();
110
111		ssize_t written = fRealEngine->Write(data, size);
112
113		if (written < 0)
114			return (status_t)written;
115		if (written < (ssize_t)size)
116			return B_IO_ERROR;
117
118		return B_OK;
119	}
120
121	void WriteTestTone(size_t bytes)
122	{
123		// phase of the sine wave
124		uint8 buffer[bytes];
125		float sampleRate = fInput.format.u.raw_audio.frame_rate;
126
127		const static int kSineBuffer[48] = {
128			0, 4276, 8480, 12539, 16383, 19947, 23169, 25995,
129			28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272,
130			28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276,
131			0, -4276, -8480, -12539, -16383, -19947, -23169, -25995,
132			-28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272,
133			-28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276
134		};
135
136		short* b = (short*)buffer;
137			// TODO: assumes 16 bit samples!
138		int32 channels = fInput.format.u.raw_audio.channel_count;
139		int32 frames = bytes / bytes_per_frame(fInput.format);
140		for (int32 i = 0; i < frames; i ++) {
141			// convert sample rate from 48000 to connected format
142			uint32 p = (uint32)((fTestTonePhase * sampleRate) / 48000);
143
144			// prevent phase from integer overflow
145			fTestTonePhase = (fTestTonePhase + 1) % 4800;
146			for (int32 k = 0; k < channels; k++)
147				b[k] = kSineBuffer[p % 48];
148			b += channels;
149		}
150
151		ssize_t written = fRealEngine->Write(buffer, bytes);
152		if (written != (ssize_t)bytes) {
153			// error
154		}
155	}
156
157	void RecycleAllBuffers()
158	{
159		CALLED();
160
161		// make sure all buffers are recycled, or we might hang
162		// when told to quit
163		while (BBuffer* buffer = (BBuffer*)fBuffers.RemoveItem((int32)0))
164			buffer->Recycle();
165	}
166
167	OpenSoundNode*			fNode;
168	int32					fEngineIndex;
169	OpenSoundDeviceEngine*	fRealEngine;
170		// engine it's connected to. can be a shadow one (!= fEngineIndex)
171	int						fOSSFormatFlags;
172		// AFMT_* flags for this input
173	media_input				fInput;
174	media_format 			fPreferredFormat;
175
176	thread_id				fThread;
177	BList					fBuffers;
178		// contains BBuffer* pointers that have not yet played
179
180	uint32					fTestTonePhase;
181};
182
183
184class OpenSoundNode::NodeOutput {
185public:
186	NodeOutput(const media_output& output, const media_format& format)
187		: fNode(NULL),
188		  fEngineIndex(-1),
189		  fRealEngine(NULL),
190		  fOSSFormatFlags(0),
191
192		  fOutput(output),
193		  fPreferredFormat(format),
194
195		  fThread(-1),
196		  fBufferGroup(NULL),
197		  fUsingOwnBufferGroup(true),
198		  fOutputEnabled(true),
199
200		  fSamplesSent(0)
201	{
202		CALLED();
203	}
204
205	~NodeOutput()
206	{
207		CALLED();
208
209		fNode->_StopRecThread(this);
210
211		FreeBuffers();
212	}
213
214	status_t AllocateBuffers(bigtime_t bufferDuration, bigtime_t latency)
215	{
216		TRACE("NodeOutput::AllocateBuffers(bufferDuration = %lld, "
217			"latency = %lld)\n", bufferDuration, latency);
218
219		FreeBuffers();
220
221		// allocate enough buffers to span our downstream latency, plus one
222		size_t size = fOutput.format.u.raw_audio.buffer_size;
223		int32 count = int32(latency / bufferDuration + 1 + 1);
224
225		fBufferGroup = new (nothrow) BBufferGroup(size, count);
226		fUsingOwnBufferGroup = true;
227		return fBufferGroup != NULL ? fBufferGroup->InitCheck() : B_NO_MEMORY;
228	}
229
230	status_t SetExternalBuffers(BBufferGroup* bufferGroup)
231	{
232		TRACE("NodeOutput::SetExternalBuffers(%p)\n", bufferGroup);
233
234		fBufferGroup = bufferGroup;
235		fUsingOwnBufferGroup = false;
236		return fBufferGroup->InitCheck();
237	}
238
239	void FreeBuffers()
240	{
241		TRACE("NodeOutput::FreeBuffers(): %p (own %d)\n", fBufferGroup,
242			fUsingOwnBufferGroup);
243// TODO: it is not clear to me how buffer group responsibility is supposed
244// to work properly. Appearantly, a consumer can use SetOutputBuffers(),
245// which is a deprecated call in the BeOS API, with "willReclaim == true".
246// In that case, we would not be responsible for deleting these buffers,
247// but I don't understand what mechanism makes sure that we know about this.
248// The documentation for SetBufferGroup() says you are supposed to delete
249// the given buffer group. In any case, the fUsingOwnBufferGroup is correclty
250// maintained as far as we are concerned, but I delete the buffers anyways,
251// which is what the code was doing from the beginning and that worked. I
252// have not tested yet, whether an external buffer group is passed to the node
253// from the system mixer.
254
255//		if (fUsingOwnBufferGroup)
256			delete fBufferGroup;
257		fBufferGroup = NULL;
258	}
259
260	BBuffer* FillNextBuffer(bigtime_t bufferDuration)
261	{
262		if (fBufferGroup == NULL)
263			return NULL;
264
265		BBuffer* buffer = fBufferGroup->RequestBuffer(
266			fOutput.format.u.raw_audio.buffer_size, bufferDuration);
267
268		// if we fail to get a buffer (for example, if the request times out),
269		// we skip this buffer and go on to the next, to avoid locking up the
270		// control thread
271		if (!buffer)
272			return NULL;
273
274		// now fill it with data
275		ssize_t sizeUsed = fRealEngine->Read(buffer->Data(),
276			fOutput.format.u.raw_audio.buffer_size);
277		if (sizeUsed < 0) {
278			TRACE("NodeOutput::%s: %s\n", __FUNCTION__,
279				strerror(sizeUsed));
280			buffer->Recycle();
281			return NULL;
282		}
283		if (sizeUsed < (ssize_t)fOutput.format.u.raw_audio.buffer_size) {
284			TRACE("NodeOutput::%s: requested %d, got %d\n", __FUNCTION__,
285				fOutput.format.u.raw_audio.buffer_size, sizeUsed);
286		}
287
288		media_header* hdr = buffer->Header();
289		if (hdr != NULL) {
290			hdr->type = B_MEDIA_RAW_AUDIO;
291			hdr->size_used = sizeUsed;
292		}
293
294		return buffer;
295	}
296
297	OpenSoundNode*			fNode;
298	int32					fEngineIndex;
299	OpenSoundDeviceEngine*	fRealEngine;
300		// engine it's connected to. can be a shadow one (!= fEngineIndex)
301	int						fOSSFormatFlags;
302		// AFMT_* flags for this output
303	media_output			fOutput;
304	media_format 			fPreferredFormat;
305
306	thread_id				fThread;
307	BBufferGroup*			fBufferGroup;
308	bool					fUsingOwnBufferGroup;
309	bool 					fOutputEnabled;
310	uint64 					fSamplesSent;
311};
312
313
314// #pragma mark - OpenSoundNode
315
316
317OpenSoundNode::OpenSoundNode(BMediaAddOn* addon, const char* name,
318		OpenSoundDevice* device, int32 internal_id, BMessage* config)
319	: BMediaNode(name),
320	  BBufferConsumer(B_MEDIA_RAW_AUDIO),
321	  BBufferProducer(B_MEDIA_RAW_AUDIO),
322	  BTimeSource(),
323	  BMediaEventLooper(),
324
325	  fInitCheckStatus(B_NO_INIT),
326	  fDevice(device),
327
328	  fTimeSourceStarted(false),
329	  fTimeSourceStartTime(0),
330
331	  fWeb(NULL),
332	  fConfig((uint32)0)
333{
334	CALLED();
335
336	if (fDevice == NULL)
337		return;
338
339	fAddOn = addon;
340	fId = internal_id;
341
342	AddNodeKind(B_PHYSICAL_OUTPUT);
343	AddNodeKind(B_PHYSICAL_INPUT);
344
345	// initialize our preferred format object
346	// TODO: this should go away! should use engine's preferred for each afmt.
347#if 1
348	fPreferredFormat = media_format();
349		// set everything to wildcard first
350	fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
351	fPreferredFormat.u.raw_audio = media_multi_audio_format::wildcard;
352	fPreferredFormat.u.raw_audio.channel_count = 2;
353	fPreferredFormat.u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;
354	OpenSoundDeviceEngine *engine = fDevice->EngineAt(0);
355	if (engine) {
356		const oss_audioinfo* ai = engine->Info();
357		int fmt = OpenSoundDevice::select_oss_format(ai->oformats);
358		fPreferredFormat.u.raw_audio.format
359			= OpenSoundDevice::convert_oss_format_to_media_format(fmt);
360		fPreferredFormat.u.raw_audio.valid_bits
361			= OpenSoundDevice::convert_oss_format_to_valid_bits(fmt);
362		// TODO: engine->PreferredChannels() ? (caps & DSP_CH*)
363		fPreferredFormat.u.raw_audio.frame_rate
364			= OpenSoundDevice::convert_oss_rate_to_media_rate(ai->max_rate);		// measured in Hertz
365	}
366
367	// TODO: Use the OSS suggested buffer size via SNDCTL_DSP_GETBLKSIZE ?
368	fPreferredFormat.u.raw_audio.buffer_size = DEFAULT_BUFFER_SIZE
369		* (fPreferredFormat.u.raw_audio.format
370			& media_raw_audio_format::B_AUDIO_SIZE_MASK)
371		* fPreferredFormat.u.raw_audio.channel_count;
372#endif
373
374	if (config != NULL) {
375		fConfig = *config;
376		PRINT_OBJECT(fConfig);
377	}
378
379	fInitCheckStatus = B_OK;
380}
381
382
383OpenSoundNode::~OpenSoundNode()
384{
385	CALLED();
386
387	fAddOn->GetConfigurationFor(this, NULL);
388
389	int32 count = fInputs.CountItems();
390	for (int32 i = 0; i < count; i++)
391		delete (NodeInput*)fInputs.ItemAtFast(i);
392	count = fOutputs.CountItems();
393	for (int32 i = 0; i < count; i++)
394		delete (NodeOutput*)fOutputs.ItemAtFast(i);
395
396	BMediaEventLooper::Quit();
397
398	fWeb = NULL;
399}
400
401
402status_t
403OpenSoundNode::InitCheck() const
404{
405	CALLED();
406	return fInitCheckStatus;
407}
408
409
410// #pragma mark - BMediaNode
411
412
413BMediaAddOn*
414OpenSoundNode::AddOn(int32* internal_id) const
415{
416	CALLED();
417	// BeBook says this only gets called if we were in an add-on.
418	if (fAddOn != 0) {
419		// If we get a null pointer then we just won't write.
420		if (internal_id != 0) {
421			*internal_id = fId;
422		}
423	}
424	return fAddOn;
425}
426
427
428void
429OpenSoundNode::Preroll()
430{
431	CALLED();
432	// XXX:Performance opportunity
433	BMediaNode::Preroll();
434}
435
436
437status_t
438OpenSoundNode::HandleMessage(int32 message, const void* data, size_t size)
439{
440	CALLED();
441	return B_ERROR;
442}
443
444
445void
446OpenSoundNode::NodeRegistered()
447{
448	CALLED();
449
450	if (fInitCheckStatus != B_OK) {
451		ReportError(B_NODE_IN_DISTRESS);
452		return;
453	}
454
455	TRACE("NodeRegistered: %d engines\n", fDevice->CountEngines());
456	for (int32 i = 0; i < fDevice->CountEngines(); i++) {
457		OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
458		if (engine == NULL)
459			continue;
460		// skip shadow engines
461		if (engine->Caps() & PCM_CAP_SHADOW)
462			continue;
463		// skip engines that don't have outputs
464		if ((engine->Caps() & PCM_CAP_OUTPUT) == 0)
465			continue;
466
467		TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .oformats=0x%08x\n",
468			i, engine->Caps(), engine->Info()->oformats);
469
470		// iterate over all possible OSS formats/encodings and
471		// create a NodeInput for each
472		for (int32 f = 0; gSupportedFormats[f]; f++) {
473			// figure out if the engine supports the given format
474			int fmt = gSupportedFormats[f] & engine->Info()->oformats;
475			if (fmt == 0)
476				continue;
477			TRACE("NodeRegistered() : creating an input for engine %i, "
478				"format[%i]\n", i, f);
479
480			media_input mediaInput;
481			status_t err = engine->PreferredFormatFor(fmt, mediaInput.format);
482			if (err < B_OK)
483				continue;
484
485			mediaInput.destination.port = ControlPort();
486			mediaInput.destination.id = fInputs.CountItems();
487			mediaInput.node = Node();
488			const char *prefix = "";
489			if (strstr(engine->Info()->name, "SPDIF"))
490				prefix = "S/PDIF ";
491			sprintf(mediaInput.name, "%sOutput %" B_PRId32 " (%s)", prefix,
492				mediaInput.destination.id, gSupportedFormatsNames[f]);
493
494			NodeInput* input = new (nothrow) NodeInput(mediaInput, i, fmt,
495				this);
496			if (input == NULL || !fInputs.AddItem(input)) {
497				delete input;
498				continue;
499			}
500		}
501	}
502
503	for (int32 i = 0; i < fDevice->CountEngines(); i++) {
504		OpenSoundDeviceEngine* engine = fDevice->EngineAt(i);
505		if (engine == NULL)
506			continue;
507		// skip shadow engines
508		if (engine->Caps() & PCM_CAP_SHADOW)
509			continue;
510		// skip engines that don't have inputs
511		if ((engine->Caps() & PCM_CAP_INPUT) == 0)
512			continue;
513
514		TRACE("NodeRegistered: engine[%d]: .caps=0x%08x, .iformats=0x%08x\n",
515			i, engine->Caps(), engine->Info()->iformats);
516
517		for (int32 f = 0; gSupportedFormats[f]; f++) {
518			int fmt = gSupportedFormats[f] & engine->Info()->iformats;
519			if (fmt == 0)
520				continue;
521			TRACE("NodeRegistered() : creating an output for engine %i, "
522				"format[%i]\n", i, f);
523
524			media_format preferredFormat;
525			status_t err = engine->PreferredFormatFor(fmt, preferredFormat);
526			if (err < B_OK)
527				continue;
528
529			media_output mediaOutput;
530
531			mediaOutput.format = preferredFormat;
532			mediaOutput.destination = media_destination::null;
533			mediaOutput.source.port = ControlPort();
534			mediaOutput.source.id = fOutputs.CountItems();
535			mediaOutput.node = Node();
536			const char *prefix = "";
537			if (strstr(engine->Info()->name, "SPDIF"))
538				prefix = "S/PDIF ";
539			sprintf(mediaOutput.name, "%sInput %" B_PRId32 " (%s)", prefix,
540				mediaOutput.source.id, gSupportedFormatsNames[f]);
541
542			NodeOutput* output = new (nothrow) NodeOutput(mediaOutput,
543				preferredFormat);
544			if (output == NULL || !fOutputs.AddItem(output)) {
545				delete output;
546				continue;
547			}
548//			output->fPreferredFormat.u.raw_audio.channel_count
549//				= engine->Info()->max_channels;
550			output->fOutput.format = output->fPreferredFormat;
551			output->fEngineIndex = i;
552			output->fOSSFormatFlags = fmt;
553			output->fNode = this;
554		}
555	}
556
557	// set up our parameter web
558	fWeb = MakeParameterWeb();
559	SetParameterWeb(fWeb);
560
561	// apply configuration
562#ifdef TRACE_OSS_NODE
563	bigtime_t start = system_time();
564#endif
565
566	int32 index = 0;
567	int32 parameterID = 0;
568	while (fConfig.FindInt32("parameterID", index, &parameterID) == B_OK) {
569		const void* data;
570		ssize_t size;
571		if (fConfig.FindData("parameterData", B_RAW_TYPE, index, &data,
572			&size) == B_OK) {
573			SetParameterValue(parameterID, TimeSource()->Now(), data, size);
574		}
575		index++;
576	}
577
578	TRACE("apply configuration in : %lld��s\n", system_time() - start);
579
580	SetPriority(B_REAL_TIME_PRIORITY);
581	Run();
582}
583
584
585status_t
586OpenSoundNode::RequestCompleted(const media_request_info& info)
587{
588	CALLED();
589	return B_OK;
590}
591
592
593void
594OpenSoundNode::SetTimeSource(BTimeSource* timeSource)
595{
596	CALLED();
597}
598
599
600// #pragma mark - BBufferConsumer
601
602
603//!	Someone, probably the producer, is asking you about this format.
604//	Give your honest opinion, possibly modifying *format. Do not ask
605//	upstream producer about the format, since he's synchronously
606//	waiting for your reply.
607status_t
608OpenSoundNode::AcceptFormat(const media_destination& dest,
609	media_format* format)
610{
611	// Check to make sure the format is okay, then remove
612	// any wildcards corresponding to our requirements.
613
614	CALLED();
615
616	NodeInput* channel = _FindInput(dest);
617
618	if (channel == NULL) {
619		fprintf(stderr, "OpenSoundNode::AcceptFormat()"
620			" - B_MEDIA_BAD_DESTINATION");
621		return B_MEDIA_BAD_DESTINATION;
622			// we only have one input so that better be it
623	}
624
625	if (format == NULL) {
626		fprintf(stderr, "OpenSoundNode::AcceptFormat() - B_BAD_VALUE\n");
627		return B_BAD_VALUE;
628	}
629
630/*	media_format * myFormat = GetFormat();
631	fprintf(stderr,"proposed format: ");
632	print_media_format(format);
633	fprintf(stderr,"\n");
634	fprintf(stderr,"my format: ");
635	print_media_format(myFormat);
636	fprintf(stderr,"\n");*/
637	// Be's format_is_compatible doesn't work.
638//	if (!format_is_compatible(*format,*myFormat)) {
639
640	BAutolock L(fDevice->Locker());
641
642	OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
643		channel->fEngineIndex, false);
644	if (!engine)
645		return B_BUSY;
646
647	status_t err = engine->AcceptFormatFor(channel->fOSSFormatFlags, *format,
648		false);
649	if (err < B_OK)
650		return err;
651
652	channel->fRealEngine = engine;
653
654/*
655	if ( format->type != B_MEDIA_RAW_AUDIO ) {
656		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
657		return B_MEDIA_BAD_FORMAT;
658	}
659*/
660
661	//channel->fInput.format = channel->fPreferredFormat;
662	channel->fInput.format = *format;
663
664	/*if(format->u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT
665		&& channel->fPreferredFormat.u.raw_audio.format
666			== media_raw_audio_format::B_AUDIO_SHORT)
667		format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
668	else*/
669/*
670	format->u.raw_audio.format = channel->fPreferredFormat.u.raw_audio.format;
671	format->u.raw_audio.valid_bits
672		= channel->fPreferredFormat.u.raw_audio.valid_bits;
673
674	format->u.raw_audio.frame_rate
675		= channel->fPreferredFormat.u.raw_audio.frame_rate;
676	format->u.raw_audio.channel_count
677		= channel->fPreferredFormat.u.raw_audio.channel_count;
678	format->u.raw_audio.byte_order
679		= B_MEDIA_HOST_ENDIAN;
680
681	format->u.raw_audio.buffer_size
682		= DEFAULT_BUFFER_SIZE
683			* (format->u.raw_audio.format
684				& media_raw_audio_format::B_AUDIO_SIZE_MASK)
685			* format->u.raw_audio.channel_count;
686*/
687
688//	media_format myFormat;
689//	GetFormat(&myFormat);
690//	if (!format_is_acceptible(*format,myFormat)) {
691//		fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n");
692//		return B_MEDIA_BAD_FORMAT;
693//	}
694
695	return B_OK;
696}
697
698
699status_t
700OpenSoundNode::GetNextInput(int32* cookie, media_input* out_input)
701{
702	CALLED();
703
704	// let's not crash even if they are stupid
705	if (out_input == NULL) {
706		// no place to write!
707		fprintf(stderr,"OpenSoundNode::GetNextInput() - B_BAD_VALUE\n");
708		return B_BAD_VALUE;
709	}
710
711	if (*cookie >= fInputs.CountItems() || *cookie < 0)
712		return B_BAD_INDEX;
713
714	NodeInput* channel = (NodeInput*)fInputs.ItemAt(*cookie);
715	*out_input = channel->fInput;
716	*cookie += 1;
717
718	TRACE("input.format : %u\n", channel->fInput.format.u.raw_audio.format);
719
720	return B_OK;
721}
722
723
724void
725OpenSoundNode::DisposeInputCookie(int32 cookie)
726{
727	CALLED();
728	// nothing to do since our cookies are just integers
729}
730
731
732void
733OpenSoundNode::BufferReceived(BBuffer* buffer)
734{
735	CALLED();
736
737	switch (buffer->Header()->type) {
738//		case B_MEDIA_PARAMETERS:
739//		{
740//			status_t status = ApplyParameterData(buffer->Data(),
741//				buffer->SizeUsed());
742//			if (status != B_OK) {
743//				fprintf(stderr, "ApplyParameterData() in "
744//					"OpenSoundNode::BufferReceived() failed: %s\n",
745//					strerror(status));
746//			}
747//			buffer->Recycle();
748//			break;
749//		}
750		case B_MEDIA_RAW_AUDIO:
751			if (buffer->Flags() & BBuffer::B_SMALL_BUFFER) {
752				fprintf(stderr, "OpenSoundNode::BufferReceived() - "
753					"B_SMALL_BUFFER not implemented\n");
754				// TODO: implement this part
755				buffer->Recycle();
756			} else {
757				media_timed_event event(buffer->Header()->start_time,
758					BTimedEventQueue::B_HANDLE_BUFFER, buffer,
759					BTimedEventQueue::B_RECYCLE_BUFFER);
760				status_t status = EventQueue()->AddEvent(event);
761				if (status != B_OK) {
762					fprintf(stderr, "OpenSoundNode::BufferReceived() - "
763						"EventQueue()->AddEvent() failed: %s\n",
764						strerror(status));
765					buffer->Recycle();
766				}
767			}
768			break;
769		default:
770			fprintf(stderr, "OpenSoundNode::BufferReceived() - unexpected "
771				"buffer type\n");
772			buffer->Recycle();
773			break;
774	}
775}
776
777
778void
779OpenSoundNode::ProducerDataStatus(const media_destination& for_whom,
780	int32 status, bigtime_t at_performance_time)
781{
782	CALLED();
783
784	NodeInput* channel = _FindInput(for_whom);
785
786	if (channel == NULL) {
787		fprintf(stderr,"OpenSoundNode::ProducerDataStatus() - "
788			"invalid destination\n");
789		return;
790	}
791
792//	TRACE("************ ProducerDataStatus: queuing event ************\n");
793//	TRACE("************ status=%d ************\n", status);
794
795	media_timed_event event(at_performance_time,
796		BTimedEventQueue::B_DATA_STATUS, &channel->fInput,
797		BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
798	EventQueue()->AddEvent(event);
799}
800
801
802status_t
803OpenSoundNode::GetLatencyFor(const media_destination& for_whom,
804	bigtime_t* out_latency, media_node_id* out_timesource)
805{
806	CALLED();
807
808	if (out_latency == NULL || out_timesource == NULL) {
809		fprintf(stderr,"OpenSoundNode::GetLatencyFor() - B_BAD_VALUE\n");
810		return B_BAD_VALUE;
811	}
812
813	NodeInput* channel = _FindInput(for_whom);
814
815	if (channel == NULL || channel->fRealEngine == NULL) {
816		fprintf(stderr,"OpenSoundNode::GetLatencyFor() - "
817			"B_MEDIA_BAD_DESTINATION\n");
818		return B_MEDIA_BAD_DESTINATION;
819	}
820
821	// start with the node latency (1 buffer duration)
822	*out_latency = EventLatency();
823
824	// add the OSS driver buffer's latency as well
825	bigtime_t bufferLatency = channel->fRealEngine->PlaybackLatency();
826	*out_latency += bufferLatency;
827
828	TRACE("OpenSoundNode::GetLatencyFor() - EventLatency %lld, OSS %lld\n",
829		EventLatency(), bufferLatency);
830
831	*out_timesource = TimeSource()->ID();
832
833	return B_OK;
834}
835
836
837status_t
838OpenSoundNode::Connected(const media_source& producer,
839	const media_destination& where, const media_format& with_format,
840	media_input* out_input)
841{
842	CALLED();
843
844	if (out_input == NULL) {
845		fprintf(stderr,"OpenSoundNode::Connected() - B_BAD_VALUE\n");
846		return B_BAD_VALUE;
847	}
848
849	NodeInput* channel = _FindInput(where);
850
851	if (channel == NULL) {
852		fprintf(stderr,"OpenSoundNode::Connected() - "
853			"B_MEDIA_BAD_DESTINATION\n");
854		return B_MEDIA_BAD_DESTINATION;
855	}
856
857	BAutolock L(fDevice->Locker());
858
859	// use one half buffer length latency
860	size_t bufferSize = channel->fRealEngine->DriverBufferSize() / 2;
861	fInternalLatency = time_for_buffer(bufferSize, with_format);
862	TRACE("  internal latency = %lld\n", fInternalLatency);
863
864	// TODO: A global node value is assigned a channel specific value!
865	// That can't be correct. For as long as there is only one output
866	// in use at a time, this will not matter of course.
867	SetEventLatency(fInternalLatency);
868
869	// record the agreed upon values
870	channel->fInput.source = producer;
871	channel->fInput.format = with_format;
872
873	*out_input = channel->fInput;
874
875	// we are sure the thread is started
876	_StartPlayThread(channel);
877
878	return B_OK;
879}
880
881
882void
883OpenSoundNode::Disconnected(const media_source& producer,
884	const media_destination& where)
885{
886	CALLED();
887
888	NodeInput* channel = _FindInput(where);
889	if (channel == NULL) {
890		fprintf(stderr,"OpenSoundNode::Disconnected() - "
891			"B_MEDIA_BAD_DESTINATION\n");
892		return;
893	}
894	if (channel->fInput.source != producer) {
895		fprintf(stderr,"OpenSoundNode::Disconnected() - "
896			"B_MEDIA_BAD_SOURCE\n");
897		return;
898	}
899
900	_StopPlayThread(channel);
901
902	channel->RecycleAllBuffers();
903
904	channel->fInput.source = media_source::null;
905	channel->fInput.format = channel->fPreferredFormat;
906	if (channel->fRealEngine)
907		channel->fRealEngine->Close();
908	channel->fRealEngine = NULL;
909}
910
911
912//! The notification comes from the upstream producer, so he's
913//	already cool with the format; you should not ask him about it
914//	in here.
915status_t
916OpenSoundNode::FormatChanged(const media_source& producer,
917	const media_destination& consumer,  int32 change_tag,
918	const media_format& format)
919{
920	CALLED();
921	NodeInput* channel = _FindInput(consumer);
922
923	if (channel == NULL) {
924		fprintf(stderr,"OpenSoundNode::FormatChanged() - "
925			"B_MEDIA_BAD_DESTINATION\n");
926		return B_MEDIA_BAD_DESTINATION;
927	}
928
929	if (channel->fInput.source != producer) {
930		fprintf(stderr,"OpenSoundNode::FormatChanged() - "
931			"B_MEDIA_BAD_SOURCE\n");
932		return B_MEDIA_BAD_SOURCE;
933	}
934
935	// currently not supported, TODO: implement?
936	return B_ERROR;
937}
938
939
940//!	Given a performance time of some previous buffer, retrieve the
941//	remembered tag of the closest (previous or exact) performance
942//	time. Set *out_flags to 0; the idea being that flags can be
943//	added later, and the understood flags returned in *out_flags.
944status_t
945OpenSoundNode::SeekTagRequested(const media_destination& destination,
946	bigtime_t in_target_time, uint32 in_flags, media_seek_tag* out_seek_tag,
947	bigtime_t* out_tagged_time, uint32* out_flags)
948{
949	CALLED();
950	return BBufferConsumer::SeekTagRequested(destination, in_target_time,
951		in_flags, out_seek_tag, out_tagged_time, out_flags);
952}
953
954
955// #pragma mark - BBufferProducer
956
957
958//!	FormatSuggestionRequested() is not necessarily part of the format
959//	negotiation process; it's simply an interrogation -- the caller wants
960//	to see what the node's preferred data format is, given a suggestion by
961//	the caller.
962status_t
963OpenSoundNode::FormatSuggestionRequested(media_type type, int32 /*quality*/,
964	media_format* format)
965{
966	CALLED();
967
968	if (format == NULL) {
969		fprintf(stderr, "\tERROR - NULL format pointer passed in!\n");
970		return B_BAD_VALUE;
971	}
972
973	// this is the format we'll be returning (our preferred format)
974	*format = fPreferredFormat;
975
976	// a wildcard type is okay; we can specialize it
977	if (type == B_MEDIA_UNKNOWN_TYPE)
978		type = B_MEDIA_RAW_AUDIO;
979
980	// TODO: For OSS engines that support encoded formats, we could
981	// handle this here. For the time being, we only support raw audio.
982	if (type != B_MEDIA_RAW_AUDIO)
983		return B_MEDIA_BAD_FORMAT;
984
985	return B_OK;
986}
987
988
989//!	FormatProposal() is the first stage in the BMediaRoster::Connect()
990//	process.  We hand out a suggested format, with wildcards for any
991//	variations we support.
992status_t
993OpenSoundNode::FormatProposal(const media_source& output, media_format* format)
994{
995	CALLED();
996
997	NodeOutput* channel = _FindOutput(output);
998
999	// is this a proposal for our select output?
1000	if (channel == NULL) {
1001		fprintf(stderr, "OpenSoundNode::FormatProposal returning "
1002			"B_MEDIA_BAD_SOURCE\n");
1003		return B_MEDIA_BAD_SOURCE;
1004	}
1005
1006	media_type requestedType = format->type;
1007#ifdef ENABLE_REC
1008	OpenSoundDeviceEngine* engine = fDevice->NextFreeEngineAt(
1009		channel->fEngineIndex, true);
1010
1011	// We only support raw audio, so we always return that, but we supply an
1012	// error code depending on whether we found the proposal acceptable.
1013	status_t err = engine->PreferredFormatFor(channel->fOSSFormatFlags, *format, true);
1014	if (err < B_OK)
1015		return err;
1016#else
1017	*format = fPreferredFormat;
1018#endif
1019	if (requestedType != B_MEDIA_UNKNOWN_TYPE
1020		&& requestedType != B_MEDIA_RAW_AUDIO
1021#ifdef ENABLE_NON_RAW_SUPPORT
1022		 && requestedType != B_MEDIA_ENCODED_AUDIO
1023#endif
1024		)
1025	{
1026		fprintf(stderr, "OpenSoundNode::FormatProposal returning "
1027			"B_MEDIA_BAD_FORMAT\n");
1028		return B_MEDIA_BAD_FORMAT;
1029	}
1030
1031	// raw audio or wildcard type, either is okay by us
1032	return B_OK;
1033}
1034
1035
1036status_t
1037OpenSoundNode::FormatChangeRequested(const media_source& source,
1038	const media_destination& destination, media_format* io_format,
1039	int32* _deprecated_)
1040{
1041	CALLED();
1042
1043	// we don't support any other formats, so we just reject any format
1044	// changes. TODO: implement?
1045	return B_ERROR;
1046}
1047
1048
1049status_t
1050OpenSoundNode::GetNextOutput(int32* cookie, media_output* out_output)
1051{
1052	CALLED();
1053
1054	if (*cookie >= fOutputs.CountItems() || *cookie < 0)
1055		return B_BAD_INDEX;
1056
1057	NodeOutput *channel = (NodeOutput*)fOutputs.ItemAt(*cookie);
1058	*out_output = channel->fOutput;
1059	*cookie += 1;
1060	return B_OK;
1061}
1062
1063
1064status_t
1065OpenSoundNode::DisposeOutputCookie(int32 cookie)
1066{
1067	CALLED();
1068	// do nothing because we don't use the cookie for anything special
1069	return B_OK;
1070}
1071
1072
1073status_t
1074OpenSoundNode::SetBufferGroup(const media_source& for_source,
1075	BBufferGroup* newGroup)
1076{
1077	CALLED();
1078
1079	NodeOutput* channel = _FindOutput(for_source);
1080
1081	// is this our output?
1082	if (channel == NULL) {
1083		fprintf(stderr, "OpenSoundNode::SetBufferGroup() returning "
1084			"B_MEDIA_BAD_SOURCE\n");
1085		return B_MEDIA_BAD_SOURCE;
1086	}
1087
1088	// Are we being passed the buffer group we're already using?
1089	if (newGroup == channel->fBufferGroup)
1090		return B_OK;
1091
1092	// Ahh, someone wants us to use a different buffer group.  At this point
1093	// we delete the one we are using and use the specified one instead.  If
1094	// the specified group is NULL, we need to recreate one ourselves, and
1095	// use *that*.  Note that if we're caching a BBuffer that we requested
1096	// earlier, we have to Recycle() that buffer *before* deleting the buffer
1097	// group, otherwise we'll deadlock waiting for that buffer to be recycled!
1098	channel->FreeBuffers();
1099		// waits for all buffers to recycle
1100	if (newGroup != NULL) {
1101		// we were given a valid group; just use that one from now on
1102		return channel->SetExternalBuffers(newGroup);
1103	} else {
1104		// we were passed a NULL group pointer; that means we construct
1105		// our own buffer group to use from now on
1106		return channel->AllocateBuffers(BufferDuration(), fLatency);
1107	}
1108}
1109
1110
1111//!	PrepareToConnect() is the second stage of format negotiations that happens
1112//	inside BMediaRoster::Connect().  At this point, the consumer's
1113//	AcceptFormat() method has been called, and that node has potentially
1114//	changed the proposed format.  It may also have left wildcards in the
1115//	format.  PrepareToConnect() *must* fully specialize the format before
1116//	returning!
1117status_t
1118OpenSoundNode::PrepareToConnect(const media_source& what,
1119	const media_destination& where, media_format* format,
1120	media_source* out_source, char* out_name)
1121{
1122	CALLED();
1123
1124	status_t err;
1125	NodeOutput *channel = _FindOutput(what);
1126
1127	// is this our output?
1128	if (channel == NULL) {
1129		fprintf(stderr, "OpenSoundNode::PrepareToConnect returning "
1130			"B_MEDIA_BAD_SOURCE\n");
1131		return B_MEDIA_BAD_SOURCE;
1132	}
1133
1134	// are we already connected?
1135	if (channel->fOutput.destination != media_destination::null)
1136		return B_MEDIA_ALREADY_CONNECTED;
1137
1138	BAutolock L(fDevice->Locker());
1139
1140	OpenSoundDeviceEngine *engine = fDevice->NextFreeEngineAt(
1141		channel->fEngineIndex, false);
1142	if (engine == NULL)
1143		return B_BUSY;
1144
1145	// the format may not yet be fully specialized (the consumer might have
1146	// passed back some wildcards).  Finish specializing it now, and return an
1147	// error if we don't support the requested format.
1148	if (format->type != B_MEDIA_RAW_AUDIO) {
1149		fprintf(stderr, "\tnon-raw-audio format?!\n");
1150		return B_MEDIA_BAD_FORMAT;
1151	}
1152
1153	// !!! validate all other fields except for buffer_size here, because the
1154	// consumer might have supplied different values from AcceptFormat()?
1155	err = engine->SpecializeFormatFor(channel->fOSSFormatFlags, *format, true);
1156	if (err < B_OK)
1157		return err;
1158
1159	channel->fRealEngine = engine;
1160
1161#if 0
1162	// check the buffer size, which may still be wildcarded
1163	if (format->u.raw_audio.buffer_size
1164		== media_raw_audio_format::wildcard.buffer_size) {
1165		format->u.raw_audio.buffer_size = 2048;
1166			// pick something comfortable to suggest
1167		fprintf(stderr, "\tno buffer size provided, suggesting %lu\n",
1168			format->u.raw_audio.buffer_size);
1169	} else {
1170		fprintf(stderr, "\tconsumer suggested buffer_size %lu\n",
1171			format->u.raw_audio.buffer_size);
1172	}
1173#endif
1174
1175	// Now reserve the connection, and return information about it
1176	channel->fOutput.destination = where;
1177	channel->fOutput.format = *format;
1178	*out_source = channel->fOutput.source;
1179	strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1180	return B_OK;
1181}
1182
1183
1184void
1185OpenSoundNode::Connect(status_t error, const media_source& source,
1186	const media_destination& destination, const media_format& format,
1187	char* io_name)
1188{
1189	CALLED();
1190
1191	NodeOutput *channel = _FindOutput(source);
1192
1193	// is this our output?
1194	if (channel == NULL) {
1195		fprintf(stderr, "OpenSoundNode::Connect returning (cause : "
1196			"B_MEDIA_BAD_SOURCE)\n");
1197		return;
1198	}
1199
1200	OpenSoundDeviceEngine *engine = channel->fRealEngine;
1201	if (engine == NULL)
1202		return;
1203
1204	// If something earlier failed, Connect() might still be called, but with
1205	// a non-zero error code.  When that happens we simply unreserve the
1206	// connection and do nothing else.
1207	if (error) {
1208		channel->fOutput.destination = media_destination::null;
1209		channel->fOutput.format = channel->fPreferredFormat;
1210		engine->Close();
1211		channel->fRealEngine = NULL;
1212		return;
1213	}
1214
1215	// Okay, the connection has been confirmed.  Record the destination and
1216	// format that we agreed on, and report our connection name again.
1217	channel->fOutput.destination = destination;
1218	channel->fOutput.format = format;
1219	strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH);
1220
1221	// reset our buffer duration, etc. to avoid later calculations
1222	bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size
1223		* 10000
1224		/ ( (channel->fOutput.format.u.raw_audio.format
1225				& media_raw_audio_format::B_AUDIO_SIZE_MASK)
1226			* channel->fOutput.format.u.raw_audio.channel_count)
1227		/ ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100));
1228
1229	SetBufferDuration(duration);
1230
1231	// Now that we're connected, we can determine our downstream latency.
1232	// Do so, then make sure we get our events early enough.
1233	media_node_id id;
1234	FindLatencyFor(channel->fOutput.destination, &fLatency, &id);
1235	TRACE("\tdownstream latency = %Ld\n", fLatency);
1236
1237	fInternalLatency = BufferDuration();
1238	TRACE("\tbuffer-filling took %Ld usec on this machine\n",
1239		fInternalLatency);
1240	//SetEventLatency(fLatency + fInternalLatency);
1241
1242	// Set up the buffer group for our connection, as long as nobody handed us
1243	// a buffer group (via SetBufferGroup()) prior to this.  That can happen,
1244	// for example, if the consumer calls SetOutputBuffersFor() on us from
1245	// within its Connected() method.
1246	if (channel->fBufferGroup == NULL)
1247		channel->AllocateBuffers(BufferDuration(), fLatency);
1248
1249	engine->StartRecording();
1250
1251	// we are sure the thread is started
1252	_StartRecThread(channel);
1253}
1254
1255
1256void
1257OpenSoundNode::Disconnect(const media_source& what,
1258	const media_destination& where)
1259{
1260	CALLED();
1261
1262	NodeOutput *channel = _FindOutput(what);
1263
1264	// is this our output?
1265	if (channel == NULL) {
1266		fprintf(stderr, "OpenSoundNode::Disconnect() returning (cause : "
1267			"B_MEDIA_BAD_SOURCE)\n");
1268		return;
1269	}
1270
1271	_StopRecThread(channel);
1272
1273	BAutolock L(fDevice->Locker());
1274
1275	OpenSoundDeviceEngine* engine = channel->fRealEngine;
1276
1277	// Make sure that our connection is the one being disconnected
1278	if (where == channel->fOutput.destination
1279		&& what == channel->fOutput.source) {
1280		if (engine)
1281			engine->Close();
1282		channel->fRealEngine = NULL;
1283		channel->fOutput.destination = media_destination::null;
1284		channel->fOutput.format = channel->fPreferredFormat;
1285		channel->FreeBuffers();
1286	} else {
1287		fprintf(stderr, "\tDisconnect() called with wrong source/destination "
1288			"(%" B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32
1289			")\n", what.id, where.id, channel->fOutput.source.id,
1290			channel->fOutput.destination.id);
1291	}
1292}
1293
1294
1295void
1296OpenSoundNode::LateNoticeReceived(const media_source& what, bigtime_t how_much,
1297	bigtime_t performance_time)
1298{
1299	CALLED();
1300
1301	// is this our output?
1302	NodeOutput *channel = _FindOutput(what);
1303	if (channel == NULL)
1304		return;
1305
1306	// If we're late, we need to catch up.  Respond in a manner appropriate
1307	// to our current run mode.
1308	if (RunMode() == B_RECORDING) {
1309		// A hardware capture node can't adjust; it simply emits buffers at
1310		// appropriate points.  We (partially) simulate this by not adjusting
1311		// our behavior upon receiving late notices -- after all, the hardware
1312		// can't choose to capture "sooner"....
1313	} else if (RunMode() == B_INCREASE_LATENCY) {
1314		// We're late, and our run mode dictates that we try to produce buffers
1315		// earlier in order to catch up.  This argues that the downstream nodes
1316		// are not properly reporting their latency, but there's not much we
1317		// can do about that at the moment, so we try to start producing
1318		// buffers earlier to compensate.
1319		fInternalLatency += how_much;
1320		SetEventLatency(fLatency + fInternalLatency);
1321
1322		fprintf(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n",
1323			fLatency + fInternalLatency);
1324	} else {
1325		// The other run modes dictate various strategies for sacrificing data
1326		// quality in the interests of timely data delivery.  The way *we* do
1327		// this is to skip a buffer, which catches us up in time by one buffer
1328		// duration.
1329//		size_t nSamples = fOutput.format.u.raw_audio.buffer_size
1330//			/ sizeof(float);
1331//		mSamplesSent += nSamples;
1332
1333		fprintf(stderr, "\tskipping a buffer to try to catch up\n");
1334	}
1335}
1336
1337
1338void
1339OpenSoundNode::EnableOutput(const media_source& what, bool enabled,
1340	int32* _deprecated_)
1341{
1342	CALLED();
1343
1344	// If I had more than one output, I'd have to walk my list of output records to see
1345	// which one matched the given source, and then enable/disable that one.  But this
1346	// node only has one output, so I just make sure the given source matches, then set
1347	// the enable state accordingly.
1348	NodeOutput *channel = _FindOutput(what);
1349
1350	if (channel != NULL)
1351	{
1352		channel->fOutputEnabled = enabled;
1353	}
1354}
1355
1356void
1357OpenSoundNode::AdditionalBufferRequested(const media_source& source,
1358	media_buffer_id prev_buffer, bigtime_t prev_time,
1359	const media_seek_tag* prev_tag)
1360{
1361	CALLED();
1362	// we don't support offline mode
1363	return;
1364}
1365
1366
1367// #pragma mark - BMediaEventLooper
1368
1369
1370void
1371OpenSoundNode::HandleEvent(const media_timed_event* event, bigtime_t lateness,
1372	bool realTimeEvent)
1373{
1374	CALLED();
1375
1376	switch (event->type) {
1377		case BTimedEventQueue::B_START:
1378			HandleStart(event,lateness,realTimeEvent);
1379			break;
1380		case BTimedEventQueue::B_SEEK:
1381			HandleSeek(event,lateness,realTimeEvent);
1382			break;
1383		case BTimedEventQueue::B_WARP:
1384			HandleWarp(event,lateness,realTimeEvent);
1385			break;
1386		case BTimedEventQueue::B_STOP:
1387			HandleStop(event,lateness,realTimeEvent);
1388			break;
1389		case BTimedEventQueue::B_HANDLE_BUFFER:
1390//			TRACE("HandleEvent: B_HANDLE_BUFFER, RunState= %d\n",
1391//				RunState());
1392			if (RunState() == BMediaEventLooper::B_STARTED) {
1393				HandleBuffer(event,lateness,realTimeEvent);
1394			}
1395			break;
1396		case BTimedEventQueue::B_DATA_STATUS:
1397			HandleDataStatus(event,lateness,realTimeEvent);
1398			break;
1399		case BTimedEventQueue::B_PARAMETER:
1400			HandleParameter(event,lateness,realTimeEvent);
1401			break;
1402		default:
1403			fprintf(stderr,"  unknown event type: %" B_PRId32 "\n",
1404				event->type);
1405			break;
1406	}
1407}
1408
1409
1410// protected
1411status_t
1412OpenSoundNode::HandleBuffer(const media_timed_event* event,
1413	bigtime_t lateness, bool realTimeEvent)
1414{
1415	CALLED();
1416
1417	// TODO: How should we handle late buffers? Drop them?
1418	// Notify the producer?
1419
1420	BBuffer* buffer = const_cast<BBuffer*>((BBuffer*)event->pointer);
1421	if (buffer == NULL) {
1422		fprintf(stderr,"OpenSoundNode::HandleBuffer() - B_BAD_VALUE\n");
1423		return B_BAD_VALUE;
1424	}
1425
1426	NodeInput *channel = _FindInput(buffer->Header()->destination);
1427//	TRACE("buffer->Header()->destination : %i\n",
1428//		buffer->Header()->destination);
1429
1430	if (channel == NULL) {
1431		buffer->Recycle();
1432		fprintf(stderr,"OpenSoundNode::HandleBuffer() - "
1433			"B_MEDIA_BAD_DESTINATION\n");
1434		return B_MEDIA_BAD_DESTINATION;
1435	}
1436
1437	media_header* hdr = buffer->Header();
1438	bigtime_t now = TimeSource()->Now();
1439	bigtime_t perf_time = hdr->start_time;
1440
1441	// the how_early calculated here doesn't include scheduling latency
1442	// because we've already been scheduled to handle the buffer
1443	bigtime_t how_early = perf_time - EventLatency() - now;
1444
1445	// if the buffer is late, we ignore it and report the fact to the producer
1446	// who sent it to us
1447	if (RunMode() != B_OFFLINE
1448			// lateness doesn't matter in offline mode...
1449		&& RunMode() != B_RECORDING
1450			// ...or in recording mode
1451		&& how_early < 0LL
1452		&& false) {
1453			// TODO: Debug
1454		//mLateBuffers++;
1455		NotifyLateProducer(channel->fInput.source, -how_early, perf_time);
1456		fprintf(stderr,"	<- LATE BUFFER : %" B_PRIdBIGTIME "\n", how_early);
1457		buffer->Recycle();
1458	} else {
1459		fDevice->Locker()->Lock();
1460		if (channel->fBuffers.CountItems() > 10) {
1461			fDevice->Locker()->Unlock();
1462			TRACE("OpenSoundNode::HandleBuffer too many buffers, "
1463				"recycling\n");
1464			buffer->Recycle();
1465		} else {
1466//			TRACE("OpenSoundNode::HandleBuffer writing channelId : %i,
1467//				how_early:%lli\n", channel->fEngineIndex, how_early);
1468			if (!channel->fBuffers.AddItem(buffer))
1469				buffer->Recycle();
1470			fDevice->Locker()->Unlock();
1471		}
1472	}
1473	return B_OK;
1474}
1475
1476
1477status_t
1478OpenSoundNode::HandleDataStatus(const media_timed_event* event,
1479	bigtime_t lateness, bool realTimeEvent)
1480{
1481//	CALLED();
1482
1483	// TODO: this is called mostly whenever the system mixer
1484	// switches from not sending us buffers (no client connected)
1485	// to sending buffers, and vice versa. In a Terminal, this
1486	// can be nicely demonstrated by provoking a system beep while
1487	// nothing else is using audio playback. Any first beep will
1488	// start with a small glitch, while more beeps before the last
1489	// one finished will not have the glitch. I am not sure, but
1490	// I seem to remember that other audio nodes have the same
1491	// problem, so it might not be a problem of this implementation.
1492
1493	BString message("OpenSoundNode::HandleDataStatus status: ");
1494
1495	switch(event->data) {
1496		case B_DATA_NOT_AVAILABLE:
1497			message << "No data";
1498			break;
1499		case B_DATA_AVAILABLE:
1500			message << "Data";
1501			break;
1502		case B_PRODUCER_STOPPED:
1503			message << "Stopped";
1504			break;
1505		default:
1506			message << "???";
1507			break;
1508	}
1509
1510	message << ", lateness: " << lateness;
1511	printf("%s\n", message.String());
1512
1513	return B_OK;
1514}
1515
1516
1517status_t
1518OpenSoundNode::HandleStart(const media_timed_event* event, bigtime_t lateness,
1519	bool realTimeEvent)
1520{
1521	CALLED();
1522	if (RunState() != B_STARTED) {
1523		// TODO: What should happen here?
1524	}
1525	return B_OK;
1526}
1527
1528
1529status_t
1530OpenSoundNode::HandleSeek(const media_timed_event* event, bigtime_t lateness,
1531	bool realTimeEvent)
1532{
1533	CALLED();
1534	TRACE("OpenSoundNode::HandleSeek(t=%lld, d=%li, bd=%lld)\n",
1535		event->event_time,event->data,event->bigdata);
1536	return B_OK;
1537}
1538
1539
1540status_t
1541OpenSoundNode::HandleWarp(const media_timed_event* event,
1542	bigtime_t lateness, bool realTimeEvent)
1543{
1544	CALLED();
1545	return B_OK;
1546}
1547
1548
1549status_t
1550OpenSoundNode::HandleStop(const media_timed_event* event, bigtime_t lateness,
1551	bool realTimeEvent)
1552{
1553	CALLED();
1554	// flush the queue so downstreamers don't get any more
1555	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
1556		BTimedEventQueue::B_HANDLE_BUFFER);
1557
1558	return B_OK;
1559}
1560
1561
1562status_t
1563OpenSoundNode::HandleParameter(const media_timed_event* event,
1564	bigtime_t lateness, bool realTimeEvent)
1565{
1566	CALLED();
1567	return B_OK;
1568}
1569
1570
1571// #pragma mark - BTimeSource
1572
1573
1574void
1575OpenSoundNode::SetRunMode(run_mode mode)
1576{
1577	CALLED();
1578	TRACE("OpenSoundNode::SetRunMode(%d)\n", mode);
1579	//BTimeSource::SetRunMode(mode);
1580}
1581
1582
1583status_t
1584OpenSoundNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
1585{
1586	CALLED();
1587	switch(op.op) {
1588		case B_TIMESOURCE_START:
1589			TRACE("TimeSourceOp op B_TIMESOURCE_START\n");
1590			if (RunState() != BMediaEventLooper::B_STARTED) {
1591				fTimeSourceStarted = true;
1592				fTimeSourceStartTime = RealTime();
1593
1594				media_timed_event startEvent(0, BTimedEventQueue::B_START);
1595				EventQueue()->AddEvent(startEvent);
1596			}
1597			break;
1598		case B_TIMESOURCE_STOP:
1599			TRACE("TimeSourceOp op B_TIMESOURCE_STOP\n");
1600			if (RunState() == BMediaEventLooper::B_STARTED) {
1601				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1602				EventQueue()->AddEvent(stopEvent);
1603				fTimeSourceStarted = false;
1604				PublishTime(0, 0, 0);
1605			}
1606			break;
1607		case B_TIMESOURCE_STOP_IMMEDIATELY:
1608			TRACE("TimeSourceOp op B_TIMESOURCE_STOP_IMMEDIATELY\n");
1609			if (RunState() == BMediaEventLooper::B_STARTED) {
1610				media_timed_event stopEvent(0, BTimedEventQueue::B_STOP);
1611				EventQueue()->AddEvent(stopEvent);
1612				fTimeSourceStarted = false;
1613				PublishTime(0, 0, 0);
1614			}
1615			break;
1616		case B_TIMESOURCE_SEEK:
1617//			TRACE("TimeSourceOp op B_TIMESOURCE_SEEK\n");
1618printf("TimeSourceOp op B_TIMESOURCE_SEEK, real %" B_PRIdBIGTIME ", "
1619	"perf %" B_PRIdBIGTIME "\n", op.real_time, op.performance_time);
1620			BroadcastTimeWarp(op.real_time, op.performance_time);
1621			break;
1622		default:
1623			break;
1624	}
1625	return B_OK;
1626}
1627
1628
1629// #pragma mark - BControllable
1630
1631
1632status_t
1633OpenSoundNode::GetParameterValue(int32 id, bigtime_t* last_change, void* value,
1634	size_t* ioSize)
1635{
1636	CALLED();
1637
1638	int channelCount = 1;
1639	int sliderShift = 8;
1640
1641	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1642	if (!mixer)
1643		return ENODEV;
1644
1645	TRACE("id : %i, *ioSize=%d\n", id, *ioSize);
1646
1647	oss_mixext mixext;
1648	status_t err = mixer->GetExtInfo(id, &mixext);
1649	if (err < B_OK)
1650		return err;
1651
1652	oss_mixer_value mixval;
1653	mixval.ctrl = mixext.ctrl;
1654	mixval.timestamp = mixext.timestamp;
1655
1656	err = mixer->GetMixerValue(&mixval);
1657	if (err < B_OK)
1658		return err;
1659
1660	if (!(mixext.flags & MIXF_READABLE))
1661		return EINVAL;
1662
1663	BParameter *parameter = NULL;
1664	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
1665		parameter = fWeb->ParameterAt(i);
1666		if(parameter->ID() == id)
1667			break;
1668	}
1669
1670	if (!parameter)
1671		return ENODEV;
1672
1673	TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1674
1675	*last_change = system_time();//??
1676
1677	switch (mixext.type) {
1678	case MIXT_DEVROOT:
1679	case MIXT_GROUP:
1680		break;
1681	case MIXT_ONOFF:
1682		if (*ioSize < sizeof(bool))
1683			return EINVAL;
1684		*(int32 *)value = mixval.value?true:false;
1685		*ioSize = sizeof(bool);
1686		return B_OK;
1687	case MIXT_ENUM:
1688		if (*ioSize < sizeof(int32))
1689			return EINVAL;
1690		*(int32 *)value = mixval.value;
1691		*ioSize = sizeof(int32);
1692		return B_OK;
1693		break;
1694	case MIXT_STEREODB:
1695	case MIXT_STEREOSLIDER16:
1696	case MIXT_STEREOSLIDER:
1697		channelCount = 2;
1698	case MIXT_SLIDER:
1699	case MIXT_MONODB:
1700	case MIXT_MONOSLIDER16:
1701	case MIXT_MONOSLIDER:
1702		if (*ioSize < channelCount * sizeof(float))
1703			return EINVAL;
1704		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1705			return EINVAL;
1706		if (mixext.type == MIXT_STEREOSLIDER16 ||
1707			mixext.type == MIXT_MONOSLIDER16)
1708			sliderShift = 16;
1709		*ioSize = channelCount * sizeof(float);
1710		((float *)value)[0] = (float)(mixval.value & ((1 << sliderShift) - 1));
1711		TRACE("%s: value[O] = %f\n", __FUNCTION__, ((float *)value)[0]);
1712		if (channelCount < 2)
1713			return B_OK;
1714		((float *)value)[1] = (float)((mixval.value >> sliderShift)
1715			& ((1 << sliderShift) - 1));
1716		TRACE("%s: value[1] = %f\n", __FUNCTION__, ((float *)value)[1]);
1717		return B_OK;
1718		break;
1719	case MIXT_MESSAGE:
1720		break;
1721	case MIXT_MONOVU:
1722		break;
1723	case MIXT_STEREOVU:
1724		break;
1725	case MIXT_MONOPEAK:
1726		break;
1727	case MIXT_STEREOPEAK:
1728		break;
1729	case MIXT_RADIOGROUP:
1730		break;//??
1731	case MIXT_MARKER:
1732		break;// separator item: ignore
1733	case MIXT_VALUE:
1734		break;
1735	case MIXT_HEXVALUE:
1736		break;
1737/*	case MIXT_MONODB:
1738		break;
1739	case MIXT_STEREODB:
1740		break;*/
1741	case MIXT_3D:
1742		break;
1743/*	case MIXT_MONOSLIDER16:
1744		break;
1745	case MIXT_STEREOSLIDER16:
1746		break;*/
1747	default:
1748		TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1749			__FUNCTION__, mixext.type);
1750	}
1751	*ioSize = 0;
1752	return EINVAL;
1753}
1754
1755
1756void
1757OpenSoundNode::SetParameterValue(int32 id, bigtime_t performance_time,
1758	const void* value, size_t size)
1759{
1760	CALLED();
1761
1762	TRACE("id : %i, performance_time : %lld, size : %i\n", id,
1763		performance_time, size);
1764
1765	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
1766	if (mixer == NULL)
1767		return;
1768
1769	oss_mixext mixext;
1770	if (mixer->GetExtInfo(id, &mixext) < B_OK)
1771		return;
1772	if (!(mixext.flags & MIXF_WRITEABLE))
1773		return;
1774
1775	oss_mixer_value mixval;
1776	mixval.ctrl = mixext.ctrl;
1777	mixval.timestamp = mixext.timestamp;
1778
1779	status_t err = mixer->GetMixerValue(&mixval);
1780	if (err < B_OK)
1781		return;
1782
1783	mixval.ctrl = mixext.ctrl;
1784	mixval.timestamp = mixext.timestamp;
1785
1786	BParameter *parameter = NULL;
1787	for(int32 i=0; i<fWeb->CountParameters(); i++) {
1788		parameter = fWeb->ParameterAt(i);
1789		if(parameter->ID() == id)
1790			break;
1791	}
1792
1793	if (!parameter)
1794		return;
1795
1796	int channelCount = 1;
1797	int sliderShift = 8;
1798
1799	switch (mixext.type) {
1800		case MIXT_DEVROOT:
1801		case MIXT_GROUP:
1802			break;
1803		case MIXT_ONOFF:
1804			if (size < sizeof(bool))
1805				return;
1806			mixval.value = (int)*(int32 *)value;
1807			mixer->SetMixerValue(&mixval);
1808			// At least on my ATI IXP, recording selection can't be set to OFF,
1809			// you have to set another one to ON to actually do it,
1810			// and setting to ON changes others to OFF
1811			// So we have to let users know about it.
1812			// XXX: find something better, doesn't work correctly here.
1813			// XXX: try a timed event ?
1814			_PropagateParameterChanges(mixext.ctrl, mixext.type, mixext.id);
1815
1816			return;
1817		case MIXT_ENUM:
1818			if (size < sizeof(int32))
1819				return;
1820			mixval.value = (int)*(int32 *)value;
1821			mixer->SetMixerValue(&mixval);
1822			break;
1823		case MIXT_STEREODB:
1824		case MIXT_STEREOSLIDER16:
1825		case MIXT_STEREOSLIDER:
1826			channelCount = 2;
1827		case MIXT_SLIDER:
1828		case MIXT_MONODB:
1829		case MIXT_MONOSLIDER16:
1830		case MIXT_MONOSLIDER:
1831			if (size < channelCount * sizeof(float))
1832				return;
1833			if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
1834				return;
1835			if (mixext.type == MIXT_STEREOSLIDER16 ||
1836				mixext.type == MIXT_MONOSLIDER16)
1837				sliderShift = 16;
1838			mixval.value = 0;
1839
1840			TRACE("-------- sliderShift=%d, v = %08x, v & %08x = %08x\n",
1841				sliderShift, mixval.value, ((1 << sliderShift) - 1),
1842				mixval.value & ((1 << sliderShift) - 1));
1843
1844			mixval.value |= ((int)(((float *)value)[0]))
1845				& ((1 << sliderShift) - 1);
1846			if (channelCount > 1) {
1847				mixval.value |= (((int)(((float *)value)[1]))
1848					& ((1 << sliderShift) - 1)) << sliderShift;
1849			}
1850
1851			TRACE("%s: value = 0x%08x\n", __FUNCTION__, mixval.value);
1852			mixer->SetMixerValue(&mixval);
1853			return;
1854			break;
1855		case MIXT_MESSAGE:
1856			break;
1857		case MIXT_MONOVU:
1858			break;
1859		case MIXT_STEREOVU:
1860			break;
1861		case MIXT_MONOPEAK:
1862			break;
1863		case MIXT_STEREOPEAK:
1864			break;
1865		case MIXT_RADIOGROUP:
1866			break;//??
1867		case MIXT_MARKER:
1868			break;// separator item: ignore
1869		case MIXT_VALUE:
1870			break;
1871		case MIXT_HEXVALUE:
1872			break;
1873//		case MIXT_MONODB:
1874//			break;
1875//		case MIXT_STEREODB:
1876//			break;
1877		case MIXT_3D:
1878			break;
1879//		case MIXT_MONOSLIDER16:
1880//			break;
1881//		case MIXT_STEREOSLIDER16:
1882//			break;
1883		default:
1884			TRACE("OpenSoundNode::%s: unknown mixer control type %d\n",
1885				__FUNCTION__, mixext.type);
1886	}
1887
1888	return;
1889}
1890
1891
1892BParameterWeb*
1893OpenSoundNode::MakeParameterWeb()
1894{
1895	CALLED();
1896	BParameterWeb* web = new BParameterWeb;
1897
1898	// TODO: the card might change the mixer controls at some point,
1899	// we should detect it (poll) and recreate the parameter web and
1900	// re-set it.
1901
1902	// TODO: cache mixext[...] and poll for changes in their update_counter.
1903
1904	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1905	if (mixer == NULL) {
1906		// some cards don't have a mixer, just put a placeholder then
1907		BParameterGroup* child = web->MakeGroup("No mixer");
1908		child->MakeNullParameter(1, B_MEDIA_UNKNOWN_TYPE, "No Mixer",
1909			B_GENERIC);
1910		return web;
1911	}
1912
1913	int mixext_count = mixer->CountExtInfos();
1914	TRACE("OpenSoundNode::MakeParameterWeb %i ExtInfos\n", mixext_count);
1915
1916	for (int32 i = 0; i < mixext_count; i++) {
1917		oss_mixext mixext;
1918		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1919			continue;
1920
1921		if (mixext.type == MIXT_DEVROOT) {
1922			oss_mixext_root* extroot = (oss_mixext_root*)mixext.data;
1923			TRACE("OpenSoundNode: mixext[%d]: ROOT\n", i);
1924			int32 nb = 0;
1925			const char* childName = mixext.extname;
1926			childName = extroot->id; // extroot->name;
1927			BParameterGroup *child = web->MakeGroup(childName);
1928			_ProcessGroup(child, i, nb);
1929		}
1930	}
1931
1932	return web;
1933}
1934
1935
1936// #pragma mark - OpenSoundNode specific
1937
1938
1939void
1940OpenSoundNode::_ProcessGroup(BParameterGroup *group, int32 index,
1941	int32& nbParameters)
1942{
1943	CALLED();
1944	// TODO: It looks wrong to use the same mixer in a recursive function!
1945	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
1946
1947	int mixext_count = mixer->CountExtInfos();
1948	for (int32 i = 0; i < mixext_count; i++) {
1949		oss_mixext mixext;
1950		if (mixer->GetExtInfo(i, &mixext) < B_OK)
1951			continue;
1952		// only keep direct children of that group
1953		if (mixext.parent != index)
1954			continue;
1955
1956		int32 nb = 1;
1957
1958		TRACE("OpenSoundNode: mixext[%d]: { %s/%s, type=%d, parent=%d, "
1959			"min=%d, max=%d, flags=0x%08x, control_no=%d, desc=%d, "
1960			"update_counter=%d }\n", i,
1961			(mixext.type != MIXT_MARKER) ? mixext.id : "",
1962			(mixext.type != MIXT_MARKER) ? mixext.extname : "",
1963			mixext.type, mixext.parent,
1964			mixext.minvalue, mixext.maxvalue,
1965			mixext.flags, mixext.control_no,
1966			mixext.desc, mixext.update_counter);
1967
1968		// should actually rename the whole group but it's too late there.
1969		const char *childName = mixext.extname;
1970		if (mixext.flags & MIXF_MAINVOL)
1971			childName = "Master Gain";
1972
1973		const char *sliderUnit = "";//"(linear)";
1974		if (mixext.flags & MIXF_HZ)
1975			sliderUnit = "Hz";
1976
1977		const char *continuousKind = B_GAIN;
1978		BParameterGroup* child;
1979
1980		switch (mixext.type) {
1981		case MIXT_DEVROOT:
1982			// root item, should be done already
1983			break;
1984		case MIXT_GROUP:
1985			TRACE("OpenSoundNode: mixext[%d]: GROUP\n", i);
1986			child = group->MakeGroup(childName);
1987			child->MakeNullParameter(i, B_MEDIA_RAW_AUDIO, childName,
1988				B_WEB_BUFFER_OUTPUT);
1989			_ProcessGroup(child, i, nb);
1990			break;
1991		case MIXT_ONOFF:
1992			TRACE("OpenSoundNode: mixext[%d]: ONOFF\n", i);
1993			// multiaudio node adds 100 to IDs !?
1994			if (0/*MMC[i].string == S_MUTE*/) {
1995				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1996					B_MUTE);
1997			} else {
1998				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
1999					B_ENABLE);
2000			}
2001			if (nbParameters > 0) {
2002				(group->ParameterAt(nbParameters - 1))->AddOutput(
2003					group->ParameterAt(nbParameters));
2004				nbParameters++;
2005			}
2006			break;
2007		case MIXT_ENUM:
2008		{
2009			TRACE("OpenSoundNode: mixext[%d]: ENUM\n", i);
2010			BDiscreteParameter *parameter =
2011				group->MakeDiscreteParameter(i, B_MEDIA_RAW_AUDIO, childName,
2012					B_INPUT_MUX);
2013			if (nbParameters > 0) {
2014				(group->ParameterAt(nbParameters - 1))->AddOutput(
2015					group->ParameterAt(nbParameters));
2016				nbParameters++;
2017			}
2018			_ProcessMux(parameter, i);
2019			break;
2020		}
2021		case MIXT_MONODB:
2022		case MIXT_STEREODB:
2023			sliderUnit = "dB";
2024		case MIXT_SLIDER:
2025		case MIXT_MONOSLIDER16:
2026		case MIXT_STEREOSLIDER16:
2027		case MIXT_MONOSLIDER:
2028			//TRACE("OpenSoundNode: mixext[%d]: MONOSLIDER\n", i);
2029			//break;
2030			// fall through
2031		case MIXT_STEREOSLIDER:
2032			TRACE("OpenSoundNode: mixext[%d]: [MONO|STEREO]SLIDER\n", i);
2033
2034			if (mixext.flags & MIXF_MAINVOL)
2035				continuousKind = B_MASTER_GAIN;
2036
2037// TODO: find out what this was supposed to do:
2038//			if (mixext.flags & MIXF_CENTIBEL)
2039//				true;//step size
2040//			if (mixext.flags & MIXF_DECIBEL)
2041//				true;//step size
2042
2043			group->MakeContinuousParameter(i, B_MEDIA_RAW_AUDIO, childName,
2044				continuousKind,  sliderUnit, mixext.minvalue, mixext.maxvalue,
2045				/*TODO: should be "granularity"*/1);
2046
2047			if (mixext.type == MIXT_STEREOSLIDER ||
2048				mixext.type == MIXT_STEREOSLIDER16 ||
2049				mixext.type == MIXT_STEREODB)
2050				group->ParameterAt(nbParameters)->SetChannelCount(2);
2051
2052			TRACE("nb parameters : %d\n", nbParameters);
2053			if (nbParameters > 0) {
2054				(group->ParameterAt(nbParameters - 1))->AddOutput(
2055					group->ParameterAt(nbParameters));
2056				nbParameters++;
2057			}
2058
2059			break;
2060		case MIXT_MESSAGE:
2061			break;
2062		case MIXT_MONOVU:
2063			break;
2064		case MIXT_STEREOVU:
2065			break;
2066		case MIXT_MONOPEAK:
2067			break;
2068		case MIXT_STEREOPEAK:
2069			break;
2070		case MIXT_RADIOGROUP:
2071			break;//??
2072		case MIXT_MARKER:
2073			break;// separator item: ignore
2074		case MIXT_VALUE:
2075			break;
2076		case MIXT_HEXVALUE:
2077			break;
2078//		case MIXT_MONODB:
2079//			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2080//				"MIXT_MONODB\n");
2081//			break;
2082//		case MIXT_STEREODB:
2083//			TRACE("OpenSoundNode::_ProcessGroup: Skipping obsolete "
2084//				"MIXT_STEREODB\n");
2085//			break;
2086//		case MIXT_SLIDER:
2087//			break;
2088		case MIXT_3D:
2089			break;
2090//		case MIXT_MONOSLIDER16:
2091//			break;
2092//		case MIXT_STEREOSLIDER16:
2093//			break;
2094		default:
2095			TRACE("OpenSoundNode::_ProcessGroup: unknown mixer control "
2096				"type %d\n", mixext.type);
2097		}
2098	}
2099
2100}
2101
2102
2103void
2104OpenSoundNode::_ProcessMux(BDiscreteParameter* parameter, int32 index)
2105{
2106	CALLED();
2107	OpenSoundDeviceMixer *mixer = fDevice->MixerAt(0);
2108	oss_mixer_enuminfo enuminfo;
2109	status_t err = mixer->GetEnumInfo(index, &enuminfo);
2110	if (err < B_OK) {
2111		// maybe there is no list.
2112		// generate a count form 0
2113		oss_mixext mixext;
2114		if (mixer->GetExtInfo(index, &mixext) < B_OK)
2115			return;
2116
2117		for (int32 i = 0; i < mixext.maxvalue; i++) {
2118			BString s;
2119			s << i;
2120			parameter->AddItem(i, s.String());
2121		}
2122		return;
2123	}
2124
2125	for (int32 i = 0; i < enuminfo.nvalues; i++) {
2126		parameter->AddItem(i, &enuminfo.strings[enuminfo.strindex[i]]);
2127	}
2128	return;
2129}
2130
2131
2132status_t
2133OpenSoundNode::_PropagateParameterChanges(int from, int type, const char* id)
2134{
2135	CALLED();
2136
2137	TRACE("OpenSoundNode::_PropagateParameterChanges(from %i, type %i, "
2138		"id %s)\n", from, type, id);
2139
2140	OpenSoundDeviceMixer* mixer = fDevice->MixerAt(0);
2141	if (mixer == NULL)
2142		return ENODEV;
2143
2144// TODO: Cortex doesn't like that!
2145// try timed event
2146// try checking update_counter+caching
2147return B_OK;
2148
2149//	char oldValues[128];
2150	char newValues[128];
2151//	size_t oldValuesSize;
2152	size_t newValuesSize;
2153
2154	for (int i = 0; i < mixer->CountExtInfos(); i++) {
2155		oss_mixext mixext;
2156		status_t err = mixer->GetExtInfo(i, &mixext);
2157		if (err < B_OK)
2158			continue;
2159
2160		// skip the caller
2161		//if (mixext.ctrl == from)
2162		//	continue;
2163
2164		if (!(mixext.flags & MIXF_READABLE))
2165			continue;
2166
2167		// match type ?
2168		if (type > -1 && mixext.type != type)
2169			continue;
2170
2171		// match internal ID string
2172		if (id && strncmp(mixext.id, id, 16))
2173			continue;
2174
2175//		BParameter *parameter = NULL;
2176//		for(int32 i=0; i<fWeb->CountParameters(); i++) {
2177//			parameter = fWeb->ParameterAt(i);
2178//			if(parameter->ID() == mixext.ctrl)
2179//				break;
2180//		}
2181//
2182//		if (!parameter)
2183//			continue;
2184
2185//		oldValuesSize = 128;
2186		newValuesSize = 128;
2187		bigtime_t last;
2188//		TRACE("OpenSoundNode::%s: comparing mixer control %d\n",
2189//			__FUNCTION__, mixext.ctrl);
2190//		if (parameter->GetValue(oldValues, &oldValuesSize, &last) < B_OK)
2191//			continue;
2192		if (GetParameterValue(mixext.ctrl, &last, newValues,
2193				&newValuesSize) < B_OK) {
2194			continue;
2195		}
2196//		if (oldValuesSize != newValuesSize || memcmp(oldValues, newValues,
2197//				MIN(oldValuesSize, newValuesSize))) {
2198			TRACE("OpenSoundNode::%s: updating mixer control %d\n",
2199				__FUNCTION__, mixext.ctrl);
2200			BroadcastNewParameterValue(last, mixext.ctrl, newValues,
2201				newValuesSize);
2202//			BroadcastChangedParameter(mixext.ctrl);
2203//		}
2204	}
2205	return B_OK;
2206}
2207
2208
2209int32
2210OpenSoundNode::_PlayThread(NodeInput* input)
2211{
2212	CALLED();
2213	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2214	signal(SIGUSR1, &_SignalHandler);
2215
2216	OpenSoundDeviceEngine* engine = input->fRealEngine;
2217	if (!engine || !engine->InUse())
2218		return B_NO_INIT;
2219	// skip unconnected or non-busy engines
2220	if (input->fInput.source == media_source::null
2221		&& input->fEngineIndex == 0)
2222		return B_NO_INIT;
2223	// must be open for write
2224	ASSERT(engine->OpenMode() & OPEN_WRITE);
2225
2226	// make writing actually block until the previous buffer played
2227	size_t driverBufferSize = engine->DriverBufferSize();
2228	size_t bufferSize = input->fInput.format.u.raw_audio.buffer_size;
2229	if (driverBufferSize != bufferSize) {
2230		printf("warning, OSS driver buffer size: %ld, audio buffer "
2231			"size: %ld", driverBufferSize, bufferSize);
2232	}
2233
2234	// cache a silence buffer
2235	uint8 silenceBuffer[bufferSize];
2236	uint8 formatSilence = 0;
2237	if (input->fInput.format.u.raw_audio.format
2238			== media_raw_audio_format::B_AUDIO_UCHAR)
2239		formatSilence = 128;
2240
2241	memset(silenceBuffer, formatSilence, bufferSize);
2242
2243	// start by writing the OSS driver buffer size of silence
2244	// so that the first call to write() already blocks for (almost) the
2245	// buffer duration
2246	input->Write(silenceBuffer, bufferSize);
2247
2248	int64 bytesWritten = 0;
2249	bigtime_t lastRealTime = RealTime();
2250	bigtime_t lastPerformanceTime = 0;
2251
2252	const int32 driftValueCount = 64;
2253	int32 currentDriftValueIndex = 0;
2254	float driftValues[driftValueCount];
2255	for (int32 i = 0; i < driftValueCount; i++)
2256		driftValues[i] = 1.0;
2257
2258	do {
2259		if (!fDevice->Locker()->Lock())
2260			break;
2261
2262		TRACE("OpenSoundNode::_PlayThread: buffers: %ld\n",
2263			input->fBuffers.CountItems());
2264
2265		BBuffer* buffer = (BBuffer*)input->fBuffers.RemoveItem((int32)0);
2266
2267		fDevice->Locker()->Unlock();
2268
2269		if (input->fThread < 0) {
2270			if (buffer)
2271				buffer->Recycle();
2272			break;
2273		}
2274
2275//input->WriteTestTone();
2276//if (buffer)
2277//	buffer->Recycle();
2278//continue;
2279
2280		int32 additionalBytesWritten = 0;
2281		if (buffer != NULL) {
2282			if (input->Write(buffer->Data(), buffer->SizeUsed()) == B_OK)
2283				additionalBytesWritten = buffer->SizeUsed();
2284			buffer->Recycle();
2285		} else {
2286			input->Write(silenceBuffer, bufferSize);
2287			additionalBytesWritten = bufferSize;
2288		}
2289
2290		// TODO: do not assume channel 0 will always be running!
2291		// update the timesource
2292		if (input->fEngineIndex == 0 && input->fThread >= 0) {
2293
2294			bigtime_t realTime = RealTime();
2295			bigtime_t realPlaybackDuration = realTime - lastRealTime;
2296			bigtime_t performanceTime
2297				= time_for_buffer(bytesWritten, input->fInput.format);
2298			float drift = (double)(performanceTime
2299				- lastPerformanceTime) / realPlaybackDuration;
2300
2301			lastPerformanceTime = performanceTime;
2302			lastRealTime = realTime;
2303
2304			driftValues[currentDriftValueIndex++] = drift;
2305			if (currentDriftValueIndex == driftValueCount)
2306				currentDriftValueIndex = 0;
2307			drift = 0.0;
2308			for (int32 i = 0; i < driftValueCount; i++)
2309				drift += driftValues[i];
2310			drift /= driftValueCount;
2311
2312			if (fDevice->Locker()->Lock()) {
2313				if (input->fThread >= 0)
2314					_UpdateTimeSource(performanceTime, realTime, drift);
2315				fDevice->Locker()->Unlock();
2316			}
2317		}
2318		bytesWritten += additionalBytesWritten;
2319
2320	} while (input->fThread > -1);
2321
2322	return 0;
2323}
2324
2325
2326int32
2327OpenSoundNode::_RecThread(NodeOutput* output)
2328{
2329	CALLED();
2330
2331	//set_thread_priority(find_thread(NULL), 5);// TODO:DEBUG
2332	signal(SIGUSR1, &_SignalHandler);
2333
2334	OpenSoundDeviceEngine *engine = output->fRealEngine;
2335	if (!engine || !engine->InUse())
2336		return B_NO_INIT;
2337	// make sure we're both started *and* connected before delivering a buffer
2338	if ((RunState() != BMediaEventLooper::B_STARTED)
2339		|| (output->fOutput.destination == media_destination::null)) {
2340		return B_NO_INIT;
2341	}
2342
2343	// must be open for read
2344	ASSERT(engine->OpenMode() & OPEN_READ);
2345
2346#ifdef ENABLE_REC
2347
2348	fDevice->Locker()->Lock();
2349	do {
2350		audio_buf_info abinfo;
2351//		size_t avail = engine->GetISpace(&abinfo);
2352//		TRACE("OpenSoundNode::_RunThread: I avail: %d\n", avail);
2353//
2354//		// skip if less than 1 buffer
2355//		if (avail < output->fOutput.format.u.raw_audio.buffer_size)
2356//			continue;
2357
2358		fDevice->Locker()->Unlock();
2359		// Get the next buffer of data
2360		BBuffer* buffer = _FillNextBuffer(&abinfo, *output);
2361		fDevice->Locker()->Lock();
2362
2363		if (buffer) {
2364			// send the buffer downstream if and only if output is enabled
2365			status_t err = B_ERROR;
2366			if (output->fOutputEnabled) {
2367				err = SendBuffer(buffer, output->fOutput.source,
2368					output->fOutput.destination);
2369			}
2370//			TRACE("OpenSoundNode::_RunThread: I avail: %d, OE %d, %s\n",
2371//				avail, output->fOutputEnabled, strerror(err));
2372			if (err != B_OK) {
2373				buffer->Recycle();
2374			} else {
2375				// track how much media we've delivered so far
2376				size_t nSamples = buffer->SizeUsed()
2377					/ (output->fOutput.format.u.raw_audio.format
2378						& media_raw_audio_format::B_AUDIO_SIZE_MASK);
2379				output->fSamplesSent += nSamples;
2380//				TRACE("OpenSoundNode::%s: sent %d samples\n",
2381//					__FUNCTION__, nSamples);
2382			}
2383
2384		}
2385	} while (output->fThread > -1);
2386	fDevice->Locker()->Unlock();
2387
2388#endif
2389	return 0;
2390}
2391
2392
2393status_t
2394OpenSoundNode::_StartPlayThread(NodeInput* input)
2395{
2396	CALLED();
2397	BAutolock L(fDevice->Locker());
2398	// the thread is already started ?
2399	if (input->fThread > B_OK)
2400		return B_OK;
2401
2402	//allocate buffer free semaphore
2403//	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2404
2405//	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2406//		"multi_audio out buffer free");
2407//	fBufferAvailableSem = create_sem(bufferCount - 1,
2408//		"OpenSound out buffer free");
2409
2410//	if (fBufferAvailableSem < B_OK)
2411//		return B_ERROR;
2412
2413	input->fThread = spawn_thread(_PlayThreadEntry,
2414		"OpenSound audio output", B_REAL_TIME_PRIORITY, input);
2415
2416	if (input->fThread < B_OK) {
2417//		delete_sem(fBufferAvailableSem);
2418		return B_ERROR;
2419	}
2420
2421	resume_thread(input->fThread);
2422	return B_OK;
2423}
2424
2425
2426status_t
2427OpenSoundNode::_StopPlayThread(NodeInput* input)
2428{
2429	if (input->fThread < 0)
2430		return B_OK;
2431
2432	CALLED();
2433
2434	thread_id th;
2435	{
2436		BAutolock L(fDevice->Locker());
2437		th = input->fThread;
2438		input->fThread = -1;
2439		//kill(th, SIGUSR1);
2440	}
2441	status_t ret;
2442	wait_for_thread(th, &ret);
2443
2444	return B_OK;
2445}
2446
2447
2448status_t
2449OpenSoundNode::_StartRecThread(NodeOutput* output)
2450{
2451	CALLED();
2452	// the thread is already started ?
2453	if (output->fThread > B_OK)
2454		return B_OK;
2455
2456	//allocate buffer free semaphore
2457//	int bufferCount = MAX(fDevice->fFragments.fragstotal, 2); // XXX
2458
2459//	fBufferAvailableSem = create_sem(fDevice->MBL.return_playback_buffers - 1,
2460//		"multi_audio out buffer free");
2461//	fBufferAvailableSem = create_sem(bufferCount - 1,
2462//		"OpenSound out buffer free");
2463
2464//	if (fBufferAvailableSem < B_OK)
2465//		return B_ERROR;
2466
2467	output->fThread = spawn_thread(_RecThreadEntry, "OpenSound audio input",
2468		B_REAL_TIME_PRIORITY, output);
2469
2470	if (output->fThread < B_OK) {
2471		//delete_sem(fBufferAvailableSem);
2472		return B_ERROR;
2473	}
2474
2475	resume_thread(output->fThread);
2476	return B_OK;
2477}
2478
2479
2480status_t
2481OpenSoundNode::_StopRecThread(NodeOutput* output)
2482{
2483	if (output->fThread < 0)
2484		return B_OK;
2485
2486	CALLED();
2487
2488	thread_id th = output->fThread;
2489	output->fThread = -1;
2490	{
2491		BAutolock L(fDevice->Locker());
2492		//kill(th, SIGUSR1);
2493	}
2494	status_t ret;
2495	wait_for_thread(th, &ret);
2496
2497	return B_OK;
2498}
2499
2500
2501void
2502OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime,
2503	bigtime_t realTime, float drift)
2504{
2505//	CALLED();
2506
2507	if (!fTimeSourceStarted)
2508		return;
2509
2510	PublishTime(performanceTime, realTime, drift);
2511
2512//	TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli, "
2513//		"drift : %f\n", perfTime, realTime, drift);
2514}
2515
2516
2517BBuffer*
2518OpenSoundNode::_FillNextBuffer(audio_buf_info* abinfo, NodeOutput& channel)
2519{
2520	CALLED();
2521
2522	BBuffer* buffer = channel.FillNextBuffer(BufferDuration());
2523	if (!buffer)
2524		return NULL;
2525
2526	if (fDevice == NULL)
2527		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - fDevice NULL\n");
2528	if (buffer->Header() == NULL) {
2529		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2530			"buffer->Header() NULL\n");
2531	}
2532	if (TimeSource() == NULL) {
2533		fprintf(stderr, "OpenSoundNode::_FillNextBuffer() - "
2534			"TimeSource() NULL\n");
2535	}
2536
2537	// fill in the buffer header
2538	media_header* hdr = buffer->Header();
2539	if (hdr != NULL) {
2540		hdr->time_source = TimeSource()->ID();
2541		// TODO: should be system_time() - latency_as_per(abinfo)
2542		hdr->start_time = PerformanceTimeFor(system_time());
2543	}
2544
2545	return buffer;
2546}
2547
2548
2549status_t
2550OpenSoundNode::GetConfigurationFor(BMessage* into_message)
2551{
2552	CALLED();
2553
2554	if (!into_message)
2555		return B_BAD_VALUE;
2556
2557	size_t size = 128;
2558	void* buffer = malloc(size);
2559
2560	for (int32 i = 0; i < fWeb->CountParameters(); i++) {
2561		BParameter* parameter = fWeb->ParameterAt(i);
2562		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER
2563			&& parameter->Type() != BParameter::B_DISCRETE_PARAMETER)
2564			continue;
2565
2566		TRACE("getting parameter %i\n", parameter->ID());
2567		size = 128;
2568		bigtime_t last_change;
2569		status_t err;
2570		while ((err = GetParameterValue(parameter->ID(), &last_change, buffer,
2571				&size)) == B_NO_MEMORY) {
2572			size += 128;
2573			free(buffer);
2574			buffer = malloc(size);
2575		}
2576
2577		if (err == B_OK && size > 0) {
2578			into_message->AddInt32("parameterID", parameter->ID());
2579			into_message->AddData("parameterData", B_RAW_TYPE, buffer, size,
2580				false);
2581		} else {
2582			TRACE("parameter err : %s\n", strerror(err));
2583		}
2584	}
2585
2586	free(buffer);
2587
2588	PRINT_OBJECT(*into_message);
2589
2590	return B_OK;
2591}
2592
2593
2594OpenSoundNode::NodeOutput*
2595OpenSoundNode::_FindOutput(const media_source& source) const
2596{
2597	int32 count = fOutputs.CountItems();
2598	for (int32 i = 0; i < count; i++) {
2599		NodeOutput* channel = (NodeOutput*)fOutputs.ItemAtFast(i);
2600		if (source == channel->fOutput.source)
2601			return channel;
2602	}
2603	return NULL;
2604}
2605
2606
2607OpenSoundNode::NodeInput*
2608OpenSoundNode::_FindInput(const media_destination& dest) const
2609{
2610	int32 count = fInputs.CountItems();
2611	for (int32 i = 0; i < count; i++) {
2612		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2613		if (dest == channel->fInput.destination)
2614			return channel;
2615	}
2616	return NULL;
2617}
2618
2619
2620OpenSoundNode::NodeInput*
2621OpenSoundNode::_FindInput(int32 destinationId)
2622{
2623	int32 count = fInputs.CountItems();
2624	for (int32 i = 0; i < count; i++) {
2625		NodeInput* channel = (NodeInput*)fInputs.ItemAtFast(i);
2626		if (destinationId == channel->fInput.destination.id)
2627			return channel;
2628	}
2629	return NULL;
2630}
2631
2632
2633// pragma mark - static
2634
2635
2636void
2637OpenSoundNode::_SignalHandler(int sig)
2638{
2639	// TODO: what was this intended for, just stopping the threads?
2640	// (see _StopThreadXXX(), there is a kill call commented out there)
2641}
2642
2643
2644int32
2645OpenSoundNode::_PlayThreadEntry(void* data)
2646{
2647	CALLED();
2648	NodeInput* channel = static_cast<NodeInput*>(data);
2649	return channel->fNode->_PlayThread(channel);
2650}
2651
2652
2653int32
2654OpenSoundNode::_RecThreadEntry(void* data)
2655{
2656	CALLED();
2657	NodeOutput* channel = static_cast<NodeOutput*>(data);
2658	return channel->fNode->_RecThread(channel);
2659}
2660
2661
2662void
2663OpenSoundNode::GetFlavor(flavor_info* outInfo, int32 id)
2664{
2665	CALLED();
2666	if (outInfo == NULL)
2667		return;
2668
2669	outInfo->flavor_flags = 0;
2670	outInfo->possible_count = 1;
2671		// one flavor at a time
2672	outInfo->in_format_count = 0;
2673		// no inputs
2674	outInfo->in_formats = 0;
2675	outInfo->out_format_count = 0;
2676		// no outputs
2677	outInfo->out_formats = 0;
2678	outInfo->internal_id = id;
2679
2680	outInfo->name = (char *)"OpenSoundNode Node";
2681	outInfo->info = (char *)"The OpenSoundNode outputs to OpenSound System v4 "
2682		"drivers.";
2683	outInfo->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_TIME_SOURCE
2684		| B_PHYSICAL_OUTPUT | B_PHYSICAL_INPUT | B_CONTROLLABLE;
2685	// TODO: If the OSS engine supports outputing encoded audio,
2686	// we would need to setup a B_MEDIA_ENCODED_AUDIO format here
2687	outInfo->in_format_count = 1;
2688		// 1 input
2689	media_format * informats = new media_format[outInfo->in_format_count];
2690	GetFormat(&informats[0]);
2691	outInfo->in_formats = informats;
2692
2693	outInfo->out_format_count = 1;
2694		// 1 output
2695	media_format * outformats = new media_format[outInfo->out_format_count];
2696	GetFormat(&outformats[0]);
2697	outInfo->out_formats = outformats;
2698}
2699
2700
2701void
2702OpenSoundNode::GetFormat(media_format* outFormat)
2703{
2704	CALLED();
2705	if (outFormat == NULL)
2706		return;
2707
2708	outFormat->type = B_MEDIA_RAW_AUDIO;
2709	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2710	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
2711	outFormat->u.raw_audio = media_raw_audio_format::wildcard;
2712}
2713