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 fAudioStream->stream->ReadFrames(buffer, &frames); 456 457 int32 frame = fAudioStream->stream->CurrentFrame(); 458 459 fAudioStream->position += fFrameSize * frames; 460 if (fAudioStream->position >= fBufferSize) 461 { 462 // we have filled the enter buffer, time to send 463 write_port(fPort, fBufferSize, &fBuffer[fBufferSize], fBufferSize); 464 fAudioStream->position = 0; 465 } 466 467 if (frame >= fAudioStream->frames) 468 { 469 if (fLooping) 470 { 471 // since we are looping, we need to start reading from 472 // the begining of the file again. 473 int64 firstFrame = 0; 474 fAudioStream->stream->SeekToFrame(&firstFrame); 475 476 fStopping = true; 477 } 478 else fStopping = true; 479 } 480 481 return true; 482 } 483 484 485 bool 486 BFileGameSound::Read(void * buffer, size_t bytes) 487 { 488 return false; 489 } 490 491 492 /* unimplemented for protection of the user: 493 * 494 * BFileGameSound::BFileGameSound() 495 * BFileGameSound::BFileGameSound(const BFileGameSound &) 496 * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &) 497 */ 498 499 500 status_t 501 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...) 502 { 503 return B_ERROR; 504 } 505 506 507 status_t 508 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...) 509 { 510 return B_ERROR; 511 } 512 513 514 status_t 515 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...) 516 { 517 return B_ERROR; 518 } 519 520 521 status_t 522 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...) 523 { 524 return B_ERROR; 525 } 526 527 528 status_t 529 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...) 530 { 531 return B_ERROR; 532 } 533 534 535 status_t 536 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...) 537 { 538 return B_ERROR; 539 } 540 541 542 status_t 543 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...) 544 { 545 return B_ERROR; 546 } 547 548 549 status_t 550 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...) 551 { 552 return B_ERROR; 553 } 554 555 556 status_t 557 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...) 558 { 559 return B_ERROR; 560 } 561 562 563 status_t 564 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...) 565 { 566 return B_ERROR; 567 } 568 569 570 status_t 571 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...) 572 { 573 return B_ERROR; 574 } 575 576 577 status_t 578 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...) 579 { 580 return B_ERROR; 581 } 582 583 584 status_t 585 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...) 586 { 587 return B_ERROR; 588 } 589 590 591 status_t 592 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...) 593 { 594 return B_ERROR; 595 } 596 597 598 status_t 599 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...) 600 { 601 return B_ERROR; 602 } 603 604 605 status_t 606 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...) 607 { 608 return B_ERROR; 609 } 610 611 612 status_t 613 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...) 614 { 615 return B_ERROR; 616 } 617 618 619 status_t 620 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...) 621 { 622 return B_ERROR; 623 } 624 625 626 status_t 627 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...) 628 { 629 return B_ERROR; 630 } 631 632 633 status_t 634 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...) 635 { 636 return B_ERROR; 637 } 638 639 640 status_t 641 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...) 642 { 643 return B_ERROR; 644 } 645 646 647 status_t 648 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...) 649 { 650 return B_ERROR; 651 } 652 653 654 status_t 655 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...) 656 { 657 return B_ERROR; 658 } 659 660 661 status_t 662 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...) 663 { 664 return B_ERROR; 665 } 666 667 668