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, ¶meterID) == 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