xref: /haiku/src/kits/game/FileGameSound.cpp (revision ea4f253fd6982ab9fe71251121e8095b784df7fe)
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 	// start reading next time from the start of the file
202 	int64 frame = 0;
203 	fAudioStream->stream->SeekToFrame(&frame);
204 
205 	fStopping = false;
206 	fAudioStream->position = 0;
207 	fPlayPosition = 0;
208 
209 	return error;
210 }
211 
212 
213 status_t
214 BFileGameSound::Preload()
215 {
216 	if (!IsPlaying())
217 		Load();
218 
219 	return B_OK;
220 }
221 
222 
223 void
224 BFileGameSound::FillBuffer(void *inBuffer,
225 						   size_t inByteCount)
226 {
227 	size_t bytes = inByteCount;
228 
229 	if (!fPaused || fPausing) {
230 		if (fPlayPosition == 0 || fPlayPosition + inByteCount >= fBufferSize) {
231 			Load();
232 		}
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 	{
303 		Lock();
304 
305 		// Clear any old ramping
306 		delete fPausing;
307 		fPausing = NULL;
308 
309 		if (rampTime > 100000)
310 		{
311 			// Setup for ramping
312 			if (isPaused)
313 				fPausing = InitRamp(&fPauseGain, 0.0, Format().frame_rate, rampTime);
314 			else
315 				fPausing = InitRamp(&fPauseGain, 1.0, Format().frame_rate, rampTime);
316 		}
317 
318 		fPaused = isPaused;
319 		Unlock();
320 	}
321 
322 	return B_OK;
323 }
324 
325 
326 int32
327 BFileGameSound::IsPaused()
328 {
329 	if (fPausing) return B_PAUSE_IN_PROGRESS;
330 
331 	if (fPaused) 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 	fAudioStream->stream->EncodedFormat(&playFormat);
353 	if (!playFormat.IsAudio())
354 		return B_MEDIA_BAD_FORMAT;
355 
356 	gs_audio_format dformat = Device()->Format();
357 
358 	// request the format we want the sound
359 	memset(&playFormat, 0, sizeof(media_format));
360 	playFormat.type = B_MEDIA_RAW_AUDIO;
361 	if (fAudioStream->stream->DecodedFormat(&playFormat) != B_OK)
362 		return B_MEDIA_BAD_FORMAT;
363 
364 	// translate the format into a "GameKit" friendly one
365 	gs_audio_format gsformat;
366 	media_to_gs_format(&gsformat, &playFormat.u.raw_audio);
367 
368 	// Since the buffer sized read from the file is most likely differnt
369 	// then the buffer used by the audio mixer, we must allocate a buffer
370 	// large enough to hold the largest request.
371 	fBufferSize = gsformat.buffer_size;
372 	if (fBufferSize < dformat.buffer_size)
373 		fBufferSize = dformat.buffer_size;
374 
375 	// create the buffer
376 	fBuffer = new char[fBufferSize * 2];
377 	memset(fBuffer, 0, fBufferSize * 2);
378 
379 	fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format);
380 	fAudioStream->frames = fAudioStream->stream->CountFrames();
381 
382 	// Ask the device to attach our sound to it
383 	gs_id sound;
384 	error = Device()->CreateBuffer(&sound, this, &gsformat);
385 	if (error != B_OK)
386 		return error;
387 
388 	return BGameSound::Init(sound);
389 }
390 
391 
392 bool
393 BFileGameSound::Load()
394 {
395 
396 	if (fPlayPosition != 0) {
397 		memcpy(fBuffer, fBuffer + fPlayPosition, fBufferSize - fPlayPosition);
398 		fPlayPosition = fBufferSize - fPlayPosition;
399 	}
400 
401 	// time to read a new buffer
402 	int64 frames = 0;
403 	fAudioStream->stream->ReadFrames(fBuffer + fPlayPosition, &frames);
404 	fBufferSize = fPlayPosition + frames * fFrameSize;
405 	fPlayPosition = 0;
406 
407 	return true;
408 }
409 
410 
411 bool
412 BFileGameSound::Read(void * buffer, size_t bytes)
413 {
414 	return false;
415 }
416 
417 
418 /* unimplemented for protection of the user:
419  *
420  * BFileGameSound::BFileGameSound()
421  * BFileGameSound::BFileGameSound(const BFileGameSound &)
422  * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &)
423  */
424 
425 
426 status_t
427 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...)
428 {
429 	return B_ERROR;
430 }
431 
432 
433 status_t
434 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...)
435 {
436 	return B_ERROR;
437 }
438 
439 
440 status_t
441 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...)
442 {
443 	return B_ERROR;
444 }
445 
446 
447 status_t
448 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...)
449 {
450 	return B_ERROR;
451 }
452 
453 
454 status_t
455 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...)
456 {
457 	return B_ERROR;
458 }
459 
460 
461 status_t
462 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...)
463 {
464 	return B_ERROR;
465 }
466 
467 
468 status_t
469 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...)
470 {
471 	return B_ERROR;
472 }
473 
474 
475 status_t
476 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...)
477 {
478 	return B_ERROR;
479 }
480 
481 
482 status_t
483 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...)
484 {
485 	return B_ERROR;
486 }
487 
488 
489 status_t
490 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...)
491 {
492 	return B_ERROR;
493 }
494 
495 
496 status_t
497 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...)
498 {
499 	return B_ERROR;
500 }
501 
502 
503 status_t
504 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...)
505 {
506 	return B_ERROR;
507 }
508 
509 
510 status_t
511 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...)
512 {
513 	return B_ERROR;
514 }
515 
516 
517 status_t
518 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...)
519 {
520 	return B_ERROR;
521 }
522 
523 
524 status_t
525 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...)
526 {
527 	return B_ERROR;
528 }
529 
530 
531 status_t
532 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...)
533 {
534 	return B_ERROR;
535 }
536 
537 
538 status_t
539 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...)
540 {
541 	return B_ERROR;
542 }
543 
544 
545 status_t
546 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...)
547 {
548 	return B_ERROR;
549 }
550 
551 
552 status_t
553 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...)
554 {
555 	return B_ERROR;
556 }
557 
558 
559 status_t
560 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...)
561 {
562 	return B_ERROR;
563 }
564 
565 
566 status_t
567 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...)
568 {
569 	return B_ERROR;
570 }
571 
572 
573 status_t
574 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...)
575 {
576 	return B_ERROR;
577 }
578 
579 
580 status_t
581 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...)
582 {
583 	return B_ERROR;
584 }
585 
586 
587 status_t
588 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...)
589 {
590 	return B_ERROR;
591 }
592 
593 
594