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