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