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