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