1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2002, OpenBeOS 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: FileGameSound.cpp 23 // Author: Christopher ML Zumwalt May (zummy@users.sf.net) 24 // Description: BFileGameSound is a class that streams data out of a file. 25 //------------------------------------------------------------------------------ 26 27 // Standard Includes ----------------------------------------------------------- 28 #include <stdlib.h> 29 #include <string.h> 30 31 // System Includes ------------------------------------------------------------- 32 #include <Entry.h> 33 #include <MediaFile.h> 34 #include <MediaTrack.h> 35 #include <scheduler.h> 36 37 // Project Includes ------------------------------------------------------------ 38 #include <GameSoundDevice.h> 39 #include <GSUtility.h> 40 41 // Local Includes -------------------------------------------------------------- 42 #include <FileGameSound.h> 43 44 // Local Defines --------------------------------------------------------------- 45 const int32 kPages = 20; 46 struct _gs_media_tracker 47 { 48 BMediaFile * file; 49 BMediaTrack * stream; 50 int64 frames; 51 size_t position; 52 }; 53 54 // Locale utility functions ----------------------------------------------- 55 bool FillBuffer(_gs_ramp * ramp, uint8 * data, uint8 * buffer, size_t * bytes) 56 { 57 int32 samples = *bytes / sizeof(int16); 58 59 for(int32 byte = 0; byte < samples; byte++) 60 { 61 float gain = *ramp->value; 62 data[byte] = uint8(float(buffer[byte]) * gain); 63 64 if (ChangeRamp(ramp)) 65 { 66 *bytes = byte * sizeof(int16); 67 return true; 68 } 69 } 70 71 return false; 72 } 73 74 bool FillBuffer(_gs_ramp * ramp, int16 * data, int16 * buffer, size_t * bytes) 75 { 76 int32 samples = *bytes / sizeof(int16); 77 78 for(int32 byte = 0; byte < samples; byte++) 79 { 80 float gain = *ramp->value; 81 data[byte] = int16(float(buffer[byte]) * gain); 82 83 if (ChangeRamp(ramp)) 84 { 85 *bytes = byte * sizeof(int16); 86 return true; 87 } 88 } 89 90 return false; 91 } 92 93 bool FillBuffer(_gs_ramp * ramp, int32 * data, int32 * buffer, size_t * bytes) 94 { 95 size_t byte = 0; 96 bool bytesAreReady = (*bytes > 0); 97 98 while(bytesAreReady) 99 { 100 float gain = *ramp->value; 101 data[byte] = int32(float(buffer[byte]) * gain); 102 103 if (ChangeRamp(ramp)) 104 { 105 *bytes = byte; 106 return true; 107 } 108 109 byte++; 110 bytesAreReady = (byte >= *bytes); 111 } 112 113 return false; 114 } 115 116 bool FillBuffer(_gs_ramp * ramp, float * data, float * buffer, size_t * bytes) 117 { 118 size_t byte = 0; 119 bool bytesAreReady = (*bytes > 0); 120 121 while(bytesAreReady) 122 { 123 float gain = *ramp->value; 124 data[byte] = buffer[byte] * gain; 125 126 if (ChangeRamp(ramp)) 127 { 128 *bytes = byte; 129 return true; 130 } 131 132 byte++; 133 bytesAreReady = (byte >= *bytes); 134 } 135 136 return false; 137 } 138 139 // BFileGameSound ------------------------------------------------------- 140 BFileGameSound::BFileGameSound(const entry_ref *file, 141 bool looping, 142 BGameSoundDevice *device) 143 : BStreamingGameSound(device), 144 fAudioStream(NULL), 145 fStopping(false), 146 fLooping(looping), 147 fBuffer(NULL), 148 fPlayPosition(0), 149 fPausing(NULL), 150 fPaused(false), 151 fPauseGain(1.0) 152 { 153 if (InitCheck() == B_OK) SetInitError(Init(file)); 154 } 155 156 157 BFileGameSound::BFileGameSound(const char *file, 158 bool looping, 159 BGameSoundDevice *device) 160 : BStreamingGameSound(device), 161 fAudioStream(NULL), 162 fStopping(false), 163 fLooping(looping), 164 fBuffer(NULL), 165 fPlayPosition(0), 166 fPausing(NULL), 167 fPaused(false), 168 fPauseGain(1.0) 169 { 170 if (InitCheck() == B_OK) 171 { 172 entry_ref node; 173 174 if (get_ref_for_path(file, &node) != B_OK) 175 SetInitError(B_ENTRY_NOT_FOUND); 176 else 177 SetInitError(Init(&node)); 178 } 179 } 180 181 182 BFileGameSound::~BFileGameSound() 183 { 184 if (fReadThread >= 0) kill_thread(fReadThread); 185 186 if (fAudioStream) 187 { 188 if (fAudioStream->stream) fAudioStream->file->ReleaseTrack(fAudioStream->stream); 189 190 fAudioStream->file->CloseFile(); 191 192 //delete fAudioStream->stream; 193 delete fAudioStream->file; 194 } 195 196 delete [] fBuffer; 197 delete fAudioStream; 198 } 199 200 201 BGameSound * 202 BFileGameSound::Clone() const 203 { 204 return NULL; 205 } 206 207 208 status_t 209 BFileGameSound::StartPlaying() 210 { 211 // restart playback if needed 212 if (IsPlaying()) StopPlaying(); 213 214 fPort = create_port(kPages, "audioque"); 215 216 // create the thread that will read the file 217 fReadThread = spawn_thread(ReadThread, "audiostream", B_NORMAL_PRIORITY, this); 218 if (fReadThread < B_OK) return B_NO_MORE_THREADS; 219 220 status_t error = resume_thread(fReadThread); 221 if (error != B_OK) return error; 222 223 // start playing the file 224 return BStreamingGameSound::StartPlaying(); 225 } 226 227 228 status_t 229 BFileGameSound::StopPlaying() 230 { 231 status_t error = BStreamingGameSound::StopPlaying(); 232 233 // start reading next time from the start of the file 234 int64 frame = 0; 235 fAudioStream->stream->SeekToFrame(&frame); 236 237 fStopping = false; 238 fAudioStream->position = 0; 239 fPlayPosition = 0; 240 241 // we don't need to read any more 242 kill_thread(fReadThread); 243 delete_port(fPort); 244 245 return error; 246 } 247 248 249 status_t 250 BFileGameSound::Preload() 251 { 252 if (!IsPlaying()) 253 { 254 for(int32 i = 0; i < kPages / 2; i++) Load(); 255 } 256 257 return B_OK; 258 } 259 260 261 void 262 BFileGameSound::FillBuffer(void *inBuffer, 263 size_t inByteCount) 264 { 265 int32 cookie; 266 size_t bytes = inByteCount; 267 268 if (!fPaused || fPausing) 269 { 270 // time to read a new buffer 271 if (fPlayPosition == 0) read_port_etc(fPort, &cookie, fBuffer, fBufferSize, B_TIMEOUT, 0); 272 273 if (fPausing) 274 { 275 Lock(); 276 277 bool rampDone = false; 278 279 // Fill the requsted buffer, stopping if the paused flag is set 280 switch(Format().format) 281 { 282 case gs_audio_format::B_GS_U8: 283 rampDone = ::FillBuffer(fPausing, (uint8*)inBuffer, (uint8*)&fBuffer[fPlayPosition], &bytes); 284 break; 285 286 case gs_audio_format::B_GS_S16: 287 rampDone = ::FillBuffer(fPausing, (int16*)inBuffer, (int16*)&fBuffer[fPlayPosition], &bytes); 288 break; 289 290 case gs_audio_format::B_GS_S32: 291 rampDone = ::FillBuffer(fPausing, (int32*)inBuffer, (int32*)&fBuffer[fPlayPosition], &bytes); 292 break; 293 294 case gs_audio_format::B_GS_F: 295 rampDone = ::FillBuffer(fPausing, (float*)inBuffer, (float*)&fBuffer[fPlayPosition], &bytes); 296 break; 297 } 298 299 // We finished ramping 300 if (rampDone) 301 { 302 if (bytes < inByteCount && !fPausing) 303 { 304 // Since are resumming play back, we need to copy any remaining samples 305 char * buffer = (char*)inBuffer; 306 memcpy(&buffer[bytes], &fBuffer[fPlayPosition + bytes], inByteCount - bytes); 307 } 308 309 delete fPausing; 310 fPausing = NULL; 311 } 312 313 Unlock(); 314 } 315 else 316 { 317 size_t byte = 0; 318 char * buffer = (char*)inBuffer; 319 320 // We need to be able to stop asap when the pause flag is flipped. 321 while(byte < bytes && (!fPaused || fPausing)) 322 { 323 buffer[byte] = fBuffer[fPlayPosition + byte]; 324 byte++; 325 } 326 327 bytes = byte; 328 } 329 } 330 331 fPlayPosition += bytes; 332 if (fPlayPosition >= fBufferSize) 333 { 334 // We have finished reading the buffer. Setup for the next buffer. 335 fPlayPosition = 0; 336 memset(fBuffer, 0, fBufferSize); 337 } 338 } 339 340 341 status_t 342 BFileGameSound::Perform(int32 selector, 343 void *data) 344 { 345 return B_ERROR; 346 } 347 348 349 status_t 350 BFileGameSound::SetPaused(bool isPaused, 351 bigtime_t rampTime) 352 { 353 if (fPaused != isPaused) 354 { 355 Lock(); 356 357 // Clear any old ramping 358 delete fPausing; 359 fPausing = NULL; 360 361 if (rampTime > 100000) 362 { 363 // Setup for ramping 364 if (isPaused) 365 fPausing = InitRamp(&fPauseGain, 0.0, Format().frame_rate, rampTime); 366 else 367 fPausing = InitRamp(&fPauseGain, 1.0, Format().frame_rate, rampTime); 368 } 369 370 fPaused = isPaused; 371 Unlock(); 372 } 373 374 return B_OK; 375 } 376 377 378 int32 379 BFileGameSound::IsPaused() 380 { 381 if (fPausing) return B_PAUSE_IN_PROGRESS; 382 383 if (fPaused) return B_PAUSED; 384 385 return B_NOT_PAUSED; 386 } 387 388 389 status_t 390 BFileGameSound::Init(const entry_ref* file) 391 { 392 fAudioStream = new _gs_media_tracker; 393 memset(fAudioStream, 0, sizeof(_gs_media_tracker)); 394 395 fAudioStream->file = new BMediaFile(file); 396 status_t error = fAudioStream->file->InitCheck(); 397 if (error != B_OK) return error; 398 399 fAudioStream->stream = fAudioStream->file->TrackAt(0); 400 401 // is this is an audio file? 402 media_format mformat; 403 fAudioStream->stream->EncodedFormat(&mformat); 404 if (!mformat.IsAudio()) return B_MEDIA_BAD_FORMAT; 405 406 gs_audio_format dformat = Device()->Format(); 407 408 // request the format we want the sound 409 memset(&mformat, 0, sizeof(media_format)); 410 mformat.type = B_MEDIA_RAW_AUDIO; 411 fAudioStream->stream->DecodedFormat(&mformat); 412 413 // translate the format into a "GameKit" friendly one 414 gs_audio_format gsformat; 415 media_to_gs_format(&gsformat, &mformat.u.raw_audio); 416 417 // Since the buffer sized read from the file is most likely differnt 418 // then the buffer used by the audio mixer, we must allocate a buffer 419 // large enough to hold the largest request. 420 fBufferSize = gsformat.buffer_size; 421 if (fBufferSize < dformat.buffer_size) fBufferSize = dformat.buffer_size; 422 423 // create the buffer 424 fBuffer = new char[fBufferSize * 2]; 425 memset(fBuffer, 0, fBufferSize * 2); 426 427 fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format); 428 fAudioStream->frames = fAudioStream->stream->CountFrames(); 429 430 // Ask the device to attach our sound to it 431 gs_id sound; 432 error = Device()->CreateBuffer(&sound, this, &gsformat); 433 if (error != B_OK) return error; 434 435 return BGameSound::Init(sound); 436 } 437 438 439 int32 440 BFileGameSound::ReadThread(void* arg) 441 { 442 BFileGameSound* obj = (BFileGameSound*)arg; 443 444 while(true) obj->Load(); 445 446 return 0; 447 } 448 449 450 bool 451 BFileGameSound::Load() 452 { 453 int64 frames; 454 char * buffer = &fBuffer[fBufferSize + fAudioStream->position]; 455 status_t err = fAudioStream->stream->ReadFrames(buffer, &frames); 456 if (err < B_OK) { 457 StopPlaying(); // XXX this is a hack, the whole class design is broken 458 } 459 460 int32 frame = fAudioStream->stream->CurrentFrame(); 461 462 fAudioStream->position += fFrameSize * frames; 463 if (fAudioStream->position >= fBufferSize) 464 { 465 // we have filled the enter buffer, time to send 466 write_port(fPort, fBufferSize, &fBuffer[fBufferSize], fBufferSize); 467 fAudioStream->position = 0; 468 } 469 470 if (frame >= fAudioStream->frames) 471 { 472 if (fLooping) 473 { 474 // since we are looping, we need to start reading from 475 // the begining of the file again. 476 int64 firstFrame = 0; 477 fAudioStream->stream->SeekToFrame(&firstFrame); 478 479 fStopping = true; 480 } 481 else fStopping = true; 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 670 671