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