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