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 26struct _gs_media_tracker { 27 BMediaFile* file; 28 BMediaTrack* stream; 29 int64 frames; 30 size_t position; 31}; 32 33 34// Local utility functions ----------------------------------------------- 35template<typename T, int32 min, int32 middle, int32 max> 36bool 37FillBuffer(_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 ------------------------------------------------------- 57BFileGameSound::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 75BFileGameSound::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 101BFileGameSound::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 119BFileGameSound::~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 134BGameSound* 135BFileGameSound::Clone() const 136{ 137 return NULL; 138} 139 140 141status_t 142BFileGameSound::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 153status_t 154BFileGameSound::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 173status_t 174BFileGameSound::Preload() 175{ 176 if (!IsPlaying()) 177 Load(); 178 179 return B_OK; 180} 181 182 183void 184BFileGameSound::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 258status_t 259BFileGameSound::Perform(int32 selector, void* data) 260{ 261 return B_ERROR; 262} 263 264 265status_t 266BFileGameSound::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 295int32 296BFileGameSound::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 308status_t 309BFileGameSound::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 389bool 390BFileGameSound::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 417bool 418BFileGameSound::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 432status_t 433BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...) 434{ 435 return B_ERROR; 436} 437 438 439status_t 440BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...) 441{ 442 return B_ERROR; 443} 444 445 446status_t 447BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...) 448{ 449 return B_ERROR; 450} 451 452 453status_t 454BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...) 455{ 456 return B_ERROR; 457} 458 459 460status_t 461BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...) 462{ 463 return B_ERROR; 464} 465 466 467status_t 468BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...) 469{ 470 return B_ERROR; 471} 472 473 474status_t 475BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...) 476{ 477 return B_ERROR; 478} 479 480 481status_t 482BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...) 483{ 484 return B_ERROR; 485} 486 487 488status_t 489BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...) 490{ 491 return B_ERROR; 492} 493 494 495status_t 496BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...) 497{ 498 return B_ERROR; 499} 500 501 502status_t 503BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...) 504{ 505 return B_ERROR; 506} 507 508 509status_t 510BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...) 511{ 512 return B_ERROR; 513} 514 515 516status_t 517BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...) 518{ 519 return B_ERROR; 520} 521 522 523status_t 524BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...) 525{ 526 return B_ERROR; 527} 528 529 530status_t 531BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...) 532{ 533 return B_ERROR; 534} 535 536 537status_t 538BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...) 539{ 540 return B_ERROR; 541} 542 543 544status_t 545BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...) 546{ 547 return B_ERROR; 548} 549 550 551status_t 552BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...) 553{ 554 return B_ERROR; 555} 556 557 558status_t 559BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...) 560{ 561 return B_ERROR; 562} 563 564 565status_t 566BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...) 567{ 568 return B_ERROR; 569} 570 571 572status_t 573BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...) 574{ 575 return B_ERROR; 576} 577 578 579status_t 580BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...) 581{ 582 return B_ERROR; 583} 584 585 586status_t 587BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...) 588{ 589 return B_ERROR; 590} 591 592 593status_t 594BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...) 595{ 596 return B_ERROR; 597} 598