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