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