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 kill_thread(fReadThread); 156 157 if (fAudioStream) { 158 if (fAudioStream->stream) 159 fAudioStream->file->ReleaseTrack(fAudioStream->stream); 160 161 delete fAudioStream->file; 162 } 163 164 delete [] fBuffer; 165 delete fAudioStream; 166 } 167 168 169 BGameSound * 170 BFileGameSound::Clone() const 171 { 172 return NULL; 173 } 174 175 176 status_t 177 BFileGameSound::StartPlaying() 178 { 179 // restart playback if needed 180 if (IsPlaying()) 181 StopPlaying(); 182 183 // start playing the file 184 return BStreamingGameSound::StartPlaying(); 185 } 186 187 188 status_t 189 BFileGameSound::StopPlaying() 190 { 191 status_t error = BStreamingGameSound::StopPlaying(); 192 193 if (!fAudioStream || !fAudioStream->stream) 194 return B_OK; 195 196 // start reading next time from the start of the file 197 int64 frame = 0; 198 fAudioStream->stream->SeekToFrame(&frame); 199 200 fStopping = false; 201 fAudioStream->position = 0; 202 fPlayPosition = 0; 203 204 return error; 205 } 206 207 208 status_t 209 BFileGameSound::Preload() 210 { 211 if (!IsPlaying()) 212 Load(); 213 214 return B_OK; 215 } 216 217 218 void 219 BFileGameSound::FillBuffer(void *inBuffer, 220 size_t inByteCount) 221 { 222 // Split or combine decoder buffers into mixer buffers 223 // fPlayPosition is where we got up to in the input buffer after last call 224 225 size_t out_offset = 0; 226 227 while (inByteCount > 0 && !fPaused) { 228 if (!fPaused || fPausing) { 229 printf("mixout %ld, inByteCount %ld, decin %ld, BufferSize %ld\n",out_offset, inByteCount, fPlayPosition, fBufferSize); 230 if (fPlayPosition == 0 || fPlayPosition >= fBufferSize) { 231 Load(); 232 } 233 234 if (fPausing) { 235 Lock(); 236 237 bool rampDone = false; 238 size_t bytes = fBufferSize - fPlayPosition; 239 240 if (bytes > inByteCount) { 241 bytes = inByteCount; 242 } 243 244 // Fill the requested buffer, stopping if the paused flag is set 245 char * buffer = (char*)inBuffer; 246 247 switch(Format().format) { 248 case gs_audio_format::B_GS_U8: 249 rampDone = ::FillBuffer(fPausing, (uint8*)&buffer[out_offset], (uint8*)&fBuffer[fPlayPosition], &bytes); 250 break; 251 252 case gs_audio_format::B_GS_S16: 253 rampDone = ::FillBuffer(fPausing, (int16*)&buffer[out_offset], (int16*)&fBuffer[fPlayPosition], &bytes); 254 break; 255 256 case gs_audio_format::B_GS_S32: 257 rampDone = ::FillBuffer(fPausing, (int32*)&buffer[out_offset], (int32*)&fBuffer[fPlayPosition], &bytes); 258 break; 259 260 case gs_audio_format::B_GS_F: 261 rampDone = ::FillBuffer(fPausing, (float*)&buffer[out_offset], (float*)&fBuffer[fPlayPosition], &bytes); 262 break; 263 } 264 265 inByteCount -= bytes; 266 out_offset += bytes; 267 fPlayPosition += bytes; 268 269 // We finished ramping 270 if (rampDone) { 271 272 // We need to be able to stop asap when the pause flag is flipped. 273 while(fPlayPosition < fBufferSize && (inByteCount > 0)) { 274 buffer[out_offset++] = fBuffer[fPlayPosition++]; 275 inByteCount--; 276 } 277 278 delete fPausing; 279 fPausing = NULL; 280 } 281 282 Unlock(); 283 } else { 284 285 char * buffer = (char*)inBuffer; 286 287 // We need to be able to stop asap when the pause flag is flipped. 288 while(fPlayPosition < fBufferSize && (!fPaused || fPausing) && (inByteCount > 0)) { 289 buffer[out_offset++] = fBuffer[fPlayPosition++]; 290 inByteCount--; 291 } 292 } 293 } 294 } 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 Lock(); 312 313 // Clear any old ramping 314 delete fPausing; 315 fPausing = NULL; 316 317 if (rampTime > 100000) { 318 // Setup for ramping 319 if (isPaused) 320 fPausing = InitRamp(&fPauseGain, 0.0, 321 Format().frame_rate, rampTime); 322 else 323 fPausing = InitRamp(&fPauseGain, 1.0, 324 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) 339 return B_PAUSE_IN_PROGRESS; 340 341 if (fPaused) 342 return B_PAUSED; 343 344 return B_NOT_PAUSED; 345 } 346 347 348 status_t 349 BFileGameSound::Init(const entry_ref* file) 350 { 351 fAudioStream = new _gs_media_tracker; 352 memset(fAudioStream, 0, sizeof(_gs_media_tracker)); 353 354 fAudioStream->file = new BMediaFile(file); 355 status_t error = fAudioStream->file->InitCheck(); 356 if (error != B_OK) 357 return error; 358 359 fAudioStream->stream = fAudioStream->file->TrackAt(0); 360 361 // is this is an audio file? 362 media_format playFormat; 363 if ((error = fAudioStream->stream->EncodedFormat(&playFormat)) != B_OK) 364 return error; 365 366 if (!playFormat.IsAudio()) 367 return B_MEDIA_BAD_FORMAT; 368 369 gs_audio_format dformat = Device()->Format(); 370 371 // request the format we want the sound 372 memset(&playFormat, 0, sizeof(media_format)); 373 playFormat.type = B_MEDIA_RAW_AUDIO; 374 if (fAudioStream->stream->DecodedFormat(&playFormat) != B_OK) 375 return B_MEDIA_BAD_FORMAT; 376 377 // translate the format into a "GameKit" friendly one 378 gs_audio_format gsformat; 379 media_to_gs_format(&gsformat, &playFormat.u.raw_audio); 380 381 // Since the buffer sized read from the file is most likely differnt 382 // then the buffer used by the audio mixer, we must allocate a buffer 383 // large enough to hold the largest request. 384 fBufferSize = gsformat.buffer_size; 385 if (fBufferSize < dformat.buffer_size) 386 fBufferSize = dformat.buffer_size; 387 388 // create the buffer 389 fBuffer = new char[fBufferSize * 2]; 390 memset(fBuffer, 0, fBufferSize * 2); 391 392 fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format); 393 fAudioStream->frames = fAudioStream->stream->CountFrames(); 394 395 // Ask the device to attach our sound to it 396 gs_id sound; 397 error = Device()->CreateBuffer(&sound, this, &gsformat); 398 if (error != B_OK) 399 return error; 400 401 return BGameSound::Init(sound); 402 } 403 404 405 bool 406 BFileGameSound::Load() 407 { 408 // read a new buffer 409 int64 frames = 0; 410 fAudioStream->stream->ReadFrames(fBuffer, &frames); 411 fBufferSize = frames * fFrameSize; 412 fPlayPosition = 0; 413 414 if (fBufferSize <= 0) { 415 // EOF 416 if (fLooping) { 417 // start reading next time from the start of the file 418 int64 frame = 0; 419 fAudioStream->stream->SeekToFrame(&frame); 420 } else { 421 StopPlaying(); 422 } 423 } 424 425 return true; 426 } 427 428 429 bool 430 BFileGameSound::Read(void * buffer, size_t bytes) 431 { 432 return false; 433 } 434 435 436 /* unimplemented for protection of the user: 437 * 438 * BFileGameSound::BFileGameSound() 439 * BFileGameSound::BFileGameSound(const BFileGameSound &) 440 * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &) 441 */ 442 443 444 status_t 445 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...) 446 { 447 return B_ERROR; 448 } 449 450 451 status_t 452 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...) 453 { 454 return B_ERROR; 455 } 456 457 458 status_t 459 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...) 460 { 461 return B_ERROR; 462 } 463 464 465 status_t 466 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...) 467 { 468 return B_ERROR; 469 } 470 471 472 status_t 473 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...) 474 { 475 return B_ERROR; 476 } 477 478 479 status_t 480 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...) 481 { 482 return B_ERROR; 483 } 484 485 486 status_t 487 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...) 488 { 489 return B_ERROR; 490 } 491 492 493 status_t 494 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...) 495 { 496 return B_ERROR; 497 } 498 499 500 status_t 501 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...) 502 { 503 return B_ERROR; 504 } 505 506 507 status_t 508 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...) 509 { 510 return B_ERROR; 511 } 512 513 514 status_t 515 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...) 516 { 517 return B_ERROR; 518 } 519 520 521 status_t 522 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...) 523 { 524 return B_ERROR; 525 } 526 527 528 status_t 529 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...) 530 { 531 return B_ERROR; 532 } 533 534 535 status_t 536 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...) 537 { 538 return B_ERROR; 539 } 540 541 542 status_t 543 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...) 544 { 545 return B_ERROR; 546 } 547 548 549 status_t 550 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...) 551 { 552 return B_ERROR; 553 } 554 555 556 status_t 557 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...) 558 { 559 return B_ERROR; 560 } 561 562 563 status_t 564 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...) 565 { 566 return B_ERROR; 567 } 568 569 570 status_t 571 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...) 572 { 573 return B_ERROR; 574 } 575 576 577 status_t 578 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...) 579 { 580 return B_ERROR; 581 } 582 583 584 status_t 585 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...) 586 { 587 return B_ERROR; 588 } 589 590 591 status_t 592 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...) 593 { 594 return B_ERROR; 595 } 596 597 598 status_t 599 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...) 600 { 601 return B_ERROR; 602 } 603 604 605 status_t 606 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...) 607 { 608 return B_ERROR; 609 } 610 611 612