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