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