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