xref: /haiku/src/kits/game/FileGameSound.cpp (revision 2e68fbd20741c99d199045a0599e614ca9a2e07e)
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
FillBuffer(_gs_ramp * ramp,T * dest,const T * src,size_t * bytes)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 -------------------------------------------------------
BFileGameSound(const entry_ref * file,bool looping,BGameSoundDevice * device)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 
BFileGameSound(const char * file,bool looping,BGameSoundDevice * device)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 
BFileGameSound(BDataIO * data,bool looping,BGameSoundDevice * device)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 
~BFileGameSound()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*
Clone() const135 BFileGameSound::Clone() const
136 {
137 	return NULL;
138 }
139 
140 
141 status_t
StartPlaying()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
StopPlaying()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
Preload()174 BFileGameSound::Preload()
175 {
176 	if (!IsPlaying())
177 		Load();
178 
179 	return B_OK;
180 }
181 
182 
183 void
FillBuffer(void * inBuffer,size_t inByteCount)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
Perform(int32 selector,void * data)259 BFileGameSound::Perform(int32 selector, void* data)
260 {
261 	return B_ERROR;
262 }
263 
264 
265 status_t
SetPaused(bool isPaused,bigtime_t rampTime)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
IsPaused()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
Init(BDataIO * data)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
Load()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
Read(void * buffer,size_t bytes)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
_Reserved_BFileGameSound_0(int32 arg,...)433 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...)
434 {
435 	return B_ERROR;
436 }
437 
438 
439 status_t
_Reserved_BFileGameSound_1(int32 arg,...)440 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...)
441 {
442 	return B_ERROR;
443 }
444 
445 
446 status_t
_Reserved_BFileGameSound_2(int32 arg,...)447 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...)
448 {
449 	return B_ERROR;
450 }
451 
452 
453 status_t
_Reserved_BFileGameSound_3(int32 arg,...)454 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...)
455 {
456 	return B_ERROR;
457 }
458 
459 
460 status_t
_Reserved_BFileGameSound_4(int32 arg,...)461 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...)
462 {
463 	return B_ERROR;
464 }
465 
466 
467 status_t
_Reserved_BFileGameSound_5(int32 arg,...)468 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...)
469 {
470 	return B_ERROR;
471 }
472 
473 
474 status_t
_Reserved_BFileGameSound_6(int32 arg,...)475 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...)
476 {
477 	return B_ERROR;
478 }
479 
480 
481 status_t
_Reserved_BFileGameSound_7(int32 arg,...)482 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...)
483 {
484 	return B_ERROR;
485 }
486 
487 
488 status_t
_Reserved_BFileGameSound_8(int32 arg,...)489 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...)
490 {
491 	return B_ERROR;
492 }
493 
494 
495 status_t
_Reserved_BFileGameSound_9(int32 arg,...)496 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...)
497 {
498 	return B_ERROR;
499 }
500 
501 
502 status_t
_Reserved_BFileGameSound_10(int32 arg,...)503 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...)
504 {
505 	return B_ERROR;
506 }
507 
508 
509 status_t
_Reserved_BFileGameSound_11(int32 arg,...)510 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...)
511 {
512 	return B_ERROR;
513 }
514 
515 
516 status_t
_Reserved_BFileGameSound_12(int32 arg,...)517 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...)
518 {
519 	return B_ERROR;
520 }
521 
522 
523 status_t
_Reserved_BFileGameSound_13(int32 arg,...)524 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...)
525 {
526 	return B_ERROR;
527 }
528 
529 
530 status_t
_Reserved_BFileGameSound_14(int32 arg,...)531 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...)
532 {
533 	return B_ERROR;
534 }
535 
536 
537 status_t
_Reserved_BFileGameSound_15(int32 arg,...)538 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...)
539 {
540 	return B_ERROR;
541 }
542 
543 
544 status_t
_Reserved_BFileGameSound_16(int32 arg,...)545 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...)
546 {
547 	return B_ERROR;
548 }
549 
550 
551 status_t
_Reserved_BFileGameSound_17(int32 arg,...)552 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...)
553 {
554 	return B_ERROR;
555 }
556 
557 
558 status_t
_Reserved_BFileGameSound_18(int32 arg,...)559 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...)
560 {
561 	return B_ERROR;
562 }
563 
564 
565 status_t
_Reserved_BFileGameSound_19(int32 arg,...)566 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...)
567 {
568 	return B_ERROR;
569 }
570 
571 
572 status_t
_Reserved_BFileGameSound_20(int32 arg,...)573 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...)
574 {
575 	return B_ERROR;
576 }
577 
578 
579 status_t
_Reserved_BFileGameSound_21(int32 arg,...)580 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...)
581 {
582 	return B_ERROR;
583 }
584 
585 
586 status_t
_Reserved_BFileGameSound_22(int32 arg,...)587 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...)
588 {
589 	return B_ERROR;
590 }
591 
592 
593 status_t
_Reserved_BFileGameSound_23(int32 arg,...)594 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...)
595 {
596 	return B_ERROR;
597 }
598