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