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 printf("mixout %ld, inByteCount %ld, decin %ld, BufferSize %ld\n", 243 out_offset, inByteCount, fPlayPosition, fBufferSize); 244 if (fPlayPosition == 0 || fPlayPosition >= fBufferSize) { 245 Load(); 246 } 247 248 if (fPausing) { 249 Lock(); 250 251 bool rampDone = false; 252 size_t bytes = fBufferSize - fPlayPosition; 253 254 if (bytes > inByteCount) { 255 bytes = inByteCount; 256 } 257 258 // Fill the requested buffer, stopping if the paused flag is set 259 char* buffer = (char*)inBuffer; 260 261 switch(Format().format) { 262 case gs_audio_format::B_GS_U8: 263 rampDone = ::FillBuffer(fPausing, 264 (uint8*)&buffer[out_offset], 265 (uint8*)&fBuffer[fPlayPosition], &bytes); 266 break; 267 268 case gs_audio_format::B_GS_S16: 269 rampDone = ::FillBuffer(fPausing, 270 (int16*)&buffer[out_offset], 271 (int16*)&fBuffer[fPlayPosition], &bytes); 272 break; 273 274 case gs_audio_format::B_GS_S32: 275 rampDone = ::FillBuffer(fPausing, 276 (int32*)&buffer[out_offset], 277 (int32*)&fBuffer[fPlayPosition], &bytes); 278 break; 279 280 case gs_audio_format::B_GS_F: 281 rampDone = ::FillBuffer(fPausing, 282 (float*)&buffer[out_offset], 283 (float*)&fBuffer[fPlayPosition], &bytes); 284 break; 285 } 286 287 inByteCount -= bytes; 288 out_offset += bytes; 289 fPlayPosition += bytes; 290 291 // We finished ramping 292 if (rampDone) { 293 294 // Need to be able to stop asap when pause flag is flipped. 295 while (fPlayPosition < fBufferSize && (inByteCount > 0)) { 296 buffer[out_offset++] = fBuffer[fPlayPosition++]; 297 inByteCount--; 298 } 299 300 delete fPausing; 301 fPausing = NULL; 302 } 303 304 Unlock(); 305 } else { 306 307 char* buffer = (char*)inBuffer; 308 309 // Need to be able to stop asap when the pause flag is flipped. 310 while (fPlayPosition < fBufferSize && (!fPaused || fPausing) 311 && (inByteCount > 0)) { 312 buffer[out_offset++] = fBuffer[fPlayPosition++]; 313 inByteCount--; 314 } 315 } 316 } 317 } 318 } 319 320 321 status_t 322 BFileGameSound::Perform(int32 selector, void* data) 323 { 324 return B_ERROR; 325 } 326 327 328 status_t 329 BFileGameSound::SetPaused(bool isPaused, bigtime_t rampTime) 330 { 331 if (fPaused == isPaused) 332 return EALREADY; 333 334 Lock(); 335 336 // Clear any old ramping 337 delete fPausing; 338 fPausing = NULL; 339 340 if (rampTime > 100000) { 341 // Setup for ramping 342 if (isPaused) { 343 fPausing = InitRamp(&fPauseGain, 0.0, 344 Format().frame_rate, rampTime); 345 } else { 346 fPausing = InitRamp(&fPauseGain, 1.0, 347 Format().frame_rate, rampTime); 348 } 349 } 350 351 fPaused = isPaused; 352 Unlock(); 353 354 return B_OK; 355 } 356 357 358 int32 359 BFileGameSound::IsPaused() 360 { 361 if (fPausing) 362 return B_PAUSE_IN_PROGRESS; 363 364 if (fPaused) 365 return B_PAUSED; 366 367 return B_NOT_PAUSED; 368 } 369 370 371 status_t 372 BFileGameSound::Init(const entry_ref* file) 373 { 374 fAudioStream = new(std::nothrow) _gs_media_tracker; 375 if (!fAudioStream) 376 return B_NO_MEMORY; 377 378 memset(fAudioStream, 0, sizeof(_gs_media_tracker)); 379 fAudioStream->file = new(std::nothrow) BMediaFile(file); 380 if (!fAudioStream->file) { 381 delete fAudioStream; 382 fAudioStream = NULL; 383 return B_NO_MEMORY; 384 } 385 386 status_t error = fAudioStream->file->InitCheck(); 387 if (error != B_OK) 388 return error; 389 390 fAudioStream->stream = fAudioStream->file->TrackAt(0); 391 392 // is this is an audio file? 393 media_format playFormat; 394 if ((error = fAudioStream->stream->EncodedFormat(&playFormat)) != B_OK) { 395 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 396 fAudioStream->stream = NULL; 397 return error; 398 } 399 400 if (!playFormat.IsAudio()) { 401 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 402 fAudioStream->stream = NULL; 403 return B_MEDIA_BAD_FORMAT; 404 } 405 406 gs_audio_format dformat = Device()->Format(); 407 408 // request the format we want the sound 409 memset(&playFormat, 0, sizeof(media_format)); 410 playFormat.type = B_MEDIA_RAW_AUDIO; 411 if (fAudioStream->stream->DecodedFormat(&playFormat) != B_OK) { 412 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 413 fAudioStream->stream = NULL; 414 return B_MEDIA_BAD_FORMAT; 415 } 416 417 // translate the format into a "GameKit" friendly one 418 gs_audio_format gsformat; 419 media_to_gs_format(&gsformat, &playFormat.u.raw_audio); 420 421 // Since the buffer sized read from the file is most likely differnt 422 // then the buffer used by the audio mixer, we must allocate a buffer 423 // large enough to hold the largest request. 424 fBufferSize = gsformat.buffer_size; 425 if (fBufferSize < dformat.buffer_size) 426 fBufferSize = dformat.buffer_size; 427 428 // create the buffer 429 fBuffer = new char[fBufferSize * 2]; 430 memset(fBuffer, 0, fBufferSize * 2); 431 432 fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format); 433 fAudioStream->frames = fAudioStream->stream->CountFrames(); 434 435 // Ask the device to attach our sound to it 436 gs_id sound; 437 error = Device()->CreateBuffer(&sound, this, &gsformat); 438 if (error != B_OK) 439 return error; 440 441 return BGameSound::Init(sound); 442 } 443 444 445 bool 446 BFileGameSound::Load() 447 { 448 if (!fAudioStream || !fAudioStream->stream) 449 return false; 450 451 // read a new buffer 452 int64 frames = 0; 453 fAudioStream->stream->ReadFrames(fBuffer, &frames); 454 fBufferSize = frames * fFrameSize; 455 fPlayPosition = 0; 456 457 if (fBufferSize <= 0) { 458 // EOF 459 if (fLooping) { 460 // start reading next time from the start of the file 461 int64 frame = 0; 462 fAudioStream->stream->SeekToFrame(&frame); 463 } else { 464 StopPlaying(); 465 } 466 } 467 468 return true; 469 } 470 471 472 bool 473 BFileGameSound::Read(void* buffer, size_t bytes) 474 { 475 return false; 476 } 477 478 479 /* unimplemented for protection of the user: 480 * 481 * BFileGameSound::BFileGameSound() 482 * BFileGameSound::BFileGameSound(const BFileGameSound &) 483 * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &) 484 */ 485 486 487 status_t 488 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...) 489 { 490 return B_ERROR; 491 } 492 493 494 status_t 495 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...) 496 { 497 return B_ERROR; 498 } 499 500 501 status_t 502 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...) 503 { 504 return B_ERROR; 505 } 506 507 508 status_t 509 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...) 510 { 511 return B_ERROR; 512 } 513 514 515 status_t 516 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...) 517 { 518 return B_ERROR; 519 } 520 521 522 status_t 523 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...) 524 { 525 return B_ERROR; 526 } 527 528 529 status_t 530 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...) 531 { 532 return B_ERROR; 533 } 534 535 536 status_t 537 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...) 538 { 539 return B_ERROR; 540 } 541 542 543 status_t 544 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...) 545 { 546 return B_ERROR; 547 } 548 549 550 status_t 551 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...) 552 { 553 return B_ERROR; 554 } 555 556 557 status_t 558 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...) 559 { 560 return B_ERROR; 561 } 562 563 564 status_t 565 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...) 566 { 567 return B_ERROR; 568 } 569 570 571 status_t 572 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...) 573 { 574 return B_ERROR; 575 } 576 577 578 status_t 579 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...) 580 { 581 return B_ERROR; 582 } 583 584 585 status_t 586 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...) 587 { 588 return B_ERROR; 589 } 590 591 592 status_t 593 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...) 594 { 595 return B_ERROR; 596 } 597 598 599 status_t 600 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...) 601 { 602 return B_ERROR; 603 } 604 605 606 status_t 607 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...) 608 { 609 return B_ERROR; 610 } 611 612 613 status_t 614 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...) 615 { 616 return B_ERROR; 617 } 618 619 620 status_t 621 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...) 622 { 623 return B_ERROR; 624 } 625 626 627 status_t 628 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...) 629 { 630 return B_ERROR; 631 } 632 633 634 status_t 635 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...) 636 { 637 return B_ERROR; 638 } 639 640 641 status_t 642 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...) 643 { 644 return B_ERROR; 645 } 646 647 648 status_t 649 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...) 650 { 651 return B_ERROR; 652 } 653