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