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