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