vfs: check userland buffers before reading them.
[haiku.git] / src / kits / game / FileGameSound.cpp
blob4cfe23c2002ec2583e39cff4c65e6f7659bea520
1 /*
2 * Copyright 2001-2012 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Christopher ML Zumwalt May (zummy@users.sf.net)
7 * Jérôme Duval
8 */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #include <Entry.h>
16 #include <FileGameSound.h>
17 #include <MediaFile.h>
18 #include <MediaTrack.h>
19 #include <scheduler.h>
21 #include "GameSoundDevice.h"
22 #include "GSUtility.h"
25 const int32 kPages = 20;
26 struct _gs_media_tracker
28 BMediaFile* file;
29 BMediaTrack* stream;
30 int64 frames;
31 size_t position;
35 // Local utility functions -----------------------------------------------
36 bool
37 FillBuffer(_gs_ramp* ramp, uint8* data, uint8* buffer, size_t* bytes)
39 int32 samples = *bytes / sizeof(uint8);
41 for (int32 byte = 0; byte < samples; byte++) {
42 float gain = *ramp->value;
43 data[byte] = uint8(float(buffer[byte]) * gain);
45 if (ChangeRamp(ramp)) {
46 *bytes = byte * sizeof(uint8);
47 return true;
51 return false;
55 bool
56 FillBuffer(_gs_ramp* ramp, int16* data, int16* buffer, size_t* bytes)
58 int32 samples = *bytes / sizeof(int16);
60 for (int32 byte = 0; byte < samples; byte++) {
61 float gain = *ramp->value;
62 data[byte] = int16(float(buffer[byte]) * gain);
64 if (ChangeRamp(ramp)) {
65 *bytes = byte * sizeof(int16);
66 return true;
70 return false;
74 bool
75 FillBuffer(_gs_ramp* ramp, int32* data, int32* buffer, size_t* bytes)
77 size_t byte = 0;
78 bool bytesAreReady = (*bytes > 0);
80 while (bytesAreReady) {
81 float gain = *ramp->value;
82 data[byte] = int32(float(buffer[byte]) * gain);
84 if (ChangeRamp(ramp)) {
85 *bytes = byte;
86 return true;
89 byte++;
90 bytesAreReady = (byte >= *bytes);
93 return false;
97 bool
98 FillBuffer(_gs_ramp* ramp, float* data, float* buffer, size_t* bytes)
100 size_t byte = 0;
101 bool bytesAreReady = (*bytes > 0);
103 while (bytesAreReady) {
104 float gain = *ramp->value;
105 data[byte] = buffer[byte] * gain;
107 if (ChangeRamp(ramp)) {
108 *bytes = byte;
109 return true;
112 byte++;
113 bytesAreReady = (byte >= *bytes);
116 return false;
120 // BFileGameSound -------------------------------------------------------
121 BFileGameSound::BFileGameSound(const entry_ref* file, bool looping,
122 BGameSoundDevice* device)
124 BStreamingGameSound(device),
125 fAudioStream(NULL),
126 fStopping(false),
127 fLooping(looping),
128 fBuffer(NULL),
129 fPlayPosition(0),
130 fPausing(NULL),
131 fPaused(false),
132 fPauseGain(1.0)
134 if (InitCheck() == B_OK)
135 SetInitError(Init(file));
139 BFileGameSound::BFileGameSound(const char* file, bool looping,
140 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)
152 if (InitCheck() == B_OK) {
153 entry_ref node;
155 if (get_ref_for_path(file, &node) != B_OK)
156 SetInitError(B_ENTRY_NOT_FOUND);
157 else
158 SetInitError(Init(&node));
163 BFileGameSound::~BFileGameSound()
165 if (fReadThread >= 0) {
166 // TODO: kill_thread() is very bad, since it will leak any resources
167 // that the thread had allocated. It will also keep locks locked that
168 // the thread holds! Set a flag to make the thread quit and use
169 // wait_for_thread() here!
170 kill_thread(fReadThread);
173 if (fAudioStream) {
174 if (fAudioStream->stream)
175 fAudioStream->file->ReleaseTrack(fAudioStream->stream);
177 delete fAudioStream->file;
180 delete [] fBuffer;
181 delete fAudioStream;
185 BGameSound*
186 BFileGameSound::Clone() const
188 return NULL;
192 status_t
193 BFileGameSound::StartPlaying()
195 // restart playback if needed
196 if (IsPlaying())
197 StopPlaying();
199 // start playing the file
200 return BStreamingGameSound::StartPlaying();
204 status_t
205 BFileGameSound::StopPlaying()
207 status_t error = BStreamingGameSound::StopPlaying();
209 if (!fAudioStream || !fAudioStream->stream)
210 return B_OK;
212 // start reading next time from the start of the file
213 int64 frame = 0;
214 fAudioStream->stream->SeekToFrame(&frame);
216 fStopping = false;
217 fAudioStream->position = 0;
218 fPlayPosition = 0;
220 return error;
224 status_t
225 BFileGameSound::Preload()
227 if (!IsPlaying())
228 Load();
230 return B_OK;
234 void
235 BFileGameSound::FillBuffer(void* inBuffer, size_t inByteCount)
237 // Split or combine decoder buffers into mixer buffers
238 // fPlayPosition is where we got up to in the input buffer after last call
240 size_t out_offset = 0;
242 while (inByteCount > 0 && !fPaused) {
243 if (!fPaused || fPausing) {
244 printf("mixout %ld, inByteCount %ld, decin %ld, BufferSize %ld\n",
245 out_offset, inByteCount, fPlayPosition, fBufferSize);
246 if (fPlayPosition == 0 || fPlayPosition >= fBufferSize) {
247 Load();
250 if (fPausing) {
251 Lock();
253 bool rampDone = false;
254 size_t bytes = fBufferSize - fPlayPosition;
256 if (bytes > inByteCount) {
257 bytes = inByteCount;
260 // Fill the requested buffer, stopping if the paused flag is set
261 char* buffer = (char*)inBuffer;
263 switch(Format().format) {
264 case gs_audio_format::B_GS_U8:
265 rampDone = ::FillBuffer(fPausing,
266 (uint8*)&buffer[out_offset],
267 (uint8*)&fBuffer[fPlayPosition], &bytes);
268 break;
270 case gs_audio_format::B_GS_S16:
271 rampDone = ::FillBuffer(fPausing,
272 (int16*)&buffer[out_offset],
273 (int16*)&fBuffer[fPlayPosition], &bytes);
274 break;
276 case gs_audio_format::B_GS_S32:
277 rampDone = ::FillBuffer(fPausing,
278 (int32*)&buffer[out_offset],
279 (int32*)&fBuffer[fPlayPosition], &bytes);
280 break;
282 case gs_audio_format::B_GS_F:
283 rampDone = ::FillBuffer(fPausing,
284 (float*)&buffer[out_offset],
285 (float*)&fBuffer[fPlayPosition], &bytes);
286 break;
289 inByteCount -= bytes;
290 out_offset += bytes;
291 fPlayPosition += bytes;
293 // We finished ramping
294 if (rampDone) {
296 // Need to be able to stop asap when pause flag is flipped.
297 while (fPlayPosition < fBufferSize && (inByteCount > 0)) {
298 buffer[out_offset++] = fBuffer[fPlayPosition++];
299 inByteCount--;
302 delete fPausing;
303 fPausing = NULL;
306 Unlock();
307 } else {
309 char* buffer = (char*)inBuffer;
311 // Need to be able to stop asap when the pause flag is flipped.
312 while (fPlayPosition < fBufferSize && (!fPaused || fPausing)
313 && (inByteCount > 0)) {
314 buffer[out_offset++] = fBuffer[fPlayPosition++];
315 inByteCount--;
323 status_t
324 BFileGameSound::Perform(int32 selector, void* data)
326 return B_ERROR;
330 status_t
331 BFileGameSound::SetPaused(bool isPaused, bigtime_t rampTime)
333 if (fPaused == isPaused)
334 return EALREADY;
336 Lock();
338 // Clear any old ramping
339 delete fPausing;
340 fPausing = NULL;
342 if (rampTime > 100000) {
343 // Setup for ramping
344 if (isPaused)
345 fPausing = InitRamp(&fPauseGain, 0.0,
346 Format().frame_rate, rampTime);
347 else
348 fPausing = InitRamp(&fPauseGain, 1.0,
349 Format().frame_rate, rampTime);
352 fPaused = isPaused;
353 Unlock();
355 return B_OK;
359 int32
360 BFileGameSound::IsPaused()
362 if (fPausing)
363 return B_PAUSE_IN_PROGRESS;
365 if (fPaused)
366 return B_PAUSED;
368 return B_NOT_PAUSED;
372 status_t
373 BFileGameSound::Init(const entry_ref* file)
375 fAudioStream = new(std::nothrow) _gs_media_tracker;
376 if (!fAudioStream)
377 return B_NO_MEMORY;
379 memset(fAudioStream, 0, sizeof(_gs_media_tracker));
380 fAudioStream->file = new(std::nothrow) BMediaFile(file);
381 if (!fAudioStream->file) {
382 delete fAudioStream;
383 fAudioStream = NULL;
384 return B_NO_MEMORY;
387 status_t error = fAudioStream->file->InitCheck();
388 if (error != B_OK)
389 return error;
391 fAudioStream->stream = fAudioStream->file->TrackAt(0);
393 // is this is an audio file?
394 media_format playFormat;
395 if ((error = fAudioStream->stream->EncodedFormat(&playFormat)) != B_OK) {
396 fAudioStream->file->ReleaseTrack(fAudioStream->stream);
397 fAudioStream->stream = NULL;
398 return error;
401 if (!playFormat.IsAudio()) {
402 fAudioStream->file->ReleaseTrack(fAudioStream->stream);
403 fAudioStream->stream = NULL;
404 return B_MEDIA_BAD_FORMAT;
407 gs_audio_format dformat = Device()->Format();
409 // request the format we want the sound
410 memset(&playFormat, 0, sizeof(media_format));
411 playFormat.type = B_MEDIA_RAW_AUDIO;
412 if (fAudioStream->stream->DecodedFormat(&playFormat) != B_OK) {
413 fAudioStream->file->ReleaseTrack(fAudioStream->stream);
414 fAudioStream->stream = NULL;
415 return B_MEDIA_BAD_FORMAT;
418 // translate the format into a "GameKit" friendly one
419 gs_audio_format gsformat;
420 media_to_gs_format(&gsformat, &playFormat.u.raw_audio);
422 // Since the buffer sized read from the file is most likely differnt
423 // then the buffer used by the audio mixer, we must allocate a buffer
424 // large enough to hold the largest request.
425 fBufferSize = gsformat.buffer_size;
426 if (fBufferSize < dformat.buffer_size)
427 fBufferSize = dformat.buffer_size;
429 // create the buffer
430 fBuffer = new char[fBufferSize * 2];
431 memset(fBuffer, 0, fBufferSize * 2);
433 fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format);
434 fAudioStream->frames = fAudioStream->stream->CountFrames();
436 // Ask the device to attach our sound to it
437 gs_id sound;
438 error = Device()->CreateBuffer(&sound, this, &gsformat);
439 if (error != B_OK)
440 return error;
442 return BGameSound::Init(sound);
446 bool
447 BFileGameSound::Load()
449 if (!fAudioStream || !fAudioStream->stream)
450 return false;
452 // read a new buffer
453 int64 frames = 0;
454 fAudioStream->stream->ReadFrames(fBuffer, &frames);
455 fBufferSize = frames * fFrameSize;
456 fPlayPosition = 0;
458 if (fBufferSize <= 0) {
459 // EOF
460 if (fLooping) {
461 // start reading next time from the start of the file
462 int64 frame = 0;
463 fAudioStream->stream->SeekToFrame(&frame);
464 } else {
465 StopPlaying();
469 return true;
473 bool
474 BFileGameSound::Read(void* buffer, size_t bytes)
476 return false;
480 /* unimplemented for protection of the user:
482 * BFileGameSound::BFileGameSound()
483 * BFileGameSound::BFileGameSound(const BFileGameSound &)
484 * BFileGameSound &BFileGameSound::operator=(const BFileGameSound &)
488 status_t
489 BFileGameSound::_Reserved_BFileGameSound_0(int32 arg, ...)
491 return B_ERROR;
495 status_t
496 BFileGameSound::_Reserved_BFileGameSound_1(int32 arg, ...)
498 return B_ERROR;
502 status_t
503 BFileGameSound::_Reserved_BFileGameSound_2(int32 arg, ...)
505 return B_ERROR;
509 status_t
510 BFileGameSound::_Reserved_BFileGameSound_3(int32 arg, ...)
512 return B_ERROR;
516 status_t
517 BFileGameSound::_Reserved_BFileGameSound_4(int32 arg, ...)
519 return B_ERROR;
523 status_t
524 BFileGameSound::_Reserved_BFileGameSound_5(int32 arg, ...)
526 return B_ERROR;
530 status_t
531 BFileGameSound::_Reserved_BFileGameSound_6(int32 arg, ...)
533 return B_ERROR;
537 status_t
538 BFileGameSound::_Reserved_BFileGameSound_7(int32 arg, ...)
540 return B_ERROR;
544 status_t
545 BFileGameSound::_Reserved_BFileGameSound_8(int32 arg, ...)
547 return B_ERROR;
551 status_t
552 BFileGameSound::_Reserved_BFileGameSound_9(int32 arg, ...)
554 return B_ERROR;
558 status_t
559 BFileGameSound::_Reserved_BFileGameSound_10(int32 arg, ...)
561 return B_ERROR;
565 status_t
566 BFileGameSound::_Reserved_BFileGameSound_11(int32 arg, ...)
568 return B_ERROR;
572 status_t
573 BFileGameSound::_Reserved_BFileGameSound_12(int32 arg, ...)
575 return B_ERROR;
579 status_t
580 BFileGameSound::_Reserved_BFileGameSound_13(int32 arg, ...)
582 return B_ERROR;
586 status_t
587 BFileGameSound::_Reserved_BFileGameSound_14(int32 arg, ...)
589 return B_ERROR;
593 status_t
594 BFileGameSound::_Reserved_BFileGameSound_15(int32 arg, ...)
596 return B_ERROR;
600 status_t
601 BFileGameSound::_Reserved_BFileGameSound_16(int32 arg, ...)
603 return B_ERROR;
607 status_t
608 BFileGameSound::_Reserved_BFileGameSound_17(int32 arg, ...)
610 return B_ERROR;
614 status_t
615 BFileGameSound::_Reserved_BFileGameSound_18(int32 arg, ...)
617 return B_ERROR;
621 status_t
622 BFileGameSound::_Reserved_BFileGameSound_19(int32 arg, ...)
624 return B_ERROR;
628 status_t
629 BFileGameSound::_Reserved_BFileGameSound_20(int32 arg, ...)
631 return B_ERROR;
635 status_t
636 BFileGameSound::_Reserved_BFileGameSound_21(int32 arg, ...)
638 return B_ERROR;
642 status_t
643 BFileGameSound::_Reserved_BFileGameSound_22(int32 arg, ...)
645 return B_ERROR;
649 status_t
650 BFileGameSound::_Reserved_BFileGameSound_23(int32 arg, ...)
652 return B_ERROR;