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