2 * Copyright 2001-2012 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Christopher ML Zumwalt May (zummy@users.sf.net)
16 #include <FileGameSound.h>
17 #include <MediaFile.h>
18 #include <MediaTrack.h>
19 #include <scheduler.h>
21 #include "GameSoundDevice.h"
22 #include "GSUtility.h"
25 const int32 kPages
= 20;
26 struct _gs_media_tracker
35 // Local utility functions -----------------------------------------------
37 FillBuffer(_gs_ramp
* ramp
, uint8
* data
, uint8
* buffer
, size_t* bytes
)
39 int32 samples
= *bytes
/ sizeof(uint8
);
41 for (int32 byte
= 0; byte
< samples
; byte
++) {
42 float gain
= *ramp
->value
;
43 data
[byte
] = uint8(float(buffer
[byte
]) * gain
);
45 if (ChangeRamp(ramp
)) {
46 *bytes
= byte
* sizeof(uint8
);
56 FillBuffer(_gs_ramp
* ramp
, int16
* data
, int16
* buffer
, size_t* bytes
)
58 int32 samples
= *bytes
/ sizeof(int16
);
60 for (int32 byte
= 0; byte
< samples
; byte
++) {
61 float gain
= *ramp
->value
;
62 data
[byte
] = int16(float(buffer
[byte
]) * gain
);
64 if (ChangeRamp(ramp
)) {
65 *bytes
= byte
* sizeof(int16
);
75 FillBuffer(_gs_ramp
* ramp
, int32
* data
, int32
* buffer
, size_t* bytes
)
78 bool bytesAreReady
= (*bytes
> 0);
80 while (bytesAreReady
) {
81 float gain
= *ramp
->value
;
82 data
[byte
] = int32(float(buffer
[byte
]) * gain
);
84 if (ChangeRamp(ramp
)) {
90 bytesAreReady
= (byte
>= *bytes
);
98 FillBuffer(_gs_ramp
* ramp
, float* data
, float* buffer
, size_t* bytes
)
101 bool bytesAreReady
= (*bytes
> 0);
103 while (bytesAreReady
) {
104 float gain
= *ramp
->value
;
105 data
[byte
] = buffer
[byte
] * gain
;
107 if (ChangeRamp(ramp
)) {
113 bytesAreReady
= (byte
>= *bytes
);
120 // BFileGameSound -------------------------------------------------------
121 BFileGameSound::BFileGameSound(const entry_ref
* file
, bool looping
,
122 BGameSoundDevice
* device
)
124 BStreamingGameSound(device
),
134 if (InitCheck() == B_OK
)
135 SetInitError(Init(file
));
139 BFileGameSound::BFileGameSound(const char* file
, bool looping
,
140 BGameSoundDevice
* device
)
142 BStreamingGameSound(device
),
152 if (InitCheck() == B_OK
) {
155 if (get_ref_for_path(file
, &node
) != B_OK
)
156 SetInitError(B_ENTRY_NOT_FOUND
);
158 SetInitError(Init(&node
));
163 BFileGameSound::~BFileGameSound()
165 if (fReadThread
>= 0) {
166 // TODO: kill_thread() is very bad, since it will leak any resources
167 // that the thread had allocated. It will also keep locks locked that
168 // the thread holds! Set a flag to make the thread quit and use
169 // wait_for_thread() here!
170 kill_thread(fReadThread
);
174 if (fAudioStream
->stream
)
175 fAudioStream
->file
->ReleaseTrack(fAudioStream
->stream
);
177 delete fAudioStream
->file
;
186 BFileGameSound::Clone() const
193 BFileGameSound::StartPlaying()
195 // restart playback if needed
199 // start playing the file
200 return BStreamingGameSound::StartPlaying();
205 BFileGameSound::StopPlaying()
207 status_t error
= BStreamingGameSound::StopPlaying();
209 if (!fAudioStream
|| !fAudioStream
->stream
)
212 // start reading next time from the start of the file
214 fAudioStream
->stream
->SeekToFrame(&frame
);
217 fAudioStream
->position
= 0;
225 BFileGameSound::Preload()
235 BFileGameSound::FillBuffer(void* inBuffer
, size_t inByteCount
)
237 // Split or combine decoder buffers into mixer buffers
238 // fPlayPosition is where we got up to in the input buffer after last call
240 size_t out_offset
= 0;
242 while (inByteCount
> 0 && !fPaused
) {
243 if (!fPaused
|| fPausing
) {
244 printf("mixout %ld, inByteCount %ld, decin %ld, BufferSize %ld\n",
245 out_offset
, inByteCount
, fPlayPosition
, fBufferSize
);
246 if (fPlayPosition
== 0 || fPlayPosition
>= fBufferSize
) {
253 bool rampDone
= false;
254 size_t bytes
= fBufferSize
- fPlayPosition
;
256 if (bytes
> inByteCount
) {
260 // Fill the requested buffer, stopping if the paused flag is set
261 char* buffer
= (char*)inBuffer
;
263 switch(Format().format
) {
264 case gs_audio_format::B_GS_U8
:
265 rampDone
= ::FillBuffer(fPausing
,
266 (uint8
*)&buffer
[out_offset
],
267 (uint8
*)&fBuffer
[fPlayPosition
], &bytes
);
270 case gs_audio_format::B_GS_S16
:
271 rampDone
= ::FillBuffer(fPausing
,
272 (int16
*)&buffer
[out_offset
],
273 (int16
*)&fBuffer
[fPlayPosition
], &bytes
);
276 case gs_audio_format::B_GS_S32
:
277 rampDone
= ::FillBuffer(fPausing
,
278 (int32
*)&buffer
[out_offset
],
279 (int32
*)&fBuffer
[fPlayPosition
], &bytes
);
282 case gs_audio_format::B_GS_F
:
283 rampDone
= ::FillBuffer(fPausing
,
284 (float*)&buffer
[out_offset
],
285 (float*)&fBuffer
[fPlayPosition
], &bytes
);
289 inByteCount
-= bytes
;
291 fPlayPosition
+= bytes
;
293 // We finished ramping
296 // Need to be able to stop asap when pause flag is flipped.
297 while (fPlayPosition
< fBufferSize
&& (inByteCount
> 0)) {
298 buffer
[out_offset
++] = fBuffer
[fPlayPosition
++];
309 char* buffer
= (char*)inBuffer
;
311 // Need to be able to stop asap when the pause flag is flipped.
312 while (fPlayPosition
< fBufferSize
&& (!fPaused
|| fPausing
)
313 && (inByteCount
> 0)) {
314 buffer
[out_offset
++] = fBuffer
[fPlayPosition
++];
324 BFileGameSound::Perform(int32 selector
, void* data
)
331 BFileGameSound::SetPaused(bool isPaused
, bigtime_t rampTime
)
333 if (fPaused
== isPaused
)
338 // Clear any old ramping
342 if (rampTime
> 100000) {
345 fPausing
= InitRamp(&fPauseGain
, 0.0,
346 Format().frame_rate
, rampTime
);
348 fPausing
= InitRamp(&fPauseGain
, 1.0,
349 Format().frame_rate
, rampTime
);
360 BFileGameSound::IsPaused()
363 return B_PAUSE_IN_PROGRESS
;
373 BFileGameSound::Init(const entry_ref
* file
)
375 fAudioStream
= new(std::nothrow
) _gs_media_tracker
;
379 memset(fAudioStream
, 0, sizeof(_gs_media_tracker
));
380 fAudioStream
->file
= new(std::nothrow
) BMediaFile(file
);
381 if (!fAudioStream
->file
) {
387 status_t error
= fAudioStream
->file
->InitCheck();
391 fAudioStream
->stream
= fAudioStream
->file
->TrackAt(0);
393 // is this is an audio file?
394 media_format playFormat
;
395 if ((error
= fAudioStream
->stream
->EncodedFormat(&playFormat
)) != B_OK
) {
396 fAudioStream
->file
->ReleaseTrack(fAudioStream
->stream
);
397 fAudioStream
->stream
= NULL
;
401 if (!playFormat
.IsAudio()) {
402 fAudioStream
->file
->ReleaseTrack(fAudioStream
->stream
);
403 fAudioStream
->stream
= NULL
;
404 return B_MEDIA_BAD_FORMAT
;
407 gs_audio_format dformat
= Device()->Format();
409 // request the format we want the sound
410 memset(&playFormat
, 0, sizeof(media_format
));
411 playFormat
.type
= B_MEDIA_RAW_AUDIO
;
412 if (fAudioStream
->stream
->DecodedFormat(&playFormat
) != B_OK
) {
413 fAudioStream
->file
->ReleaseTrack(fAudioStream
->stream
);
414 fAudioStream
->stream
= NULL
;
415 return B_MEDIA_BAD_FORMAT
;
418 // translate the format into a "GameKit" friendly one
419 gs_audio_format gsformat
;
420 media_to_gs_format(&gsformat
, &playFormat
.u
.raw_audio
);
422 // Since the buffer sized read from the file is most likely differnt
423 // then the buffer used by the audio mixer, we must allocate a buffer
424 // large enough to hold the largest request.
425 fBufferSize
= gsformat
.buffer_size
;
426 if (fBufferSize
< dformat
.buffer_size
)
427 fBufferSize
= dformat
.buffer_size
;
430 fBuffer
= new char[fBufferSize
* 2];
431 memset(fBuffer
, 0, fBufferSize
* 2);
433 fFrameSize
= gsformat
.channel_count
* get_sample_size(gsformat
.format
);
434 fAudioStream
->frames
= fAudioStream
->stream
->CountFrames();
436 // Ask the device to attach our sound to it
438 error
= Device()->CreateBuffer(&sound
, this, &gsformat
);
442 return BGameSound::Init(sound
);
447 BFileGameSound::Load()
449 if (!fAudioStream
|| !fAudioStream
->stream
)
454 fAudioStream
->stream
->ReadFrames(fBuffer
, &frames
);
455 fBufferSize
= frames
* fFrameSize
;
458 if (fBufferSize
<= 0) {
461 // start reading next time from the start of the file
463 fAudioStream
->stream
->SeekToFrame(&frame
);
474 BFileGameSound::Read(void* buffer
, size_t bytes
)
480 /* unimplemented for protection of the user:
482 * BFileGameSound::BFileGameSound()
483 * BFileGameSound::BFileGameSound(const BFileGameSound &)
484 * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &)
489 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg
, ...)
496 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg
, ...)
503 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg
, ...)
510 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg
, ...)
517 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg
, ...)
524 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg
, ...)
531 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg
, ...)
538 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg
, ...)
545 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg
, ...)
552 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg
, ...)
559 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg
, ...)
566 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg
, ...)
573 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg
, ...)
580 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg
, ...)
587 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg
, ...)
594 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg
, ...)
601 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg
, ...)
608 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg
, ...)
615 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg
, ...)
622 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg
, ...)
629 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg
, ...)
636 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg
, ...)
643 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg
, ...)
650 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg
, ...)