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