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