1 /* 2 * Copyright 2001-2012 Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Christopher ML Zumwalt May (zummy@users.sf.net) 7 * Jérôme Duval 8 */ 9 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <Entry.h> 16 #include <File.h> 17 #include <FileGameSound.h> 18 #include <MediaFile.h> 19 #include <MediaTrack.h> 20 #include <scheduler.h> 21 22 #include "GameSoundDevice.h" 23 #include "GSUtility.h" 24 25 26 struct _gs_media_tracker { 27 BMediaFile* file; 28 BMediaTrack* stream; 29 int64 frames; 30 size_t position; 31 }; 32 33 34 // Local utility functions ----------------------------------------------- 35 bool 36 FillBuffer(_gs_ramp* ramp, uint8* data, uint8* buffer, size_t* bytes) 37 { 38 int32 samples = *bytes / sizeof(uint8); 39 40 for (int32 byte = 0; byte < samples; byte++) { 41 float gain = *ramp->value; 42 data[byte] = uint8(float(buffer[byte]) * gain); 43 44 if (ChangeRamp(ramp)) { 45 *bytes = byte * sizeof(uint8); 46 return true; 47 } 48 } 49 50 return false; 51 } 52 53 54 bool 55 FillBuffer(_gs_ramp* ramp, int16* data, int16* buffer, size_t* bytes) 56 { 57 int32 samples = *bytes / sizeof(int16); 58 59 for (int32 byte = 0; byte < samples; byte++) { 60 float gain = *ramp->value; 61 data[byte] = int16(float(buffer[byte]) * gain); 62 63 if (ChangeRamp(ramp)) { 64 *bytes = byte * sizeof(int16); 65 return true; 66 } 67 } 68 69 return false; 70 } 71 72 73 bool 74 FillBuffer(_gs_ramp* ramp, int32* data, int32* buffer, size_t* bytes) 75 { 76 size_t byte = 0; 77 bool bytesAreReady = (*bytes > 0); 78 79 while (bytesAreReady) { 80 float gain = *ramp->value; 81 data[byte] = int32(float(buffer[byte]) * gain); 82 83 if (ChangeRamp(ramp)) { 84 *bytes = byte; 85 return true; 86 } 87 88 byte++; 89 bytesAreReady = (byte >= *bytes); 90 } 91 92 return false; 93 } 94 95 96 bool 97 FillBuffer(_gs_ramp* ramp, float* data, float* buffer, size_t* bytes) 98 { 99 size_t byte = 0; 100 bool bytesAreReady = (*bytes > 0); 101 102 while (bytesAreReady) { 103 float gain = *ramp->value; 104 data[byte] = buffer[byte] * gain; 105 106 if (ChangeRamp(ramp)) { 107 *bytes = byte; 108 return true; 109 } 110 111 byte++; 112 bytesAreReady = (byte >= *bytes); 113 } 114 115 return false; 116 } 117 118 119 // BFileGameSound ------------------------------------------------------- 120 BFileGameSound::BFileGameSound(const entry_ref* file, bool looping, 121 BGameSoundDevice* device) 122 : 123 BStreamingGameSound(device), 124 fAudioStream(NULL), 125 fStopping(false), 126 fLooping(looping), 127 fBuffer(NULL), 128 fPlayPosition(0), 129 fPausing(NULL), 130 fPaused(false), 131 fPauseGain(1.0) 132 { 133 if (InitCheck() == B_OK) 134 SetInitError(Init(new(std::nothrow) BFile(file, B_READ_ONLY))); 135 } 136 137 138 BFileGameSound::BFileGameSound(const char* file, bool looping, 139 BGameSoundDevice* device) 140 : 141 BStreamingGameSound(device), 142 fAudioStream(NULL), 143 fStopping(false), 144 fLooping(looping), 145 fBuffer(NULL), 146 fPlayPosition(0), 147 fPausing(NULL), 148 fPaused(false), 149 fPauseGain(1.0) 150 { 151 if (InitCheck() == B_OK) { 152 entry_ref node; 153 154 if (get_ref_for_path(file, &node) != B_OK) 155 SetInitError(B_ENTRY_NOT_FOUND); 156 else { 157 BFile* file = new(std::nothrow) BFile(&node, B_READ_ONLY); 158 SetInitError(Init(file)); 159 } 160 } 161 } 162 163 164 BFileGameSound::BFileGameSound(BDataIO* data, bool looping, 165 BGameSoundDevice* device) 166 : 167 BStreamingGameSound(device), 168 fAudioStream(NULL), 169 fStopping(false), 170 fLooping(looping), 171 fBuffer(NULL), 172 fPlayPosition(0), 173 fPausing(NULL), 174 fPaused(false), 175 fPauseGain(1.0) 176 { 177 if (InitCheck() == B_OK) 178 SetInitError(Init(data)); 179 } 180 181 182 BFileGameSound::~BFileGameSound() 183 { 184 if (fAudioStream != NULL) { 185 if (fAudioStream->stream != NULL) 186 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 187 188 delete fAudioStream->file; 189 } 190 191 delete [] fBuffer; 192 delete fAudioStream; 193 delete fDataSource; 194 } 195 196 197 BGameSound* 198 BFileGameSound::Clone() const 199 { 200 return NULL; 201 } 202 203 204 status_t 205 BFileGameSound::StartPlaying() 206 { 207 // restart playback if needed 208 if (IsPlaying()) 209 StopPlaying(); 210 211 // start playing the file 212 return BStreamingGameSound::StartPlaying(); 213 } 214 215 216 status_t 217 BFileGameSound::StopPlaying() 218 { 219 status_t error = BStreamingGameSound::StopPlaying(); 220 221 if (fAudioStream == NULL || fAudioStream->stream == NULL) 222 return B_OK; 223 224 // start reading next time from the start of the file 225 int64 frame = 0; 226 fAudioStream->stream->SeekToFrame(&frame); 227 228 fStopping = false; 229 fAudioStream->position = 0; 230 fPlayPosition = 0; 231 232 return error; 233 } 234 235 236 status_t 237 BFileGameSound::Preload() 238 { 239 if (!IsPlaying()) 240 Load(); 241 242 return B_OK; 243 } 244 245 246 void 247 BFileGameSound::FillBuffer(void* inBuffer, size_t inByteCount) 248 { 249 // Split or combine decoder buffers into mixer buffers 250 // fPlayPosition is where we got up to in the input buffer after last call 251 252 size_t out_offset = 0; 253 254 while (inByteCount > 0 && !fPaused) { 255 if (!fPaused || fPausing) { 256 if (fPlayPosition == 0 || fPlayPosition >= fBufferSize) { 257 Load(); 258 } 259 260 if (fPausing) { 261 Lock(); 262 263 bool rampDone = false; 264 size_t bytes = fBufferSize - fPlayPosition; 265 266 if (bytes > inByteCount) { 267 bytes = inByteCount; 268 } 269 270 // Fill the requested buffer, stopping if the paused flag is set 271 char* buffer = (char*)inBuffer; 272 273 switch(Format().format) { 274 case gs_audio_format::B_GS_U8: 275 rampDone = ::FillBuffer(fPausing, 276 (uint8*)&buffer[out_offset], 277 (uint8*)&fBuffer[fPlayPosition], &bytes); 278 break; 279 280 case gs_audio_format::B_GS_S16: 281 rampDone = ::FillBuffer(fPausing, 282 (int16*)&buffer[out_offset], 283 (int16*)&fBuffer[fPlayPosition], &bytes); 284 break; 285 286 case gs_audio_format::B_GS_S32: 287 rampDone = ::FillBuffer(fPausing, 288 (int32*)&buffer[out_offset], 289 (int32*)&fBuffer[fPlayPosition], &bytes); 290 break; 291 292 case gs_audio_format::B_GS_F: 293 rampDone = ::FillBuffer(fPausing, 294 (float*)&buffer[out_offset], 295 (float*)&fBuffer[fPlayPosition], &bytes); 296 break; 297 } 298 299 inByteCount -= bytes; 300 out_offset += bytes; 301 fPlayPosition += bytes; 302 303 // We finished ramping 304 if (rampDone) { 305 306 // Need to be able to stop asap when pause flag is flipped. 307 while (fPlayPosition < fBufferSize && (inByteCount > 0)) { 308 buffer[out_offset++] = fBuffer[fPlayPosition++]; 309 inByteCount--; 310 } 311 312 delete fPausing; 313 fPausing = NULL; 314 } 315 316 Unlock(); 317 } else { 318 319 char* buffer = (char*)inBuffer; 320 321 // Need to be able to stop asap when the pause flag is flipped. 322 while (fPlayPosition < fBufferSize && (!fPaused || fPausing) 323 && (inByteCount > 0)) { 324 buffer[out_offset++] = fBuffer[fPlayPosition++]; 325 inByteCount--; 326 } 327 } 328 } 329 } 330 } 331 332 333 status_t 334 BFileGameSound::Perform(int32 selector, void* data) 335 { 336 return B_ERROR; 337 } 338 339 340 status_t 341 BFileGameSound::SetPaused(bool isPaused, bigtime_t rampTime) 342 { 343 if (fPaused == isPaused) 344 return EALREADY; 345 346 Lock(); 347 348 // Clear any old ramping 349 delete fPausing; 350 fPausing = NULL; 351 352 if (rampTime > 100000) { 353 // Setup for ramping 354 if (isPaused) { 355 fPausing = InitRamp(&fPauseGain, 0.0, 356 Format().frame_rate, rampTime); 357 } else { 358 fPausing = InitRamp(&fPauseGain, 1.0, 359 Format().frame_rate, rampTime); 360 } 361 } 362 363 fPaused = isPaused; 364 Unlock(); 365 366 return B_OK; 367 } 368 369 370 int32 371 BFileGameSound::IsPaused() 372 { 373 if (fPausing) 374 return B_PAUSE_IN_PROGRESS; 375 376 if (fPaused) 377 return B_PAUSED; 378 379 return B_NOT_PAUSED; 380 } 381 382 383 status_t 384 BFileGameSound::Init(BDataIO* data) 385 { 386 fDataSource = data; 387 if (fDataSource == NULL) 388 return B_NO_MEMORY; 389 390 fAudioStream = new(std::nothrow) _gs_media_tracker; 391 if (fAudioStream == NULL) 392 return B_NO_MEMORY; 393 394 memset(fAudioStream, 0, sizeof(_gs_media_tracker)); 395 fAudioStream->file = new(std::nothrow) BMediaFile(data); 396 if (fAudioStream->file == NULL) { 397 delete fAudioStream; 398 fAudioStream = NULL; 399 return B_NO_MEMORY; 400 } 401 402 status_t error = fAudioStream->file->InitCheck(); 403 if (error != B_OK) 404 return error; 405 406 fAudioStream->stream = fAudioStream->file->TrackAt(0); 407 408 // is this is an audio file? 409 media_format playFormat; 410 if ((error = fAudioStream->stream->EncodedFormat(&playFormat)) != B_OK) { 411 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 412 fAudioStream->stream = NULL; 413 return error; 414 } 415 416 if (!playFormat.IsAudio()) { 417 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 418 fAudioStream->stream = NULL; 419 return B_MEDIA_BAD_FORMAT; 420 } 421 422 gs_audio_format dformat = Device()->Format(); 423 424 // request the format we want the sound 425 playFormat.Clear(); 426 playFormat.type = B_MEDIA_RAW_AUDIO; 427 if (fAudioStream->stream->DecodedFormat(&playFormat) != B_OK) { 428 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 429 fAudioStream->stream = NULL; 430 return B_MEDIA_BAD_FORMAT; 431 } 432 433 // translate the format into a "GameKit" friendly one 434 gs_audio_format gsformat; 435 media_to_gs_format(&gsformat, &playFormat.u.raw_audio); 436 437 // Since the buffer sized read from the file is most likely differnt 438 // then the buffer used by the audio mixer, we must allocate a buffer 439 // large enough to hold the largest request. 440 fBufferSize = gsformat.buffer_size; 441 if (fBufferSize < dformat.buffer_size) 442 fBufferSize = dformat.buffer_size; 443 444 // create the buffer 445 fBuffer = new char[fBufferSize * 2]; 446 memset(fBuffer, 0, fBufferSize * 2); 447 448 fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format); 449 fAudioStream->frames = fAudioStream->stream->CountFrames(); 450 451 // Ask the device to attach our sound to it 452 gs_id sound; 453 error = Device()->CreateBuffer(&sound, this, &gsformat); 454 if (error != B_OK) 455 return error; 456 457 return BGameSound::Init(sound); 458 } 459 460 461 bool 462 BFileGameSound::Load() 463 { 464 if (fAudioStream == NULL || fAudioStream->stream == NULL) 465 return false; 466 467 // read a new buffer 468 int64 frames = 0; 469 fAudioStream->stream->ReadFrames(fBuffer, &frames); 470 fBufferSize = frames * fFrameSize; 471 fPlayPosition = 0; 472 473 if (fBufferSize <= 0) { 474 // EOF 475 if (fLooping) { 476 // start reading next time from the start of the file 477 int64 frame = 0; 478 fAudioStream->stream->SeekToFrame(&frame); 479 } else { 480 StopPlaying(); 481 } 482 } 483 484 return true; 485 } 486 487 488 bool 489 BFileGameSound::Read(void* buffer, size_t bytes) 490 { 491 return false; 492 } 493 494 495 /* unimplemented for protection of the user: 496 * 497 * BFileGameSound::BFileGameSound() 498 * BFileGameSound::BFileGameSound(const BFileGameSound &) 499 * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &) 500 */ 501 502 503 status_t 504 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...) 505 { 506 return B_ERROR; 507 } 508 509 510 status_t 511 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...) 512 { 513 return B_ERROR; 514 } 515 516 517 status_t 518 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...) 519 { 520 return B_ERROR; 521 } 522 523 524 status_t 525 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...) 526 { 527 return B_ERROR; 528 } 529 530 531 status_t 532 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...) 533 { 534 return B_ERROR; 535 } 536 537 538 status_t 539 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...) 540 { 541 return B_ERROR; 542 } 543 544 545 status_t 546 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...) 547 { 548 return B_ERROR; 549 } 550 551 552 status_t 553 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...) 554 { 555 return B_ERROR; 556 } 557 558 559 status_t 560 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...) 561 { 562 return B_ERROR; 563 } 564 565 566 status_t 567 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...) 568 { 569 return B_ERROR; 570 } 571 572 573 status_t 574 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...) 575 { 576 return B_ERROR; 577 } 578 579 580 status_t 581 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...) 582 { 583 return B_ERROR; 584 } 585 586 587 status_t 588 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...) 589 { 590 return B_ERROR; 591 } 592 593 594 status_t 595 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...) 596 { 597 return B_ERROR; 598 } 599 600 601 status_t 602 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...) 603 { 604 return B_ERROR; 605 } 606 607 608 status_t 609 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...) 610 { 611 return B_ERROR; 612 } 613 614 615 status_t 616 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...) 617 { 618 return B_ERROR; 619 } 620 621 622 status_t 623 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...) 624 { 625 return B_ERROR; 626 } 627 628 629 status_t 630 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...) 631 { 632 return B_ERROR; 633 } 634 635 636 status_t 637 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...) 638 { 639 return B_ERROR; 640 } 641 642 643 status_t 644 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...) 645 { 646 return B_ERROR; 647 } 648 649 650 status_t 651 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...) 652 { 653 return B_ERROR; 654 } 655 656 657 status_t 658 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...) 659 { 660 return B_ERROR; 661 } 662 663 664 status_t 665 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...) 666 { 667 return B_ERROR; 668 } 669