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