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