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