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