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