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