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 38 using std::nothrow; 39 40 41 class FunctionTracer { 42 public: 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 74 class OpenSoundNode::NodeInput { 75 public: 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 185 class OpenSoundNode::NodeOutput { 186 public: 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 318 OpenSoundNode::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 384 OpenSoundNode::~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 403 status_t 404 OpenSoundNode::InitCheck() const 405 { 406 CALLED(); 407 return fInitCheckStatus; 408 } 409 410 411 // #pragma mark - BMediaNode 412 413 414 BMediaAddOn* 415 OpenSoundNode::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 429 void 430 OpenSoundNode::Preroll() 431 { 432 CALLED(); 433 // XXX:Performance opportunity 434 BMediaNode::Preroll(); 435 } 436 437 438 status_t 439 OpenSoundNode::HandleMessage(int32 message, const void* data, size_t size) 440 { 441 CALLED(); 442 return B_ERROR; 443 } 444 445 446 void 447 OpenSoundNode::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 586 status_t 587 OpenSoundNode::RequestCompleted(const media_request_info& info) 588 { 589 CALLED(); 590 return B_OK; 591 } 592 593 594 void 595 OpenSoundNode::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. 608 status_t 609 OpenSoundNode::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 700 status_t 701 OpenSoundNode::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 725 void 726 OpenSoundNode::DisposeInputCookie(int32 cookie) 727 { 728 CALLED(); 729 // nothing to do since our cookies are just integers 730 } 731 732 733 void 734 OpenSoundNode::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 779 void 780 OpenSoundNode::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 803 status_t 804 OpenSoundNode::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 838 status_t 839 OpenSoundNode::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 883 void 884 OpenSoundNode::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. 916 status_t 917 OpenSoundNode::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. 945 status_t 946 OpenSoundNode::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. 963 status_t 964 OpenSoundNode::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. 993 status_t 994 OpenSoundNode::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 1037 status_t 1038 OpenSoundNode::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 1050 status_t 1051 OpenSoundNode::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 1065 status_t 1066 OpenSoundNode::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 1074 status_t 1075 OpenSoundNode::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! 1118 status_t 1119 OpenSoundNode::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 1185 void 1186 OpenSoundNode::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 1257 void 1258 OpenSoundNode::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 1296 void 1297 OpenSoundNode::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 1339 void 1340 OpenSoundNode::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 1357 void 1358 OpenSoundNode::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 1371 void 1372 OpenSoundNode::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 1412 status_t 1413 OpenSoundNode::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 1478 status_t 1479 OpenSoundNode::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 1518 status_t 1519 OpenSoundNode::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 1530 status_t 1531 OpenSoundNode::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 1541 status_t 1542 OpenSoundNode::HandleWarp(const media_timed_event* event, 1543 bigtime_t lateness, bool realTimeEvent) 1544 { 1545 CALLED(); 1546 return B_OK; 1547 } 1548 1549 1550 status_t 1551 OpenSoundNode::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 1563 status_t 1564 OpenSoundNode::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 1575 void 1576 OpenSoundNode::SetRunMode(run_mode mode) 1577 { 1578 CALLED(); 1579 TRACE("OpenSoundNode::SetRunMode(%d)\n", mode); 1580 //BTimeSource::SetRunMode(mode); 1581 } 1582 1583 1584 status_t 1585 OpenSoundNode::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"); 1619 printf("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 1633 status_t 1634 OpenSoundNode::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 1757 void 1758 OpenSoundNode::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 1893 BParameterWeb* 1894 OpenSoundNode::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 1940 void 1941 OpenSoundNode::_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 2104 void 2105 OpenSoundNode::_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 2133 status_t 2134 OpenSoundNode::_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 2148 return 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 2210 int32 2211 OpenSoundNode::_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 2330 int32 2331 OpenSoundNode::_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 2397 status_t 2398 OpenSoundNode::_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 2430 status_t 2431 OpenSoundNode::_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 2452 status_t 2453 OpenSoundNode::_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 2484 status_t 2485 OpenSoundNode::_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 2505 void 2506 OpenSoundNode::_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 2521 BBuffer* 2522 OpenSoundNode::_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 2553 status_t 2554 OpenSoundNode::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 2598 OpenSoundNode::NodeOutput* 2599 OpenSoundNode::_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 2611 OpenSoundNode::NodeInput* 2612 OpenSoundNode::_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 2624 OpenSoundNode::NodeInput* 2625 OpenSoundNode::_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 2640 void 2641 OpenSoundNode::_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 2648 int32 2649 OpenSoundNode::_PlayThreadEntry(void* data) 2650 { 2651 CALLED(); 2652 NodeInput* channel = static_cast<NodeInput*>(data); 2653 return channel->fNode->_PlayThread(channel); 2654 } 2655 2656 2657 int32 2658 OpenSoundNode::_RecThreadEntry(void* data) 2659 { 2660 CALLED(); 2661 NodeOutput* channel = static_cast<NodeOutput*>(data); 2662 return channel->fNode->_RecThread(channel); 2663 } 2664 2665 2666 void 2667 OpenSoundNode::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 2704 void 2705 OpenSoundNode::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