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