xref: /haiku/src/kits/game/FileGameSound.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		FileGameSound.cpp
23 //	Author:			Christopher ML Zumwalt May (zummy@users.sf.net)
24 //	Description:	BFileGameSound is a class that streams data out of a file.
25 //------------------------------------------------------------------------------
26 
27 // Standard Includes -----------------------------------------------------------
28 #include <memory.h>
29 
30 // System Includes -------------------------------------------------------------
31 #include <Entry.h>
32 #include <MediaFile.h>
33 #include <MediaTrack.h>
34 #include <scheduler.h>
35 
36 // Project Includes ------------------------------------------------------------
37 #include <GameSoundDevice.h>
38 #include <GSUtility.h>
39 
40 // Local Includes --------------------------------------------------------------
41 #include <FileGameSound.h>
42 
43 // Local Defines ---------------------------------------------------------------
44 const int32 kPages = 20;
45 struct _gs_media_tracker
46 {
47 	BMediaFile *	file;
48 	BMediaTrack * 	stream;
49 	int64			frames;
50 	size_t			position;
51 };
52 
53 // Locale utility functions -----------------------------------------------
54 bool FillBuffer(_gs_ramp * ramp, uint8 * data, uint8 * buffer, size_t * bytes)
55 {
56 	int32 samples = *bytes / sizeof(int16);
57 
58 	for(int32 byte = 0; byte < samples; byte++)
59 	{
60 		float gain = *ramp->value;
61 		data[byte] = uint8(float(buffer[byte]) * gain);
62 
63 		if (ChangeRamp(ramp))
64 		{
65 			*bytes = byte * sizeof(int16);
66 			return true;
67 		}
68 	}
69 
70 	return false;
71 }
72 
73 bool FillBuffer(_gs_ramp * ramp, int16 * data, int16 * buffer, size_t * bytes)
74 {
75 	int32 samples = *bytes / sizeof(int16);
76 
77 	for(int32 byte = 0; byte < samples; byte++)
78 	{
79 		float gain = *ramp->value;
80 		data[byte] = int16(float(buffer[byte]) * gain);
81 
82 		if (ChangeRamp(ramp))
83 		{
84 			*bytes = byte * sizeof(int16);
85 			return true;
86 		}
87 	}
88 
89 	return false;
90 }
91 
92 bool FillBuffer(_gs_ramp * ramp, int32 * data, int32 * buffer, size_t * bytes)
93 {
94 	size_t byte = 0;
95 	bool bytesAreReady = (*bytes > 0);
96 
97 	while(bytesAreReady)
98 	{
99 		float gain = *ramp->value;
100 		data[byte] = int32(float(buffer[byte]) * gain);
101 
102 		if (ChangeRamp(ramp))
103 		{
104 			*bytes = byte;
105 			return true;
106 		}
107 
108 		byte++;
109 		bytesAreReady = (byte >= *bytes);
110 	}
111 
112 	return false;
113 }
114 
115 bool FillBuffer(_gs_ramp * ramp, float * data, float * buffer, size_t * bytes)
116 {
117 	size_t byte = 0;
118 	bool bytesAreReady = (*bytes > 0);
119 
120 	while(bytesAreReady)
121 	{
122 		float gain = *ramp->value;
123 		data[byte] = buffer[byte] * gain;
124 
125 		if (ChangeRamp(ramp))
126 		{
127 			*bytes = byte;
128 			return true;
129 		}
130 
131 		byte++;
132 		bytesAreReady = (byte >= *bytes);
133 	}
134 
135 	return false;
136 }
137 
138 // BFileGameSound -------------------------------------------------------
139 BFileGameSound::BFileGameSound(const entry_ref *file,
140 							   bool looping,
141 							   BGameSoundDevice *device)
142  	:	BStreamingGameSound(device),
143 		fAudioStream(NULL),
144  		fStopping(false),
145  		fLooping(looping),
146  		fBuffer(NULL),
147  		fPlayPosition(0),
148  		fPausing(NULL),
149  		fPaused(false),
150  		fPauseGain(1.0)
151 {
152 	if (InitCheck() == B_OK) SetInitError(Init(file));
153 }
154 
155 
156 BFileGameSound::BFileGameSound(const char *file,
157 							   bool looping,
158 							   BGameSoundDevice *device)
159  	:	BStreamingGameSound(device),
160 		fAudioStream(NULL),
161  		fStopping(false),
162  		fLooping(looping),
163 		fBuffer(NULL),
164  		fPlayPosition(0),
165  		fPausing(NULL),
166  		fPaused(false),
167  		fPauseGain(1.0)
168 {
169 	if (InitCheck() == B_OK)
170 	{
171 		entry_ref node;
172 
173 		if (get_ref_for_path(file, &node) != B_OK)
174 			SetInitError(B_ENTRY_NOT_FOUND);
175 		else
176 			SetInitError(Init(&node));
177 	}
178 }
179 
180 
181 BFileGameSound::~BFileGameSound()
182 {
183 	if (fReadThread >= 0) kill_thread(fReadThread);
184 
185 	if (fAudioStream)
186 	{
187 		if (fAudioStream->stream) fAudioStream->file->ReleaseTrack(fAudioStream->stream);
188 
189 		fAudioStream->file->CloseFile();
190 
191 		//delete fAudioStream->stream;
192 		delete fAudioStream->file;
193 	}
194 
195 	delete [] fBuffer;
196 	delete fAudioStream;
197 }
198 
199 
200 BGameSound *
201 BFileGameSound::Clone() const
202 {
203 	return NULL;
204 }
205 
206 
207 status_t
208 BFileGameSound::StartPlaying()
209 {
210 	// restart playback if needed
211 	if (IsPlaying()) StopPlaying();
212 
213 	fPort = create_port(kPages, "audioque");
214 
215 	// create the thread that will read the file
216 	fReadThread = spawn_thread(ReadThread, "audiostream", B_NORMAL_PRIORITY, this);
217 	if (fReadThread < B_OK) return B_NO_MORE_THREADS;
218 
219 	status_t error = resume_thread(fReadThread);
220 	if (error != B_OK) return error;
221 
222 	// start playing the file
223 	return BStreamingGameSound::StartPlaying();
224 }
225 
226 
227 status_t
228 BFileGameSound::StopPlaying()
229 {
230 	status_t error = BStreamingGameSound::StopPlaying();
231 
232 	// start reading next time from the start of the file
233 	int64 frame = 0;
234 	fAudioStream->stream->SeekToFrame(&frame);
235 
236 	fStopping = false;
237 	fAudioStream->position = 0;
238 	fPlayPosition = 0;
239 
240 	// we don't need to read any more
241 	kill_thread(fReadThread);
242 	delete_port(fPort);
243 
244 	return error;
245 }
246 
247 
248 status_t
249 BFileGameSound::Preload()
250 {
251 	if (!IsPlaying())
252 	{
253 		for(int32 i = 0; i < kPages / 2; i++) Load();
254 	}
255 
256 	return B_OK;
257 }
258 
259 
260 void
261 BFileGameSound::FillBuffer(void *inBuffer,
262 						   size_t inByteCount)
263 {
264 	int32 cookie;
265 	size_t bytes = inByteCount;
266 
267 	if (!fPaused || fPausing)
268 	{
269 		// time to read a new buffer
270 		if (fPlayPosition == 0) read_port_etc(fPort, &cookie, fBuffer, fBufferSize, B_TIMEOUT, 0);
271 
272 		if (fPausing)
273 		{
274 			Lock();
275 
276 			bool rampDone = false;
277 
278 			// Fill the requsted buffer, stopping if the paused flag is set
279 			switch(Format().format)
280 			{
281 				case gs_audio_format::B_GS_U8:
282 					rampDone = ::FillBuffer(fPausing, (uint8*)inBuffer, (uint8*)&fBuffer[fPlayPosition], &bytes);
283 					break;
284 
285 				case gs_audio_format::B_GS_S16:
286 					rampDone = ::FillBuffer(fPausing, (int16*)inBuffer, (int16*)&fBuffer[fPlayPosition], &bytes);
287 					break;
288 
289 				case gs_audio_format::B_GS_S32:
290 					rampDone = ::FillBuffer(fPausing, (int32*)inBuffer, (int32*)&fBuffer[fPlayPosition], &bytes);
291 					break;
292 
293 				case gs_audio_format::B_GS_F:
294 					rampDone = ::FillBuffer(fPausing, (float*)inBuffer, (float*)&fBuffer[fPlayPosition], &bytes);
295 					break;
296 			}
297 
298 			// We finished ramping
299 			if (rampDone)
300 			{
301 				if (bytes < inByteCount && !fPausing)
302 				{
303 					// Since are resumming play back, we need to copy any remaining samples
304 					char * buffer = (char*)inBuffer;
305 					memcpy(&buffer[bytes], &fBuffer[fPlayPosition + bytes], inByteCount - bytes);
306 				}
307 
308 				delete fPausing;
309 				fPausing = NULL;
310 			}
311 
312 			Unlock();
313 		}
314 		else
315 		{
316 			size_t byte = 0;
317 			char * buffer = (char*)inBuffer;
318 
319 			// We need to be able to stop asap when the pause flag is flipped.
320 			while(byte < bytes && (!fPaused || fPausing))
321 			{
322 				buffer[byte] = fBuffer[fPlayPosition + byte];
323 				byte++;
324 			}
325 
326 			bytes = byte;
327 		}
328 	}
329 
330 	fPlayPosition += bytes;
331 	if (fPlayPosition >= fBufferSize)
332 	{
333 		// We have finished reading the buffer. Setup for the next buffer.
334 		fPlayPosition = 0;
335 		memset(fBuffer, 0, fBufferSize);
336 	}
337 }
338 
339 
340 status_t
341 BFileGameSound::Perform(int32 selector,
342 						void *data)
343 {
344 	return B_ERROR;
345 }
346 
347 
348 status_t
349 BFileGameSound::SetPaused(bool isPaused,
350 						  bigtime_t rampTime)
351 {
352 	if (fPaused != isPaused)
353 	{
354 		Lock();
355 
356 		// Clear any old ramping
357 		delete fPausing;
358 		fPausing = NULL;
359 
360 		if (rampTime > 100000)
361 		{
362 			// Setup for ramping
363 			if (isPaused)
364 				fPausing = InitRamp(&fPauseGain, 0.0, Format().frame_rate, rampTime);
365 			else
366 				fPausing = InitRamp(&fPauseGain, 1.0, Format().frame_rate, rampTime);
367 		}
368 
369 		fPaused = isPaused;
370 		Unlock();
371 	}
372 
373 	return B_OK;
374 }
375 
376 
377 int32
378 BFileGameSound::IsPaused()
379 {
380 	if (fPausing) return B_PAUSE_IN_PROGRESS;
381 
382 	if (fPaused) return B_PAUSED;
383 
384 	return B_NOT_PAUSED;
385 }
386 
387 
388 status_t
389 BFileGameSound::Init(const entry_ref* file)
390 {
391 	fAudioStream = new _gs_media_tracker;
392 	memset(fAudioStream, 0, sizeof(_gs_media_tracker));
393 
394 	fAudioStream->file = new BMediaFile(file);
395 	status_t error = fAudioStream->file->InitCheck();
396 	if (error != B_OK) return error;
397 
398 	fAudioStream->stream = fAudioStream->file->TrackAt(0);
399 
400 	// is this is an audio file?
401 	media_format mformat;
402 	fAudioStream->stream->EncodedFormat(&mformat);
403 	if (!mformat.IsAudio()) return B_MEDIA_BAD_FORMAT;
404 
405 	gs_audio_format dformat = Device()->Format();
406 
407 	// request the format we want the sound
408 	memset(&mformat, 0, sizeof(media_format));
409 	mformat.type = B_MEDIA_RAW_AUDIO;
410 	fAudioStream->stream->DecodedFormat(&mformat);
411 
412 	// translate the format into a "GameKit" friendly one
413 	gs_audio_format gsformat;
414 	media_to_gs_format(&gsformat, &mformat.u.raw_audio);
415 
416 	// Since the buffer sized read from the file is most likely differnt
417 	// then the buffer used by the audio mixer, we must allocate a buffer
418 	// large enough to hold the largest request.
419 	fBufferSize = gsformat.buffer_size;
420 	if (fBufferSize < dformat.buffer_size) fBufferSize = dformat.buffer_size;
421 
422 	// create the buffer
423 	fBuffer = new char[fBufferSize * 2];
424 	memset(fBuffer, 0, fBufferSize * 2);
425 
426 	fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format);
427 	fAudioStream->frames = fAudioStream->stream->CountFrames();
428 
429 	// Ask the device to attach our sound to it
430 	gs_id sound;
431 	error = Device()->CreateBuffer(&sound, this, &gsformat);
432 	if (error != B_OK) return error;
433 
434 	return BGameSound::Init(sound);
435 }
436 
437 
438 int32
439 BFileGameSound::ReadThread(void* arg)
440 {
441 	BFileGameSound* obj = (BFileGameSound*)arg;
442 
443 	while(true) obj->Load();
444 
445 	return 0;
446 }
447 
448 
449 bool
450 BFileGameSound::Load()
451 {
452 	int64 frames;
453 	char * buffer = &fBuffer[fBufferSize + fAudioStream->position];
454 	fAudioStream->stream->ReadFrames(buffer, &frames);
455 
456 	int32 frame = fAudioStream->stream->CurrentFrame();
457 
458 	fAudioStream->position += fFrameSize * frames;
459 	if (fAudioStream->position >= fBufferSize)
460 	{
461 		// we have filled the enter buffer, time to send
462 		write_port(fPort, fBufferSize, &fBuffer[fBufferSize], fBufferSize);
463 		fAudioStream->position = 0;
464 	}
465 
466 	if (frame >= fAudioStream->frames)
467 	{
468 		if (fLooping)
469 		{
470 			// since we are looping, we need to start reading from
471 			// the begining of the file again.
472 			int64 firstFrame = 0;
473 			fAudioStream->stream->SeekToFrame(&firstFrame);
474 
475 			fStopping = true;
476 		}
477 		else fStopping = true;
478 	}
479 
480 	return true;
481 }
482 
483 
484 bool
485 BFileGameSound::Read(void * buffer, size_t bytes)
486 {
487 	return false;
488 }
489 
490 
491 /* unimplemented for protection of the user:
492  *
493  * BFileGameSound::BFileGameSound()
494  * BFileGameSound::BFileGameSound(const BFileGameSound &)
495  * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &)
496  */
497 
498 
499 status_t
500 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...)
501 {
502 	return B_ERROR;
503 }
504 
505 
506 status_t
507 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...)
508 {
509 	return B_ERROR;
510 }
511 
512 
513 status_t
514 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...)
515 {
516 	return B_ERROR;
517 }
518 
519 
520 status_t
521 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...)
522 {
523 	return B_ERROR;
524 }
525 
526 
527 status_t
528 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...)
529 {
530 	return B_ERROR;
531 }
532 
533 
534 status_t
535 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...)
536 {
537 	return B_ERROR;
538 }
539 
540 
541 status_t
542 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...)
543 {
544 	return B_ERROR;
545 }
546 
547 
548 status_t
549 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...)
550 {
551 	return B_ERROR;
552 }
553 
554 
555 status_t
556 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...)
557 {
558 	return B_ERROR;
559 }
560 
561 
562 status_t
563 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...)
564 {
565 	return B_ERROR;
566 }
567 
568 
569 status_t
570 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...)
571 {
572 	return B_ERROR;
573 }
574 
575 
576 status_t
577 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...)
578 {
579 	return B_ERROR;
580 }
581 
582 
583 status_t
584 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...)
585 {
586 	return B_ERROR;
587 }
588 
589 
590 status_t
591 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...)
592 {
593 	return B_ERROR;
594 }
595 
596 
597 status_t
598 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...)
599 {
600 	return B_ERROR;
601 }
602 
603 
604 status_t
605 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...)
606 {
607 	return B_ERROR;
608 }
609 
610 
611 status_t
612 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...)
613 {
614 	return B_ERROR;
615 }
616 
617 
618 status_t
619 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...)
620 {
621 	return B_ERROR;
622 }
623 
624 
625 status_t
626 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...)
627 {
628 	return B_ERROR;
629 }
630 
631 
632 status_t
633 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...)
634 {
635 	return B_ERROR;
636 }
637 
638 
639 status_t
640 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...)
641 {
642 	return B_ERROR;
643 }
644 
645 
646 status_t
647 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...)
648 {
649 	return B_ERROR;
650 }
651 
652 
653 status_t
654 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...)
655 {
656 	return B_ERROR;
657 }
658 
659 
660 status_t
661 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...)
662 {
663 	return B_ERROR;
664 }
665 
666 
667