diff --git a/headers/os/game/FileGameSound.h b/headers/os/game/FileGameSound.h index edd60d199e..218a469490 100644 --- a/headers/os/game/FileGameSound.h +++ b/headers/os/game/FileGameSound.h @@ -1,95 +1,105 @@ -/******************************************************************************* -/ -/ File: FileGameSound.h -/ -/ Description: BFileGameSound is a class that streams data out of a file. -/ -/ Copyright 1999, Be Incorporated, All Rights Reserved -/ -*******************************************************************************/ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: FileGameSound.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BFileGameSound is a class that streams data out of a file. +//------------------------------------------------------------------------------ -#if !defined(_FILE_GAME_SOUND_H) -#define _FILE_GAME_SOUND_H +#ifndef _FILEGAMESOUND_H +#define _FILEGAMESOUND_H +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- #include -class BMediaFile; -namespace BPrivate { - class BTrackReader; -} +// Project Includes ------------------------------------------------------------ +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- +struct _gs_media_tracker; +struct _gs_ramp; + +// FileGameSound ------------------------------------------------------------- class BFileGameSound : public BStreamingGameSound { public: - BFileGameSound( - const entry_ref * file, - bool looping = true, - BGameSoundDevice * device = NULL); - BFileGameSound( - const char * file, - bool looping = true, - BGameSoundDevice * device = NULL); + BFileGameSound(const entry_ref * file, + bool looping = true, + BGameSoundDevice * device = NULL); + BFileGameSound(const char * file, + bool looping = true, + BGameSoundDevice * device = NULL); -virtual ~BFileGameSound(); + virtual ~BFileGameSound(); -virtual BGameSound * Clone() const; + virtual BGameSound * Clone() const; -virtual status_t StartPlaying(); -virtual status_t StopPlaying(); - status_t Preload(); // if you have stopped and want to start quickly again + virtual status_t StartPlaying(); + virtual status_t StopPlaying(); + status_t Preload(); // if you have stopped and want to start quickly again -virtual void FillBuffer( - void * inBuffer, - size_t inByteCount); - -virtual status_t Perform(int32 selector, void * data); - -virtual status_t SetPaused( - bool isPaused, - bigtime_t rampTime); - enum { - B_NOT_PAUSED, - B_PAUSE_IN_PROGRESS, - B_PAUSED - }; - int32 IsPaused(); + virtual void FillBuffer(void * inBuffer, + size_t inByteCount); + virtual status_t Perform(int32 selector, void * data); + virtual status_t SetPaused(bool isPaused, + bigtime_t rampTime); + enum { + B_NOT_PAUSED, + B_PAUSE_IN_PROGRESS, + B_PAUSED + }; + int32 IsPaused(); private: - BMediaFile * _m_soundFile; - BPrivate::BTrackReader * _m_trackReader; - bool _m_looping; - bool _m_stopping; - uchar _m_zero; - uchar _m_reserved_u; - - // cruft for read-ahead goes here - thread_id _m_streamThread; - port_id _m_streamPort; - enum { - cmdStartPlaying = 1, - cmdStopPlaying, - cmdUsedBytes, - cmdPreload - }; - int32 _m_loopCount; - char * _m_buffer; - size_t _m_bufferSize; - int32 _m_stopCount; - int32 _m_pauseState; - float _m_pauseGain; - float _m_pausePan; - -static status_t stream_thread( - void * that); - void LoadFunc(); - size_t Load( - size_t & offset, - size_t bytes); - void read_data( - char * dest, - size_t bytes); - + _gs_media_tracker * fAudioStream; + + bool fStopping; + bool fLooping; + char fReserved; + char * fBuffer; + + size_t fFrameSize; + size_t fBufferSize; + size_t fPlayPosition; + + thread_id fReadThread; + port_id fPort; + + _gs_ramp * fPausing; + bool fPaused; + float fPauseGain; + + status_t Init(const entry_ref* file); + + static int32 ReadThread(void* arg); + + bool Load(); + bool Read(void * buffer, size_t bytes); + /* leave these declarations private unless you plan on actually implementing and using them. */ BFileGameSound(); BFileGameSound(const BFileGameSound&); @@ -97,7 +107,7 @@ static status_t stream_thread( /* fbc data and virtuals */ - uint32 _reserved_BFileGameSound_[8]; + uint32 _reserved_BFileGameSound_[9]; status_t _Reserved_BFileGameSound_0(int32 arg, ...); /* SetPaused(bool paused, bigtime_t ramp); */ virtual status_t _Reserved_BFileGameSound_1(int32 arg, ...); diff --git a/headers/os/game/GameSound.h b/headers/os/game/GameSound.h index 9de8eaa276..5ca2514677 100644 --- a/headers/os/game/GameSound.h +++ b/headers/os/game/GameSound.h @@ -1,110 +1,109 @@ -/******************************************************************************* -/ -/ File: GameGameSound.h -/ -/ Description: BGameSound is an abstract base class for all sounds being -/ played using the gamesound kit. Use one of the concrete -/ subclasses for actually playing sound. -/ -/ Copyright 1999, Be Incorporated, All Rights Reserved -/ -*******************************************************************************/ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSound.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Base class for play sounds using the game kit +//------------------------------------------------------------------------------ +#ifndef _GAMESOUND_H +#define _GAMESOUND_H -#if !defined(_GAME_SOUND_H) -#define _GAME_SOUND_H - +// Standard Includes ----------------------------------------------------------- +// System Includes ------------------------------------------------------------- #include -#include -struct gs_audio_format; +// Project Includes ------------------------------------------------------------ + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- class BGameSoundDevice; -namespace BPrivate { - class PrivGameSound; -} - +// BGameSound class ------------------------------------------------------------- class BGameSound { public: - BGameSound( - BGameSoundDevice * device = NULL); -virtual ~BGameSound(); + BGameSound(BGameSoundDevice * device = NULL); + virtual ~BGameSound(); -virtual BGameSound * Clone() const = 0; - status_t InitCheck() const; + virtual BGameSound * Clone() const = 0; + status_t InitCheck() const; + + // BGameSound attributes + BGameSoundDevice * Device() const; + gs_id ID() const; + const gs_audio_format & Format() const; // only valid after Init() - BGameSoundDevice * Device() const; - gs_id ID() const; - const gs_audio_format & - Format() const; // only valid after Init() + // Playing sounds + virtual status_t StartPlaying(); + virtual bool IsPlaying(); + virtual status_t StopPlaying(); -virtual status_t StartPlaying(); -virtual bool IsPlaying(); -virtual status_t StopPlaying(); + + // Modifing the playback + status_t SetGain(float gain, + bigtime_t duration = 0); // ramp duration in seconds + status_t SetPan(float pan, + bigtime_t duration = 0); // ramp duration in seconds + float Gain(); + float Pan(); - status_t SetGain( - float gain, - bigtime_t duration = 0); // ramp duration in seconds - status_t SetPan( - float pan, - bigtime_t duration = 0); // ramp duration in seconds - float Gain(); - float Pan(); + virtual status_t SetAttributes(gs_attribute * inAttributes, + size_t inAttributeCount); + virtual status_t GetAttributes(gs_attribute * outAttributes, + size_t inAttributeCount); -virtual status_t SetAttributes( - gs_attribute * inAttributes, - size_t inAttributeCount); -virtual status_t GetAttributes( - gs_attribute * outAttributes, - size_t inAttributeCount); - -virtual status_t Perform(int32 selector, void * data); - - void * operator new( - size_t size); - void * operator new( - size_t size, - const nothrow_t &) throw(); - void operator delete( - void * ptr); -#if !__MWERKS__ - // there's a bug in MWCC under R4.1 and earlier - void operator delete( - void * ptr, - const nothrow_t &) throw(); -#endif - -static status_t SetMemoryPoolSize( // implemented in PrivGameSound.cpp - size_t in_poolSize); -static status_t LockMemoryPool( // implemented in PrivGameSound.cpp - bool in_lockInCore); -static int32 SetMaxSoundCount( // implemented in PrivGameSound.cpp - int32 in_maxCount); +// void * operator new(size_t size); +// void * operator new(size_t size, const nothrow_t &) throw(); +// void operator delete(void * ptr); +//#if !__MWERKS__ + // there's a bug in MWCC under R4.1 and earlier +// void operator delete(void * ptr, +// const nothrow_t &) throw(); +//#endif + static status_t SetMemoryPoolSize(size_t in_poolSize); + static status_t LockMemoryPool(bool in_lockInCore); + static int32 SetMaxSoundCount(int32 in_maxCount); + + virtual status_t Perform(int32 selector, void * data); protected: - status_t SetInitError( - status_t in_initError); - status_t Init( - gs_id handle); - - BGameSound( - const BGameSound & other); - BGameSound & operator=( - const BGameSound & other); + status_t SetInitError(status_t in_initError); + status_t Init(gs_id handle); + BGameSound(const BGameSound & other); + BGameSound & operator=(const BGameSound & other); + private: - - friend class BPrivate::PrivGameSound; - - gs_id _m_handle; - BGameSoundDevice * _m_device; - gs_audio_format - _m_format; - status_t _m_initError; - + BGameSoundDevice* fDevice; + status_t fInitError; + + gs_audio_format fFormat; + gs_id fSound; + /* leave these declarations private unless you plan on actually implementing and using them. */ BGameSound(); diff --git a/headers/os/game/GameSoundDefs.h b/headers/os/game/GameSoundDefs.h index bfb13d27a5..a3f17fefa2 100644 --- a/headers/os/game/GameSoundDefs.h +++ b/headers/os/game/GameSoundDefs.h @@ -1,12 +1,28 @@ -/******************************************************************************* -/ -/ File: GameSoundDefs.h -/ -/ Description: Definitions of common types and constants for the gamesound kit. -/ -/ Copyright 1999, Be Incorporated, All Rights Reserved -/ -*******************************************************************************/ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSound.cpp +// Author: Christopher ML Zumwalt May (cdzumwalt@dsl-only.net) +// Description: Declerations for game sounds +//------------------------------------------------------------------------------ #if !defined(_GAME_SOUND_DEFS_H) #define _GAME_SOUND_DEFS_H diff --git a/headers/os/game/PushGameSound.h b/headers/os/game/PushGameSound.h index 009c17d61e..994a7dcd1a 100644 --- a/headers/os/game/PushGameSound.h +++ b/headers/os/game/PushGameSound.h @@ -1,30 +1,54 @@ -/******************************************************************************* -/ -/ File: PushGameSound.h -/ -/ Description: BPushGameSound is a class for games that want to push data -/ at the system, rather than have a callback get called. -/ -/ Copyright 1999, Be Incorporated, All Rights Reserved -/ -*******************************************************************************/ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: FileGameSound.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BFileGameSound is a class that streams data out of a file. +//------------------------------------------------------------------------------ +#ifndef _PUSHGAMESOUND_H +#define _PUSHGAMESOUND_H +// Standard Includes ----------------------------------------------------------- -#if !defined(_PUSH_GAME_SOUND_H) -#define _PUSH_GAME_SOUND_H - +// System Includes ------------------------------------------------------------- #include +// Project Includes ------------------------------------------------------------ + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- + +// FileGameSound ------------------------------------------------------------- class BPushGameSound : public BStreamingGameSound { public: - BPushGameSound( - size_t inBufferFrameCount, - const gs_audio_format * format, - size_t inBufferCount = 2, - BGameSoundDevice * device = NULL); + BPushGameSound(size_t inBufferFrameCount, + const gs_audio_format * format, + size_t inBufferCount = 2, + BGameSoundDevice * device = NULL); -virtual ~BPushGameSound(); + virtual ~BPushGameSound(); enum lock_status { lock_failed = -1, // not yet time to do more @@ -32,56 +56,57 @@ virtual ~BPushGameSound(); lock_ok_frames_dropped // you may have missed some buffers }; -virtual lock_status LockNextPage( - void ** out_pagePtr, - size_t * out_pageSize); -virtual status_t UnlockPage( - void * in_pagePtr); + virtual lock_status LockNextPage(void ** out_pagePtr, + size_t * out_pageSize); + virtual status_t UnlockPage(void * in_pagePtr); -virtual lock_status LockForCyclic( - void ** out_basePtr, - size_t * out_size); -virtual status_t UnlockCyclic(); -virtual size_t CurrentPosition(); + virtual lock_status LockForCyclic(void ** out_basePtr, + size_t * out_size); + virtual status_t UnlockCyclic(); + virtual size_t CurrentPosition(); -virtual BGameSound * Clone() const; + virtual BGameSound * Clone() const; -virtual status_t Perform(int32 selector, void * data); + virtual status_t Perform(int32 selector, void * data); protected: + + BPushGameSound(BGameSoundDevice * device); -virtual status_t SetParameters( // will allocate handle and call Init() - size_t inBufferFrameCount, - const gs_audio_format * format, - size_t inBufferCount); + virtual status_t SetParameters(size_t inBufferFrameCount, + const gs_audio_format * format, + size_t inBufferCount); -virtual status_t SetStreamHook( - void (*hook)(void * inCookie, void * inBuffer, size_t inByteCount, BStreamingGameSound * me), - void * cookie); -virtual void FillBuffer( // default is to call stream hook, if any - void * inBuffer, - size_t inByteCount); + virtual status_t SetStreamHook(void (*hook)(void * inCookie, void * inBuffer, size_t inByteCount, BStreamingGameSound * me), + void * cookie); + + virtual void FillBuffer(void * inBuffer, + size_t inByteCount); private: - /* leave these declarations private unless you plan on actually implementing and using them. */ - BPushGameSound(); - BPushGameSound(const BPushGameSound&); - BPushGameSound& operator=(const BPushGameSound&); + /* leave these declarations private unless you plan on actually implementing and using them. */ + BPushGameSound(); + BPushGameSound(const BPushGameSound&); + BPushGameSound& operator=(const BPushGameSound&); - int32 _m_benCount; - sem_id _m_benSem; - char * _m_lockedPtr; - char * _m_playedPtr; - size_t _m_pageSize; - int32 _m_pageCount; - size_t _m_bufSize; - char * _m_buffer; + bool BytesReady(size_t * bytes); + + sem_id fLock; + BList * fPageLocked; + + size_t fLockPos; + size_t fPlayPos; + + char * fBuffer; + size_t fPageSize; + int32 fPageCount; + size_t fBufferSize; - /* fbc data and virtuals */ + /* fbc data and virtuals */ - uint32 _reserved_BPushGameSound_[12]; + uint32 _reserved_BPushGameSound_[12]; virtual status_t _Reserved_BPushGameSound_0(int32 arg, ...); virtual status_t _Reserved_BPushGameSound_1(int32 arg, ...); diff --git a/headers/os/game/SimpleGameSound.h b/headers/os/game/SimpleGameSound.h index 121811daf8..624815fa5a 100644 --- a/headers/os/game/SimpleGameSound.h +++ b/headers/os/game/SimpleGameSound.h @@ -1,50 +1,78 @@ -/* SimpleGameSound.h */ -/******************************************************************************* -/ -/ File: SimpleGameSound.h -/ -/ Description: BSimpleGameSound is a class for sound effects that are -/ short, and consists of non-changing samples in memory. -/ -/ Copyright 1999, Be Incorporated, All Rights Reserved -/ -*******************************************************************************/ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: SimpleGameSound.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BSimpleGameSound is a class for sound effects that are +// short, and consists of non-changing samples in memory. +//------------------------------------------------------------------------------ -#if !defined(_SIMPLE_GAME_SOUND_H) -#define _SIMPLE_GAME_SOUND_H +#ifndef _SIMPLEGAMESOUND_H +#define _SIMPLEGAMESOUND_H + +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- +#include #include +// Project Includes ------------------------------------------------------------ + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- + +// BGameSound class ------------------------------------------------------------- class BSimpleGameSound : public BGameSound { public: - BSimpleGameSound( - const entry_ref * inFile, - BGameSoundDevice * device = NULL); - BSimpleGameSound( - const char * inFile, - BGameSoundDevice * device = NULL); - BSimpleGameSound( - const void * inData, - size_t inFrameCount, - const gs_audio_format * format, - BGameSoundDevice * device = NULL); + BSimpleGameSound(const entry_ref * inFile, + BGameSoundDevice * device = NULL); + BSimpleGameSound(const char * inFile, + BGameSoundDevice * device = NULL); + BSimpleGameSound(const void * inData, + size_t inFrameCount, + const gs_audio_format * format, + BGameSoundDevice * device = NULL); - BSimpleGameSound( - const BSimpleGameSound & other); + BSimpleGameSound(const BSimpleGameSound & other); -virtual ~BSimpleGameSound(); + virtual ~BSimpleGameSound(); -virtual BGameSound * Clone() const; + virtual BGameSound * Clone() const; -virtual status_t Perform(int32 selector, void * data); + virtual status_t Perform(int32 selector, void * data); - status_t SetIsLooping( - bool looping); // whether to go back to beginning - bool IsLooping() const; + status_t SetIsLooping(bool looping); + bool IsLooping() const; private: - + status_t Init(const entry_ref * inFile); + status_t Init(const void* inData, + int64 inFrameCount, + const gs_audio_format* format); + /* leave these declarations private unless you plan on actually implementing and using them. */ BSimpleGameSound(); BSimpleGameSound& operator=(const BSimpleGameSound&); diff --git a/headers/os/game/StreamingGameSound.h b/headers/os/game/StreamingGameSound.h index 61ec22af75..2a075119ee 100644 --- a/headers/os/game/StreamingGameSound.h +++ b/headers/os/game/StreamingGameSound.h @@ -1,67 +1,87 @@ -/******************************************************************************* -/ -/ File: StreamingGameSound.h -/ -/ Description: BStreamingGameSound is a class for all kinds of streaming -/ (data not known beforehand) game sounds. -/ -/ Copyright 1999, Be Incorporated, All Rights Reserved -/ -*******************************************************************************/ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: StreamingGameSound.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BStreamingGameSound is a class for all kinds of streaming +// (data not known beforehand) game sounds. +//------------------------------------------------------------------------------ -#if !defined(_STREAMING_GAME_SOUND_H) -#define _STREAMING_GAME_SOUND_H +#ifndef _STREAMINGGAMESOUND_H +#define _STREAMINGGAMESOUND_H +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- #include #include #include +// Project Includes ------------------------------------------------------------ + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- + class BStreamingGameSound : public BGameSound { public: - BStreamingGameSound( - size_t inBufferFrameCount, - const gs_audio_format * format, - size_t inBufferCount = 2, - BGameSoundDevice * device = NULL); + BStreamingGameSound(size_t inBufferFrameCount, + const gs_audio_format * format, + size_t inBufferCount = 2, + BGameSoundDevice * device = NULL); -virtual ~BStreamingGameSound(); + virtual ~BStreamingGameSound(); -virtual BGameSound * Clone() const; + virtual BGameSound * Clone() const; -virtual status_t SetStreamHook( - void (*hook)(void * inCookie, void * inBuffer, size_t inByteCount, BStreamingGameSound * me), - void * cookie); -virtual void FillBuffer( // default is to call stream hook, if any - void * inBuffer, - size_t inByteCount); + virtual status_t SetStreamHook(void (*hook)(void * inCookie, void * inBuffer, size_t inByteCount, BStreamingGameSound * me), + void * cookie); + virtual void FillBuffer(void * inBuffer, + size_t inByteCount); -virtual status_t SetAttributes( - gs_attribute * inAttributes, - size_t inAttributeCount); + virtual status_t SetAttributes(gs_attribute * inAttributes, + size_t inAttributeCount); -virtual status_t Perform(int32 selector, void * data); + virtual status_t Perform(int32 selector, void * data); protected: - BStreamingGameSound( - BGameSoundDevice * device); -virtual status_t SetParameters( // will allocate handle and call Init() - size_t inBufferFrameCount, - const gs_audio_format * format, - size_t inBufferCount); - - bool Lock(); - void Unlock(); + BStreamingGameSound(BGameSoundDevice * device); + + virtual status_t SetParameters(size_t inBufferFrameCount, + const gs_audio_format * format, + size_t inBufferCount); + + bool Lock(); + void Unlock(); private: - void (*_m_streamHook)(void * cookie, void * buffer, size_t bytes, BStreamingGameSound * sound); - void * _m_streamCookie; - BLocker _m_lock; - -static void stream_callback(void * cookie, gs_id handle, void * buffer, int32 offset, int32 size, size_t bufSize); + void (*fStreamHook)(void * cookie, void * buffer, size_t bytes, BStreamingGameSound * sound); + void * fStreamCookie; + BLocker fLock; /* leave these declarations private unless you plan on actually implementing and using them. */ BStreamingGameSound(); diff --git a/src/kits/game/FileGameSound.cpp b/src/kits/game/FileGameSound.cpp index 2fd12e2c01..b242982ce4 100644 --- a/src/kits/game/FileGameSound.cpp +++ b/src/kits/game/FileGameSound.cpp @@ -1,32 +1,199 @@ -/* - * Copyright 2002, Marcus Overhagen. All rights reserved. - * Distributed under the terms of the MIT License. - */ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: FileGameSound.cpp +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BFileGameSound is a class that streams data out of a file. +//------------------------------------------------------------------------------ +// Standard Includes ----------------------------------------------------------- +#include + +// System Includes ------------------------------------------------------------- +#include +#include +#include +#include + +// Project Includes ------------------------------------------------------------ +#include +#include + +// Local Includes -------------------------------------------------------------- #include -static const size_t default_in_buffer_frame_count = 4096; // XXX change this -static const gs_audio_format default_format = {}; // XXX change this -static const size_t default_in_buffer_count = 2; // XXX change this +// Local Defines --------------------------------------------------------------- +const int32 kPages = 20; +struct _gs_media_tracker +{ + BMediaFile * file; + BMediaTrack * stream; + int64 frames; + size_t position; +}; +// Locale utility functions ----------------------------------------------- +bool FillBuffer(_gs_ramp * ramp, uint8 * data, uint8 * buffer, size_t * bytes) +{ + int32 samples = *bytes / sizeof(int16); + + for(int32 byte = 0; byte < samples; byte++) + { + float gain = *ramp->value; + data[byte] = uint8(float(buffer[byte]) * gain); + + if (ChangeRamp(ramp)) + { + *bytes = byte * sizeof(int16); + return true; + } + } + + return false; +} + +bool FillBuffer(_gs_ramp * ramp, int16 * data, int16 * buffer, size_t * bytes) +{ + int32 samples = *bytes / sizeof(int16); + + for(int32 byte = 0; byte < samples; byte++) + { + float gain = *ramp->value; + data[byte] = int16(float(buffer[byte]) * gain); + + if (ChangeRamp(ramp)) + { + *bytes = byte * sizeof(int16); + return true; + } + } + + return false; +} + +bool FillBuffer(_gs_ramp * ramp, int32 * data, int32 * buffer, size_t * bytes) +{ + size_t byte = 0; + bool bytesAreReady = (*bytes > 0); + + while(bytesAreReady) + { + float gain = *ramp->value; + data[byte] = int32(float(buffer[byte]) * gain); + + if (ChangeRamp(ramp)) + { + *bytes = byte; + return true; + } + + byte++; + bytesAreReady = (byte >= *bytes); + } + + return false; +} + +bool FillBuffer(_gs_ramp * ramp, float * data, float * buffer, size_t * bytes) +{ + size_t byte = 0; + bool bytesAreReady = (*bytes > 0); + + while(bytesAreReady) + { + float gain = *ramp->value; + data[byte] = buffer[byte] * gain; + + if (ChangeRamp(ramp)) + { + *bytes = byte; + return true; + } + + byte++; + bytesAreReady = (byte >= *bytes); + } + + return false; +} + +// BFileGameSound ------------------------------------------------------- BFileGameSound::BFileGameSound(const entry_ref *file, bool looping, BGameSoundDevice *device) - : BStreamingGameSound(default_in_buffer_frame_count, &default_format, default_in_buffer_count, device) + : BStreamingGameSound(device), + fAudioStream(NULL), + fStopping(false), + fLooping(looping), + fBuffer(NULL), + fPlayPosition(0), + fPausing(NULL), + fPaused(false), + fPauseGain(1.0) { + if (InitCheck() == B_OK) SetInitError(Init(file)); } BFileGameSound::BFileGameSound(const char *file, bool looping, BGameSoundDevice *device) - : BStreamingGameSound(default_in_buffer_frame_count, &default_format, default_in_buffer_count, device) + : BStreamingGameSound(device), + fAudioStream(NULL), + fStopping(false), + fLooping(looping), + fBuffer(NULL), + fPlayPosition(0), + fPausing(NULL), + fPaused(false), + fPauseGain(1.0) { + if (InitCheck() == B_OK) + { + entry_ref node; + + if (get_ref_for_path(file, &node) != B_OK) + SetInitError(B_ENTRY_NOT_FOUND); + else + SetInitError(Init(&node)); + } } BFileGameSound::~BFileGameSound() { + if (fReadThread >= 0) kill_thread(fReadThread); + + if (fAudioStream) + { + if (fAudioStream->stream) fAudioStream->file->ReleaseTrack(fAudioStream->stream); + + fAudioStream->file->CloseFile(); + + //delete fAudioStream->stream; + delete fAudioStream->file; + } + + delete [] fBuffer; + delete fAudioStream; } @@ -40,21 +207,53 @@ BFileGameSound::Clone() const status_t BFileGameSound::StartPlaying() { - return B_ERROR; + // restart playback if needed + if (IsPlaying()) StopPlaying(); + + fPort = create_port(kPages, "audioque"); + + // create the thread that will read the file + fReadThread = spawn_thread(ReadThread, "audiostream", B_NORMAL_PRIORITY, this); + if (fReadThread < B_OK) return B_NO_MORE_THREADS; + + status_t error = resume_thread(fReadThread); + if (error != B_OK) return error; + + // start playing the file + return BStreamingGameSound::StartPlaying(); } status_t BFileGameSound::StopPlaying() { - return B_ERROR; + status_t error = BStreamingGameSound::StopPlaying(); + + // start reading next time from the start of the file + int64 frame = 0; + fAudioStream->stream->SeekToFrame(&frame); + + fStopping = false; + fAudioStream->position = 0; + fPlayPosition = 0; + + // we don't need to read any more + kill_thread(fReadThread); + delete_port(fPort); + + return error; } status_t BFileGameSound::Preload() { - return B_ERROR; + if (!IsPlaying()) + { + for(int32 i = 0; i < kPages / 2; i++) Load(); + } + + return B_OK; } @@ -62,6 +261,79 @@ void BFileGameSound::FillBuffer(void *inBuffer, size_t inByteCount) { + int32 cookie; + size_t bytes = inByteCount; + + if (!fPaused || fPausing) + { + // time to read a new buffer + if (fPlayPosition == 0) read_port_etc(fPort, &cookie, fBuffer, fBufferSize, B_TIMEOUT, 0); + + if (fPausing) + { + Lock(); + + bool rampDone = false; + + // Fill the requsted buffer, stopping if the paused flag is set + switch(Format().format) + { + case gs_audio_format::B_GS_U8: + rampDone = ::FillBuffer(fPausing, (uint8*)inBuffer, (uint8*)&fBuffer[fPlayPosition], &bytes); + break; + + case gs_audio_format::B_GS_S16: + rampDone = ::FillBuffer(fPausing, (int16*)inBuffer, (int16*)&fBuffer[fPlayPosition], &bytes); + break; + + case gs_audio_format::B_GS_S32: + rampDone = ::FillBuffer(fPausing, (int32*)inBuffer, (int32*)&fBuffer[fPlayPosition], &bytes); + break; + + case gs_audio_format::B_GS_F: + rampDone = ::FillBuffer(fPausing, (float*)inBuffer, (float*)&fBuffer[fPlayPosition], &bytes); + break; + } + + // We finished ramping + if (rampDone) + { + if (bytes < inByteCount && !fPausing) + { + // Since are resumming play back, we need to copy any remaining samples + char * buffer = (char*)inBuffer; + memcpy(&buffer[bytes], &fBuffer[fPlayPosition + bytes], inByteCount - bytes); + } + + delete fPausing; + fPausing = NULL; + } + + Unlock(); + } + else + { + size_t byte = 0; + char * buffer = (char*)inBuffer; + + // We need to be able to stop asap when the pause flag is flipped. + while(byte < bytes && (!fPaused || fPausing)) + { + buffer[byte] = fBuffer[fPlayPosition + byte]; + byte++; + } + + bytes = byte; + } + } + + fPlayPosition += bytes; + if (fPlayPosition >= fBufferSize) + { + // We have finished reading the buffer. Setup for the next buffer. + fPlayPosition = 0; + memset(fBuffer, 0, fBufferSize); + } } @@ -77,45 +349,145 @@ status_t BFileGameSound::SetPaused(bool isPaused, bigtime_t rampTime) { - return B_ERROR; + if (fPaused != isPaused) + { + Lock(); + + // Clear any old ramping + delete fPausing; + fPausing = NULL; + + if (rampTime > 100000) + { + // Setup for ramping + if (isPaused) + fPausing = InitRamp(&fPauseGain, 0.0, Format().frame_rate, rampTime); + else + fPausing = InitRamp(&fPauseGain, 1.0, Format().frame_rate, rampTime); + } + + fPaused = isPaused; + Unlock(); + } + + return B_OK; } int32 BFileGameSound::IsPaused() { - return 0; + if (fPausing) return B_PAUSE_IN_PROGRESS; + + if (fPaused) return B_PAUSED; + + return B_NOT_PAUSED; } status_t -BFileGameSound::stream_thread(void *that) +BFileGameSound::Init(const entry_ref* file) { - return B_ERROR; + fAudioStream = new _gs_media_tracker; + memset(fAudioStream, 0, sizeof(_gs_media_tracker)); + + fAudioStream->file = new BMediaFile(file); + status_t error = fAudioStream->file->InitCheck(); + if (error != B_OK) return error; + + fAudioStream->stream = fAudioStream->file->TrackAt(0); + + // is this is an audio file? + media_format mformat; + fAudioStream->stream->EncodedFormat(&mformat); + if (!mformat.IsAudio()) return B_MEDIA_BAD_FORMAT; + + gs_audio_format dformat = Device()->Format(); + + // request the format we want the sound + memset(&mformat, 0, sizeof(media_format)); + mformat.type = B_MEDIA_RAW_AUDIO; + fAudioStream->stream->DecodedFormat(&mformat); + + // translate the format into a "GameKit" friendly one + gs_audio_format gsformat; + media_to_gs_format(&gsformat, &mformat.u.raw_audio); + + // Since the buffer sized read from the file is most likely differnt + // then the buffer used by the audio mixer, we must allocate a buffer + // large enough to hold the largest request. + fBufferSize = gsformat.buffer_size; + if (fBufferSize < dformat.buffer_size) fBufferSize = dformat.buffer_size; + + // create the buffer + fBuffer = new char[fBufferSize * 2]; + memset(fBuffer, 0, fBufferSize * 2); + + fFrameSize = gsformat.channel_count * get_sample_size(gsformat.format); + fAudioStream->frames = fAudioStream->stream->CountFrames(); + + // Ask the device to attach our sound to it + gs_id sound; + error = Device()->CreateBuffer(&sound, this, &gsformat); + if (error != B_OK) return error; + + return BGameSound::Init(sound); } -void -BFileGameSound::LoadFunc() -{ -} - - -size_t -BFileGameSound::Load(size_t &offset, - size_t bytes) +int32 +BFileGameSound::ReadThread(void* arg) { + BFileGameSound* obj = (BFileGameSound*)arg; + + while(true) obj->Load(); + return 0; } -void -BFileGameSound::read_data(char *dest, - size_t bytes) +bool +BFileGameSound::Load() { + int64 frames; + char * buffer = &fBuffer[fBufferSize + fAudioStream->position]; + fAudioStream->stream->ReadFrames(buffer, &frames); + + int32 frame = fAudioStream->stream->CurrentFrame(); + + fAudioStream->position += fFrameSize * frames; + if (fAudioStream->position >= fBufferSize) + { + // we have filled the enter buffer, time to send + write_port(fPort, fBufferSize, &fBuffer[fBufferSize], fBufferSize); + fAudioStream->position = 0; + } + + if (frame >= fAudioStream->frames) + { + if (fLooping) + { + // since we are looping, we need to start reading from + // the begining of the file again. + int64 firstFrame = 0; + fAudioStream->stream->SeekToFrame(&firstFrame); + + fStopping = true; + } + else fStopping = true; + } + + return true; } +bool +BFileGameSound::Read(void * buffer, size_t bytes) +{ + return false; +} + + /* unimplemented for protection of the user: * * BFileGameSound::BFileGameSound() diff --git a/src/kits/game/GSUtility.cpp b/src/kits/game/GSUtility.cpp new file mode 100644 index 0000000000..dabfeb9b61 --- /dev/null +++ b/src/kits/game/GSUtility.cpp @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSoundDevice.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Utility functions used by sound system +//------------------------------------------------------------------------------ + +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- +#include + +// Project Includes ------------------------------------------------------------ +#include + +// Local Includes -------------------------------------------------------------- +#include + +// Local Defines --------------------------------------------------------------- + +// Utility functions ----------------------------------------------------------- +_gs_ramp* InitRamp(float* value, float set, float frames, bigtime_t duration) +{ + float diff = (set > *value) ? set - *value : *value - set; + bigtime_t sec = bigtime_t(duration / 1000000.0); + float inc = diff * 200; + + _gs_ramp* ramp = new _gs_ramp; + ramp->value = value; + + ramp->frame_total = frames * sec; + ramp->frame_inc = int(ramp->frame_total / inc); + + ramp->inc = (set - *value) / inc; + + ramp->frame_count = 0; + ramp->frame_inc_count = 0; + + ramp->duration = duration; + + return ramp; +} + +bool ChangeRamp(_gs_ramp* ramp) +{ + if (ramp->frame_count > ramp->frame_total) return true; + + if (ramp->frame_inc_count >= ramp->frame_inc) + { + ramp->frame_inc_count = 0; + *ramp->value += ramp->inc; + } + else ramp->frame_inc_count++; + + ramp->frame_count++; + return false; +} + +size_t get_sample_size(int32 format) +{ + size_t sample; + + switch(format) + { + case media_raw_audio_format::B_AUDIO_CHAR: + sample = sizeof(char); + break; + + case gs_audio_format::B_GS_U8: + sample = sizeof(uint8); + break; + + case gs_audio_format::B_GS_S16: + sample = sizeof(int16); + break; + + case gs_audio_format::B_GS_S32: + sample = sizeof(int32); + break; + + case gs_audio_format::B_GS_F: + sample = sizeof(float); + break; + + default: sample = 0; + } + + return sample; +} + +void media_to_gs_format(gs_audio_format* dest, media_raw_audio_format* source) +{ + dest->format = source->format; + dest->frame_rate = source->frame_rate; + dest->channel_count = source->channel_count; + dest->byte_order = source->byte_order; + dest->buffer_size = source->buffer_size; +} diff --git a/src/kits/game/GSUtility.h b/src/kits/game/GSUtility.h new file mode 100644 index 0000000000..63afdd1e6a --- /dev/null +++ b/src/kits/game/GSUtility.h @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSoundDevice.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Utility functions used by sound system +//------------------------------------------------------------------------------ + +#ifndef _GAMESOUND_UTILITY_H +#define _GAMESOUND_UTILITY_H + +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- + +// Project Includes ------------------------------------------------------------ +#include + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +struct _gs_ramp +{ + float inc; + float orignal; + float* value; + + float frame_total; + float frame_inc; + + float frame_count; + float frame_inc_count; + + bigtime_t duration; +}; + +_gs_ramp* InitRamp(float* value, float set, float frames, bigtime_t duration); +bool ChangeRamp(_gs_ramp* ramp); +size_t get_sample_size(int32 format); +void media_to_gs_format(gs_audio_format * dest, media_raw_audio_format * source); + +#endif diff --git a/src/kits/game/GameProducer.cpp b/src/kits/game/GameProducer.cpp new file mode 100644 index 0000000000..16b252eb10 --- /dev/null +++ b/src/kits/game/GameProducer.cpp @@ -0,0 +1,562 @@ +/*****************************************************************************/ +// GameProdcure.h +// +// This produce creates audio buffer on behalf of the GameKit. +// +// Copyright (c) 2001 OpenBeOS Project +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameProducer.cpp +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: A MediaKit producer node which mixes sound from the GameKit +// and sends them to the audio mixer +/*****************************************************************************/ + +// Standard Includes ----------------------------------------------------------- +#include +#include + +// System Includes ------------------------------------------------------------- +#include +#include +#include +#include +#include +#include + +// Project Includes ------------------------------------------------------------ +#include +#include +#include + +// Local Includes -------------------------------------------------------------- +#include + +// Local Defines --------------------------------------------------------------- +struct _gs_play +{ + gs_id sound; + bool * hook; + + _gs_play * next; + _gs_play * previous; +}; + +GameProducer::GameProducer(GameSoundBuffer * object, + const gs_audio_format * format) + : BMediaNode("GameProducer.h"), + BBufferProducer(B_MEDIA_RAW_AUDIO), + BMediaEventLooper(), + fBufferGroup(NULL), + fLatency(0), + fInternalLatency(0), + fOutputEnabled(true) +{ + // initialize our preferred format object + fPreferredFormat.type = B_MEDIA_RAW_AUDIO; + fPreferredFormat.u.raw_audio.format = format->format; + fPreferredFormat.u.raw_audio.channel_count = format->channel_count; + fPreferredFormat.u.raw_audio.frame_rate = format->frame_rate; // measured in Hertz + fPreferredFormat.u.raw_audio.byte_order = format->byte_order; +// fPreferredFormat.u.raw_audio.channel_mask = B_CHANNEL_LEFT & B_CHANNEL_RIGHT; +// fPreferredFormat.u.raw_audio.valid_bits = 32; +// fPreferredFormat.u.raw_audio.matrix_mask = B_MATRIX_AMBISONIC_WXYZ; + + // we'll use the consumer's preferred buffer size, if any + fPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size; + + // we're not connected yet + fOutput.destination = media_destination::null; + fOutput.format = fPreferredFormat; + + fFrameSize = get_sample_size(format->format) * format->channel_count; + fObject = object; +} + + +GameProducer::~GameProducer() +{ + // Stop the BMediaEventLooper thread + Quit(); +} + + +// BMediaNode methods +BMediaAddOn * +GameProducer::AddOn(int32 *internal_id) const +{ + return NULL; +} + + +// BBufferProducer methods +status_t +GameProducer::GetNextOutput(int32* cookie, media_output* out_output) +{ + // we currently support only one output + if (0 != *cookie) return B_BAD_INDEX; + + *out_output = fOutput; + *cookie += 1; + return B_OK; +} + + +status_t +GameProducer::DisposeOutputCookie(int32 cookie) +{ + // do nothing because our cookie is only an integer + return B_OK; +} + + +void +GameProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_) +{ + // If I had more than one output, I'd have to walk my list of output records to see + // which one matched the given source, and then enable/disable that one. But this + // node only has one output, so I just make sure the given source matches, then set + // the enable state accordingly. + if (what == fOutput.source) + { + fOutputEnabled = enabled; + } +} + + +status_t +GameProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format) +{ + // insure that we received a format + if (!format) + return B_BAD_VALUE; + + // returning our preferred format + *format = fPreferredFormat; + + // our format is supported + if (type == B_MEDIA_UNKNOWN_TYPE) return B_OK; + + // we only support raw audo + return (type != B_MEDIA_RAW_AUDIO) ? B_MEDIA_BAD_FORMAT : B_OK; +} + + +status_t +GameProducer::FormatProposal(const media_source& output, media_format* format) +{ + // doest the proposed output match our output? + if (output != fOutput.source) return B_MEDIA_BAD_SOURCE; + + // return our preferred format + *format = fPreferredFormat; + + // we will reject the proposal if the format is not audio + media_type requestedType = format->type; + if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) + return B_MEDIA_BAD_FORMAT; + + return B_OK; // raw audio or wildcard type, either is okay by us +} + + +status_t +GameProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name) +{ + // The format has been processed by the consumer at this point. We need + // to insure the format is still acceptable and any wild care are filled in. + + // trying to connect something that isn't our source? + if (what != fOutput.source) return B_MEDIA_BAD_SOURCE; + + // are we already connected? + if (fOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED; + + // the format may not yet be fully specialized (the consumer might have + // passed back some wildcards). Finish specializing it now, and return an + // error if we don't support the requested format. + if (format->type != B_MEDIA_RAW_AUDIO) + return B_MEDIA_BAD_FORMAT; + + if (format->u.raw_audio.format != fPreferredFormat.u.raw_audio.format) + return B_MEDIA_BAD_FORMAT; + + // check the buffer size, which may still be wildcarded + if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) + format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest + + // Now reserve the connection, and return information about it + fOutput.destination = where; + fOutput.format = *format; + *out_source = fOutput.source; + strncpy(out_name, fOutput.name, B_MEDIA_NAME_LENGTH); + return B_OK; +} + + +void +GameProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) +{ + // If something earlier failed, Connect() might still be called, but with a non-zero + // error code. When that happens we simply unreserve the connection and do + // nothing else. + if (error) + { + fOutput.destination = media_destination::null; + fOutput.format = fPreferredFormat; + } + else + { + // Okay, the connection has been confirmed. Record the destination and format + // that we agreed on, and report our connection name again. + fOutput.destination = destination; + fOutput.format = format; + strncpy(io_name, fOutput.name, B_MEDIA_NAME_LENGTH); + + // Now that we're connected, we can determine our downstream latency. + // Do so, then make sure we get our events early enough. + media_node_id id; + FindLatencyFor(fOutput.destination, &fLatency, &id); + + // Use a dry run to see how long it takes me to fill a buffer of data + + // The first step to setup the buffer + bigtime_t start, produceLatency; + int32 frames = int32(fOutput.format.u.raw_audio.buffer_size / fFrameSize); + float* data = new float[frames * 2]; + + // Second, fill the buffer + start = ::system_time(); + for(int32 i = 0; i < frames; i++) + { + data[i*2] = 0.8 * float(i/frames); + data[i*2+1] = 0.8 * float(i/frames); + } + produceLatency = ::system_time(); + + // Thid, calculate the latency + fInternalLatency = produceLatency - start; + SetEventLatency(fLatency + fInternalLatency); + + // Finaily, clean up + delete [] data; + + // reset our buffer duration, etc. to avoid later calculations + bigtime_t duration = bigtime_t(1000000) * frames / bigtime_t(fOutput.format.u.raw_audio.frame_rate); + SetBufferDuration(duration); + + // Set up the buffer group for our connection, as long as nobody handed us a + // buffer group (via SetBufferGroup()) prior to this. + if (!fBufferGroup) + { + size_t size = fOutput.format.u.raw_audio.buffer_size; + int32 count = int32(fLatency / BufferDuration() + 2); + fBufferGroup = new BBufferGroup(size, count); + } + } +} + + +void +GameProducer::Disconnect(const media_source& what, const media_destination& where) +{ + // Make sure that our connection is the one being disconnected + if ((where == fOutput.destination) && (what == fOutput.source)) + { + fOutput.destination = media_destination::null; + fOutput.format = fPreferredFormat; + delete fBufferGroup; + fBufferGroup = NULL; + } +} + + +status_t +GameProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_) +{ + // we don't support any other formats, so we just reject any format changes. + return B_ERROR; +} + + +status_t +GameProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) +{ + // verify that we didn't get bogus arguments before we proceed + if (for_source != fOutput.source) return B_MEDIA_BAD_SOURCE; + + // Are we being passed the buffer group we're already using? + if (newGroup == fBufferGroup) return B_OK; + + // Ahh, someone wants us to use a different buffer group. At this point we delete + // the one we are using and use the specified one instead. If the specified group is + // NULL, we need to recreate one ourselves, and use *that*. Note that if we're + // caching a BBuffer that we requested earlier, we have to Recycle() that buffer + // *before* deleting the buffer group, otherwise we'll deadlock waiting for that + // buffer to be recycled! + delete fBufferGroup; // waits for all buffers to recycle + if (newGroup != NULL) + { + // we were given a valid group; just use that one from now on + fBufferGroup = newGroup; + } + else + { + // we were passed a NULL group pointer; that means we construct + // our own buffer group to use from now on + size_t size = fOutput.format.u.raw_audio.buffer_size; + int32 count = int32(fLatency / BufferDuration() + 2); + fBufferGroup = new BBufferGroup(size, count); + } + + return B_OK; +} + + +status_t +GameProducer::GetLatency(bigtime_t* out_latency) +{ + // report our *total* latency: internal plus downstream plus scheduling + *out_latency = EventLatency() + SchedulingLatency(); + return B_OK; +} + + +void +GameProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) +{ + // If we're late, we need to catch up. Respond in a manner appropriate to our + // current run mode. + if (what == fOutput.source) + { + if (RunMode() == B_RECORDING) + { + // A hardware capture node can't adjust; it simply emits buffers at + // appropriate points. We (partially) simulate this by not adjusting + // our behavior upon receiving late notices -- after all, the hardware + // can't choose to capture "sooner".... + } + else if (RunMode() == B_INCREASE_LATENCY) + { + // We're late, and our run mode dictates that we try to produce buffers + // earlier in order to catch up. This argues that the downstream nodes are + // not properly reporting their latency, but there's not much we can do about + // that at the moment, so we try to start producing buffers earlier to + // compensate. + fInternalLatency += how_much; + SetEventLatency(fLatency + fInternalLatency); + } + else + { + // The other run modes dictate various strategies for sacrificing data quality + // in the interests of timely data delivery. The way *we* do this is to skip + // a buffer, which catches us up in time by one buffer duration. + size_t nSamples = fOutput.format.u.raw_audio.buffer_size / fFrameSize; + fFramesSent += nSamples; + } + } +} + + +void +GameProducer::LatencyChanged(const media_source& source, const media_destination& destination, bigtime_t new_latency, uint32 flags) +{ + // something downstream changed latency, so we need to start producing + // buffers earlier (or later) than we were previously. Make sure that the + // connection that changed is ours, and adjust to the new downstream + // latency if so. + if ((source == fOutput.source) && (destination == fOutput.destination)) + { + fLatency = new_latency; + SetEventLatency(fLatency + fInternalLatency); + } +} + + +status_t +GameProducer::SetPlayRate(int32 numer, int32 denom) +{ + // Play rates are weird. We don't support them + return B_ERROR; +} + + +status_t +GameProducer::HandleMessage(int32 message, const void* data, size_t size) +{ + // We currently do not handle private messages + return B_ERROR; +} + + +void +GameProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag) +{ + // we don't support offline mode (yet...) + return; +} + + +// BMediaEventLooper methods +void +GameProducer::NodeRegistered() +{ + // Start the BMediaEventLooper thread + SetPriority(B_REAL_TIME_PRIORITY); + Run(); + + // set up as much information about our output as we can + fOutput.source.port = ControlPort(); + fOutput.source.id = 0; + fOutput.node = Node(); + ::strcpy(fOutput.name, "GameProducer Output"); +} + + +void +GameProducer::SetRunMode(run_mode mode) +{ + // We don't support offline run mode, so broadcast an error if we're set to + // B_OFFLINE. Unfortunately, we can't actually reject the mode change... + if (B_OFFLINE == mode) + { + ReportError(B_NODE_FAILED_SET_RUN_MODE); + } +} + + +void +GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) +{ +// FPRINTF(stderr, "ToneProducer::HandleEvent\n"); + switch (event->type) + { + case BTimedEventQueue::B_START: + // don't do anything if we're already running + if (RunState() != B_STARTED) + { + // We are going to start sending buffers so setup the needed bookkeeping + fFramesSent = 0; + fStartTime = event->event_time; + media_timed_event firstBufferEvent(fStartTime, BTimedEventQueue::B_HANDLE_BUFFER); + + // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through + // the event queue, like this: + // + // this->HandleEvent(&firstBufferEvent, 0, false); + // + EventQueue()->AddEvent(firstBufferEvent); + } + break; + + case BTimedEventQueue::B_STOP: + // When we handle a stop, we must ensure that downstream consumers don't + // get any more buffers from us. This means we have to flush any pending + // buffer-producing events from the queue. + EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); + break; + + case BTimedEventQueue::B_HANDLE_BUFFER: + { + // make sure we're both started *and* connected before delivering a buffer + if ((RunState() == BMediaEventLooper::B_STARTED) && (fOutput.destination != media_destination::null)) + { + // Get the next buffer of data + BBuffer* buffer = FillNextBuffer(event->event_time); + if (buffer) + { + // send the buffer downstream if and only if output is enabled + status_t err = B_ERROR; + if (fOutputEnabled) err = SendBuffer(buffer, fOutput.destination); + if (err) + { + // we need to recycle the buffer ourselves if output is disabled or + // if the call to SendBuffer() fails + buffer->Recycle(); + } + } + + // track how much media we've delivered so far + size_t nFrames = fOutput.format.u.raw_audio.buffer_size / fFrameSize; + fFramesSent += nFrames; + + // The buffer is on its way; now schedule the next one to go + bigtime_t nextEvent = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); + media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER); + EventQueue()->AddEvent(nextBufferEvent); + } + } + break; + + default: + break; + } +} + + +// -------------------------------- +// GameProducer +BBuffer* +GameProducer::FillNextBuffer(bigtime_t event_time) +{ + // get a buffer from our buffer group + BBuffer* buf = fBufferGroup->RequestBuffer(fOutput.format.u.raw_audio.buffer_size, BufferDuration()); + + // if we fail to get a buffer (for example, if the request times out), we skip this + // buffer and go on to the next, to avoid locking up the control thread + if (!buf) return NULL; + + // we need to discribe the buffer + int64 frames = int64(fOutput.format.u.raw_audio.buffer_size / fFrameSize); + memset(buf->Data(), 0, fOutput.format.u.raw_audio.buffer_size); + + // now fill the buffer with data, continuing where the last buffer left off + fObject->Play(buf->Data(), frames); + + // fill in the buffer header + media_header* hdr = buf->Header(); + hdr->type = B_MEDIA_RAW_AUDIO; + hdr->size_used = fOutput.format.u.raw_audio.buffer_size; + hdr->time_source = TimeSource()->ID(); + + bigtime_t stamp; + if (RunMode() == B_RECORDING) + { + // In B_RECORDING mode, we stamp with the capture time. We're not + // really a hardware capture node, but we simulate it by using the (precalculated) + // time at which this buffer "should" have been created. + stamp = event_time; + } + else + { + // okay, we're in one of the "live" performance run modes. in these modes, we + // stamp the buffer with the time at which the buffer should be rendered to the + // output, not with the capture time. fStartTime is the cached value of the + // first buffer's performance time; we calculate this buffer's performance time as + // an offset from that time, based on the amount of media we've created so far. + // Recalculating every buffer like this avoids accumulation of error. + stamp = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); + } + hdr->start_time = stamp; + + return buf; +} + + diff --git a/src/kits/game/GameProducer.h b/src/kits/game/GameProducer.h new file mode 100644 index 0000000000..fd88d962cb --- /dev/null +++ b/src/kits/game/GameProducer.h @@ -0,0 +1,151 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameProducer.cpp +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: A MediaKit producer node which mixes sound from the GameKit +// and sends them to the audio mixer +//------------------------------------------------------------------------------ + +#ifndef _GAMEPRODUCER_H +#define _GAMEPRODUCER_H + +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- +#include +#include +#include + +// Project Includes ------------------------------------------------------------ + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- +class GameSoundBuffer; + +// GameProducer class ------------------------------------------------------------- +class GameProducer : public BBufferProducer, public BMediaEventLooper +{ +public: + GameProducer(GameSoundBuffer * object, + const gs_audio_format * format); + ~GameProducer(); + + // BMediaNode methods + BMediaAddOn* AddOn(int32* internal_id) const; + + // BBufferProducer methods + status_t FormatSuggestionRequested(media_type type, + int32 quality, + media_format* format); + + status_t FormatProposal(const media_source& output, + media_format* format); + + status_t FormatChangeRequested(const media_source& source, + const media_destination& destination, + media_format* io_format, + int32* _deprecated_); + + status_t GetNextOutput(int32* cookie, + media_output* out_output); + + status_t DisposeOutputCookie(int32 cookie); + + status_t SetBufferGroup(const media_source& for_source, + BBufferGroup* group); + + + status_t GetLatency(bigtime_t* out_latency); + + status_t PrepareToConnect(const media_source& what, + const media_destination& where, + media_format* format, + media_source* out_source, + char* out_name); + + void Connect(status_t error, + const media_source& source, + const media_destination& destination, + const media_format& format, + char* io_name); + + void Disconnect(const media_source& what, + const media_destination& where); + + void LateNoticeReceived(const media_source& what, + bigtime_t how_much, + bigtime_t performance_time); + + void EnableOutput(const media_source & what, + bool enabled, + int32* _deprecated_); + + status_t SetPlayRate(int32 numer, + int32 denom); + + status_t HandleMessage(int32 message, + const void* data, + size_t size); + + void AdditionalBufferRequested(const media_source& source, + media_buffer_id prev_buffer, + bigtime_t prev_time, + const media_seek_tag* prev_tag); + + void LatencyChanged(const media_source& source, + const media_destination& destination, + bigtime_t new_latency, + uint32 flags); + + // BMediaEventLooper methods + void NodeRegistered(); + void SetRunMode(run_mode mode); + void HandleEvent(const media_timed_event* event, + bigtime_t lateness, + bool realTimeEvent = false); + + // GameProducer + status_t StartPlaying(GameSoundBuffer* sound); + status_t StopPlaying(GameSoundBuffer* sound); + bool IsPlaying(GameSoundBuffer* sound) const; + + int32 SoundCount() const; + +private: + BBuffer* FillNextBuffer(bigtime_t event_time); + + BBufferGroup * fBufferGroup; + bigtime_t fLatency, fInternalLatency; + media_output fOutput; + bool fOutputEnabled; + media_format fPreferredFormat; + + bigtime_t fStartTime; + size_t fFrameSize; + int64 fFramesSent; + GameSoundBuffer * fObject; +}; + +#endif diff --git a/src/kits/game/GameSound.cpp b/src/kits/game/GameSound.cpp index 8beb7ba8a5..7e6b93aaeb 100644 --- a/src/kits/game/GameSound.cpp +++ b/src/kits/game/GameSound.cpp @@ -1,65 +1,120 @@ -/* - * Copyright 2002, Marcus Overhagen. All rights reserved. - * Distributed under the terms of the MIT License. - */ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSound.cpp +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Provides much of interfaces with BGameSoundDevice on behalf +// of the rest of it's childern. +//------------------------------------------------------------------------------ +// Standard Includes ----------------------------------------------------------- +#include + +// System Includes ------------------------------------------------------------- + +// Project Includes ------------------------------------------------------------ +#include +#include + +// Local Includes -------------------------------------------------------------- #include +// Local Defines --------------------------------------------------------------- + +// BGameSound class ------------------------------------------------------------ BGameSound::BGameSound(BGameSoundDevice *device) + : fSound(-1) { + fDevice = GetDefaultDevice(); + fInitError = fDevice->InitCheck(); +} + + +BGameSound::BGameSound(const BGameSound &other) + : fSound(-1) +{ + memcpy(&fFormat, &other.fFormat, sizeof(gs_audio_format)); + fDevice = GetDefaultDevice(); + + fInitError = fDevice->InitCheck(); } BGameSound::~BGameSound() { + if (fSound >= 0) + fDevice->ReleaseBuffer(fSound); + + ReleaseDevice(); } status_t BGameSound::InitCheck() const { - return B_ERROR; + return fInitError; } BGameSoundDevice * BGameSound::Device() const { - return NULL; + return fDevice; } gs_id BGameSound::ID() const { - return 0; + return fSound; } const gs_audio_format & BGameSound::Format() const { + return fDevice->Format(fSound); } status_t BGameSound::StartPlaying() { - return B_ERROR; + fDevice->StartPlaying(fSound); + return B_OK; } bool BGameSound::IsPlaying() { - return false; + return fDevice->IsPlaying(fSound); } status_t BGameSound::StopPlaying() { - return B_ERROR; + fDevice->StopPlaying(fSound); + return B_OK; } @@ -67,7 +122,14 @@ status_t BGameSound::SetGain(float gain, bigtime_t duration) { - return B_ERROR; + gs_attribute attribute; + + attribute.attribute = B_GS_GAIN; + attribute.value = gain; + attribute.duration = duration; + attribute.flags = 0; + + return fDevice->SetAttributes(fSound, &attribute, 1); } @@ -75,21 +137,44 @@ status_t BGameSound::SetPan(float pan, bigtime_t duration) { - return B_ERROR; + gs_attribute attribute; + + attribute.attribute = B_GS_PAN; + attribute.value = pan; + attribute.duration = duration; + attribute.flags = 0; + + return fDevice->SetAttributes(fSound, &attribute, 1); } float BGameSound::Gain() { - return 0.0f; + gs_attribute attribute; + + attribute.attribute = B_GS_GAIN; + attribute.flags = 0; + + if (fDevice->GetAttributes(fSound, &attribute, 1) != B_OK) + return 0.0; + + return attribute.value; } float BGameSound::Pan() { - return 0.0f; + gs_attribute attribute; + + attribute.attribute = B_GS_PAN; + attribute.flags = 0; + + if (fDevice->GetAttributes(fSound, &attribute, 1) != B_OK) + return 0.0; + + return attribute.value; } @@ -97,7 +182,7 @@ status_t BGameSound::SetAttributes(gs_attribute *inAttributes, size_t inAttributeCount) { - return B_ERROR; + return fDevice->SetAttributes(fSound, inAttributes, inAttributeCount); } @@ -105,7 +190,7 @@ status_t BGameSound::GetAttributes(gs_attribute *outAttributes, size_t inAttributeCount) { - return B_ERROR; + return fDevice->GetAttributes(fSound, outAttributes, inAttributeCount); } @@ -116,7 +201,7 @@ BGameSound::Perform(int32 selector, return B_ERROR; } - +/* void * BGameSound::operator new(size_t size) { @@ -128,7 +213,7 @@ void BGameSound::operator delete(void *ptr) { } - +*/ status_t BGameSound::SetMemoryPoolSize(size_t in_poolSize) @@ -147,34 +232,39 @@ BGameSound::LockMemoryPool(bool in_lockInCore) int32 BGameSound::SetMaxSoundCount(int32 in_maxCount) { - return 0; + return in_maxCount; } status_t BGameSound::SetInitError(status_t in_initError) { - return B_ERROR; + fInitError = in_initError; + return B_OK; } status_t BGameSound::Init(gs_id handle) { - return B_ERROR; + if (fSound < 0) fSound = handle; + + return B_OK; } - -BGameSound::BGameSound(const BGameSound &other) -{ -} - - +/* BGameSound & BGameSound::operator=(const BGameSound &other) { + if (fSound) + fDevice->ReleaseBuffer(fSound); + + fSound = other.fSound; + fInitError = other.fInitError; + + return this; } - +*/ /* unimplemented for protection of the user: * diff --git a/src/kits/game/GameSoundBuffer.cpp b/src/kits/game/GameSoundBuffer.cpp new file mode 100644 index 0000000000..721bd4fa1c --- /dev/null +++ b/src/kits/game/GameSoundBuffer.cpp @@ -0,0 +1,541 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSoundBuffer.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Interface to a single sound, managed by the GameSoundDevice. +//------------------------------------------------------------------------------ + +// Standard Includes ----------------------------------------------------------- +#include +#include +#include + +// System Includes ------------------------------------------------------------- +#include +#include +#include +#include + +// Project Includes ------------------------------------------------------------ +#include +#include +#include + +// Local Includes -------------------------------------------------------------- +#include +#include + +// Local Defines --------------------------------------------------------------- + +// Sound Buffer Utility functions ---------------------------------------- +inline void ApplyMod(uint8 * data, uint8 * buffer, int64 index, float gain, float * pan) +{ + data[index*2] += uint8(float(buffer[index*2]) * gain * pan[0]); + data[index*2+1] += uint8(float(buffer[index*2+1]) * gain * pan[1]); +} + + +inline void ApplyMod(int16 * data, int16 * buffer, int32 index, float gain, float * pan) +{ + data[index*2] = int16(float(buffer[index*2]) * gain * pan[0]); + data[index*2+1] = int16(float(buffer[index*2+1]) * gain * pan[1]); +} + + +inline void ApplyMod(int32 * data, int32 * buffer, int32 index, float gain, float * pan) +{ + data[index*2] += int32(float(buffer[index*2]) * gain * pan[0]); + data[index*2+1] += int32(float(buffer[index*2+1]) * gain * pan[1]); +} + + +inline void ApplyMod(float * data, float * buffer, int32 index, float gain, float * pan) +{ + data[index*2] += buffer[index*2] * gain * pan[0]; + data[index*2+1] += buffer[index*2+1] * gain * pan[1]; +} + +// GameSoundBuffer ------------------------------------------------------- +GameSoundBuffer::GameSoundBuffer(const gs_audio_format * format) + : fLooping(false), + fIsConnected(false), + fIsPlaying(false), + fGain(1.0), + fPan(0.0), + fPanLeft(1.0), + fPanRight(1.0), + fGainRamp(NULL), + fPanRamp(NULL) +{ + fConnection = new Connection; + fNode = new GameProducer(this, format); + + fFrameSize = get_sample_size(format->format) * format->channel_count; + + memcpy(&fFormat, format, sizeof(gs_audio_format)); +} + + +GameSoundBuffer::~GameSoundBuffer() +{ + BMediaRoster* r = BMediaRoster::Roster(); + + if (fIsPlaying) r->StopNode(fConnection->producer, 0, true); + + if (fIsConnected) + { + // Ordinarily we'd stop *all* of the nodes in the chain at this point. However, + // one of the nodes is the System Mixer, and stopping the Mixer is a Bad Idea (tm). + // So, we just disconnect from it, and release our references to the nodes that + // we're using. We *are* supposed to do that even for global nodes like the Mixer. + r->Disconnect(fConnection->producer.node, fConnection->source, + fConnection->consumer.node, fConnection->destination); + + r->ReleaseNode(fConnection->producer); + r->ReleaseNode(fConnection->consumer); + } + + delete fGainRamp; + delete fPanRamp; + + delete fConnection; + delete fNode; +} + + +const gs_audio_format & +GameSoundBuffer::Format() const +{ + return fFormat; +} + + +bool +GameSoundBuffer::IsLooping() const +{ + return fLooping; +} + + +void +GameSoundBuffer::SetLooping(bool looping) +{ + fLooping = looping; +} + + +float +GameSoundBuffer::Gain() const +{ + return fGain; +} + + +status_t +GameSoundBuffer::SetGain(float gain, bigtime_t duration) +{ + if (gain < 0.0 || gain > 1.0) + return B_BAD_VALUE; + + if (fGainRamp) delete fGainRamp; + + if (duration > 100000) + fGainRamp = InitRamp(&fGain, gain, fFormat.frame_rate, duration); + else + fGain = gain; + + return B_OK; +} + + +float +GameSoundBuffer::Pan() const +{ + return fPan; +} + + +status_t +GameSoundBuffer::SetPan(float pan, bigtime_t duration) +{ + if (pan < -1.0 || pan > 1.0) + return B_BAD_VALUE; + + if (fPanRamp) delete fPanRamp; + + if (duration < 100000) + { + fPan = pan; + + if (fPan < 0.0) + { + fPanLeft = 1.0; + fPanRight = 1.0 + fPan; + } + else + { + fPanRight = 1.0; + fPanLeft = 1.0 - fPan; + } + } + else fPanRamp = InitRamp(&fPan, pan, fFormat.frame_rate, duration); + + return B_OK; +} + + +status_t +GameSoundBuffer::GetAttributes(gs_attribute * attributes, + size_t attributeCount) +{ + for(size_t i = 0; i < attributeCount; i++) + { + switch(attributes[i].attribute) + { + case B_GS_GAIN: + attributes[i].value = fGain; + if (fGainRamp) attributes[i].duration = fGainRamp->duration; + break; + + case B_GS_PAN: + attributes[i].value = fPan; + if (fPanRamp) attributes[i].duration = fGainRamp->duration; + + case B_GS_LOOPING: + attributes[i].value = (fLooping) ? -1.0 : 0.0; + attributes[i].duration = bigtime_t(0); + + default: + attributes[i].value = 0.0; + attributes[i].duration = bigtime_t(0); + } + } + + return B_OK; +} + + +status_t +GameSoundBuffer::SetAttributes(gs_attribute * attributes, + size_t attributeCount) +{ + status_t error = B_OK; + + for(size_t i = 0; i < attributeCount; i++) + { + switch(attributes[i].attribute) + { + case B_GS_GAIN: + error = SetGain(attributes[i].value, attributes[i].duration); + break; + + case B_GS_PAN: + error = SetPan(attributes[i].value, attributes[i].duration); + break; + + case B_GS_LOOPING: + fLooping = bool(attributes[i].value); + } + } + + return error; +} + + +void +GameSoundBuffer::Play(void * data, int64 frames) +{ + float * pan = new float[1]; + pan[0] = fPanRight; + pan[1] = fPanLeft; + + char * buffer = new char[fFrameSize * frames]; + + FillBuffer(buffer, frames); + + switch(fFormat.format) + { + case gs_audio_format::B_GS_U8: + { + for(int64 i = 0; i < frames; i++) + { + ApplyMod((uint8*)data, (uint8*)buffer, i, fGain, pan); + UpdateMods(); + } + + break; + } + + case gs_audio_format::B_GS_S16: + { + for(int64 i = 0; i < frames; i++) + { + ApplyMod((int16*)data, (int16*)buffer, i, fGain, pan); + UpdateMods(); + } + + break; + } + + case gs_audio_format::B_GS_S32: + { + for(int64 i = 0; i < frames; i++) + { + ApplyMod((int32*)data, (int32*)buffer, i, fGain, pan); + UpdateMods(); + } + + break; + } + + case gs_audio_format::B_GS_F: + { + for(int64 i = 0; i < frames; i++) + { + ApplyMod((float*)data, (float*)buffer, i, fGain, pan); + UpdateMods(); + } + + break; + } + } + + delete [] buffer; + delete [] pan; +} + + +void +GameSoundBuffer::UpdateMods() +{ + // adjust the gain if needed + if (fGainRamp) + { + if (ChangeRamp(fGainRamp)) + { + delete fGainRamp; + fGainRamp = NULL; + } + } + + // adjust the ramp if needed + if (fPanRamp) + { + if (ChangeRamp(fPanRamp)) + { + delete fPanRamp; + fPanRamp = NULL; + } + else + { + if (fPan < 0.0) + { + fPanLeft = 1.0; + fPanRight = 1.0 + fPan; + } + else + { + fPanRight = 1.0; + fPanLeft = 1.0 - fPan; + } + } + } +} + + +void +GameSoundBuffer::Reset() +{ + fGain = 1.0; + delete fGainRamp; + fGainRamp = NULL; + + fPan = 0.0; + fPanLeft = 1.0; + fPanRight = 1.0; + delete fPanRamp; + fPanRamp = NULL; + + fLooping = false; +} + + +status_t +GameSoundBuffer::Connect(media_node * consumer) +{ + BMediaRoster* r = BMediaRoster::Roster(); + status_t err; + + err = r->RegisterNode(fNode); + if (err != B_OK) return err; + + // make sure the Media Roster knows that we're using the node + r->GetNodeFor(fNode->Node().node, &fConnection->producer); + + // connect to the mixer + fConnection->consumer = *consumer; + if (err != B_OK) return err; + + // set the producer's time source to be the "default" time source, which + // the Mixer uses too. + r->GetTimeSource(&fConnection->timeSource); + r->SetTimeSourceFor(fConnection->producer.node, fConnection->timeSource.node); + + // got the nodes; now we find the endpoints of the connection + media_input mixerInput; + media_output soundOutput; + int32 count = 1; + err = r->GetFreeOutputsFor(fConnection->producer, &soundOutput, 1, &count); + if (err != B_OK) return err; + count = 1; + err = r->GetFreeInputsFor(fConnection->consumer, &mixerInput, 1, &count); + if (err != B_OK) return err; + + // got the endpoints; now we connect it! + media_format format; + format.type = B_MEDIA_RAW_AUDIO; + format.u.raw_audio = media_raw_audio_format::wildcard; + err = r->Connect(soundOutput.source, mixerInput.destination, &format, &soundOutput, &mixerInput); + if (err != B_OK) return err; + + // the inputs and outputs might have been reassigned during the + // nodes' negotiation of the Connect(). That's why we wait until + // after Connect() finishes to save their contents. + fConnection->format = format; + fConnection->source = soundOutput.source; + fConnection->destination = mixerInput.destination; + + fIsConnected = true; + return B_OK; +} + + +status_t +GameSoundBuffer::StartPlaying() +{ + if (fIsPlaying) return EALREADY; + + BMediaRoster* r = BMediaRoster::Roster(); + BTimeSource* ts = r->MakeTimeSourceFor(fConnection->producer); + + // make sure we give the producer enough time to run buffers through + // the node chain, otherwise it'll start up already late + bigtime_t latency = 0; + r->GetLatencyFor(fConnection->producer, &latency); + r->StartNode(fConnection->producer, ts->Now() + latency); + ts->Release(); + + fIsPlaying = true; + + return B_OK; +} + + +status_t +GameSoundBuffer::StopPlaying() +{ + if (!fIsPlaying) return EALREADY; + + BMediaRoster* r = BMediaRoster::Roster(); + r->StopNode(fConnection->producer, 0, true); // synchronous stop + + Reset(); + fIsPlaying = false; + + return B_OK; +} + + +bool +GameSoundBuffer::IsPlaying() +{ + return fIsPlaying; +} + + +// SimpleSoundBuffer ------------------------------------------------------ +SimpleSoundBuffer::SimpleSoundBuffer(const gs_audio_format * format, + const void * data, + int64 frames) + : GameSoundBuffer(format), + fPosition(0) + +{ + fBuffer = new char[frames * fFrameSize]; + fBufferSize = frames * fFrameSize; +} + + +SimpleSoundBuffer::~SimpleSoundBuffer() +{ + delete [] fBuffer; +} + + +void +SimpleSoundBuffer::Reset() +{ + GameSoundBuffer::Reset(); + fPosition = 0; +} + + +void +SimpleSoundBuffer::FillBuffer(void * data, int64 frames) +{ + char * buffer = (char*)data; + size_t bytes = fFrameSize * frames; + + if (fPosition + bytes >= fBufferSize) + { + size_t remainder = fBufferSize - fPosition; + memcpy(buffer, &fBuffer[fPosition], remainder); + + if (fLooping) memcpy(&buffer[remainder], fBuffer, bytes - remainder); + } + else memcpy(buffer, &fBuffer[fPosition], bytes); +} + + +// StreamingSoundBuffer ------------------------------------------------------ +StreamingSoundBuffer::StreamingSoundBuffer(const gs_audio_format * format, + const void * streamHook) + : GameSoundBuffer(format) +{ + fStreamHook = (void*)streamHook; +} + + +StreamingSoundBuffer::~StreamingSoundBuffer() +{ +} + + +void +StreamingSoundBuffer::FillBuffer(void * buffer, + int64 frames) +{ + BStreamingGameSound* object = (BStreamingGameSound*)fStreamHook; + + size_t bytes = fFrameSize * frames; + object->FillBuffer(buffer, bytes); +} diff --git a/src/kits/game/GameSoundBuffer.h b/src/kits/game/GameSoundBuffer.h new file mode 100644 index 0000000000..3d99120bc9 --- /dev/null +++ b/src/kits/game/GameSoundBuffer.h @@ -0,0 +1,149 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSoundBuffer.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Interface to a single sound, managed by the GameSoundDevice. +//------------------------------------------------------------------------------ + +#ifndef _GAMESOUNDBUFFER_H +#define _GAMESOUNDBUFFER_H + +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- +#include +#include +#include + +// Project Includes ------------------------------------------------------------ + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- +const int32 kPausedAttribute = B_GS_FIRST_PRIVATE_ATTRIBUTE; + +class GameProducer; +struct _gs_ramp; +struct Connection +{ + media_node producer, consumer; + media_source source; + media_destination destination; + media_format format; + media_node timeSource; +}; + +// GameSoundBuffer ------------------------------------------------------------- +class GameSoundBuffer +{ +public: + + GameSoundBuffer(const gs_audio_format* format); + virtual ~GameSoundBuffer(); + + virtual status_t Connect(media_node * consumer); + status_t StartPlaying(); + status_t StopPlaying(); + bool IsPlaying(); + + void Play(void * data, int64 frames); + void UpdateMods(); + virtual void Reset(); + + virtual void * Data() { return NULL; } + const gs_audio_format & Format() const; + + bool IsLooping() const; + void SetLooping(bool loop); + float Gain() const; + status_t SetGain(float gain, bigtime_t duration); + float Pan() const; + status_t SetPan(float pan, bigtime_t duration); + + virtual status_t GetAttributes(gs_attribute * attributes, + size_t attributeCount); + virtual status_t SetAttributes(gs_attribute * attributes, + size_t attributeCount); +protected: + + virtual void FillBuffer(void * data, int64 frames) = 0; + + gs_audio_format fFormat; + bool fLooping; + + size_t fFrameSize; + +private: + + Connection * fConnection; + GameProducer * fNode; + bool fIsConnected; + bool fIsPlaying; + + float fGain; + float fPan, fPanLeft, fPanRight; + _gs_ramp* fGainRamp; + _gs_ramp* fPanRamp; +}; + + +class SimpleSoundBuffer : public GameSoundBuffer +{ +public: + SimpleSoundBuffer(const gs_audio_format* format, + const void * data, + int64 frames = 0); + virtual ~SimpleSoundBuffer(); + + virtual void * Data() { return fBuffer; } + virtual void Reset(); + +protected: + + virtual void FillBuffer(void * data, int64 frames); + +private: + char * fBuffer; + size_t fBufferSize; + size_t fPosition; +}; + +class StreamingSoundBuffer : public GameSoundBuffer +{ +public: + StreamingSoundBuffer(const gs_audio_format * format, + const void * streamHook); + virtual ~StreamingSoundBuffer(); + +protected: + + virtual void FillBuffer(void * data, int64 frames); + +private: + + void * fStreamHook; +}; + +#endif + diff --git a/src/kits/game/GameSoundDevice.cpp b/src/kits/game/GameSoundDevice.cpp new file mode 100644 index 0000000000..6bc10dbda6 --- /dev/null +++ b/src/kits/game/GameSoundDevice.cpp @@ -0,0 +1,380 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: BGameSoundDevice.cpp +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Manages the game producer. The class may change with out +// notice and was only inteneded for use by the GameKit at +// this time. Use at your own risk. +//------------------------------------------------------------------------------ + +// Standard Includes ----------------------------------------------------------- +#include +#include + +// System Includes ------------------------------------------------------------- +#include +#include +#include +#include +#include + +// Project Includes ------------------------------------------------------------ +#include +#include +#include + +// Local Includes -------------------------------------------------------------- +#include + +// Local Defines --------------------------------------------------------------- + +// BGameSoundDevice definitions ------------------------------------ +const int32 kInitSoundCount = 32; +const int32 kGrowth = 16; + +static int32 deviceCount = 0; +static BGameSoundDevice* theDevice = NULL; + +BGameSoundDevice * GetDefaultDevice() +{ + if (!theDevice) + theDevice = new BGameSoundDevice(); + + deviceCount++; + return theDevice; +} + +void ReleaseDevice() +{ + deviceCount--; + + if (deviceCount <= 0) + { + delete theDevice; + theDevice = NULL; + } +} + +// BGameSoundDevice ------------------------------------------------------- +BGameSoundDevice::BGameSoundDevice() + : fIsConnected(false), + fSoundCount(kInitSoundCount) +{ + fConnection = new Connection; + memset(&fFormat, 0, sizeof(gs_audio_format)); + + fInitError = Connect(); + + fSounds = new GameSoundBuffer*[kInitSoundCount]; + for(int32 i = 0; i < kInitSoundCount; i++) + fSounds[i] = NULL; +} + + +BGameSoundDevice::~BGameSoundDevice() +{ + BMediaRoster* r = BMediaRoster::Roster(); + + // We need to stop all the sounds before we stop the mixer + for(int32 i = 0; i < fSoundCount; i++) + delete fSounds[i]; + + if (fIsConnected) + { + // stop the nodes if they are running + r->StopNode(fConnection->producer, 0, true); // synchronous stop + + // Ordinarily we'd stop *all* of the nodes in the chain at this point. However, + // one of the nodes is the System Mixer, and stopping the Mixer is a Bad Idea (tm). + // So, we just disconnect from it, and release our references to the nodes that + // we're using. We *are* supposed to do that even for global nodes like the Mixer. + r->Disconnect(fConnection->producer.node, fConnection->source, + fConnection->consumer.node, fConnection->destination); + + r->ReleaseNode(fConnection->producer); + r->ReleaseNode(fConnection->consumer); + } + + delete [] fSounds; + delete fConnection; +} + + +status_t +BGameSoundDevice::InitCheck() const +{ + return fInitError; +} + + +const gs_audio_format & +BGameSoundDevice::Format() const +{ + return fFormat; +} + + +const gs_audio_format & +BGameSoundDevice::Format(gs_id sound) const +{ + return fSounds[sound-1]->Format(); +} + + +void +BGameSoundDevice::SetInitError(status_t error) +{ + fInitError = error; +} + + +status_t +BGameSoundDevice::CreateBuffer(gs_id * sound, + const gs_audio_format * format, + const void * data, + int64 frames) +{ + if (frames <= 0 || !sound) return B_BAD_VALUE; + + status_t err = B_MEDIA_TOO_MANY_BUFFERS; + int32 position = AllocateSound(); + + if (position >= 0) + { + fSounds[position] = new SimpleSoundBuffer(format, data, frames); + err = fSounds[position]->Connect(&fConnection->producer); + } + + *sound = gs_id(position+1); + return err; +} + + +status_t +BGameSoundDevice::CreateBuffer(gs_id * sound, + const void * object, + const gs_audio_format * format) +{ + if (!object || !sound) return B_BAD_VALUE; + + status_t err = B_MEDIA_TOO_MANY_BUFFERS; + int32 position = AllocateSound(); + + if (position >= 0) + { + fSounds[position] = new StreamingSoundBuffer(format, object); + err = fSounds[position]->Connect(&fConnection->producer); + } + + *sound = gs_id(position+1); + return err; +} + + +void +BGameSoundDevice::ReleaseBuffer(gs_id sound) +{ + delete fSounds[sound-1]; + fSounds[sound-1] = NULL; +} + + +status_t +BGameSoundDevice::Buffer(gs_id sound, + gs_audio_format * format, + void * data) +{ + if (!format) return B_BAD_VALUE; + + memcpy(format, &fSounds[sound-1]->Format(), sizeof(gs_audio_format)); + + if (fSounds[sound-1]->Data()) + { + data = malloc(format->buffer_size); + memcpy(data, fSounds[sound-1]->Data(), format->buffer_size); + } + else data = NULL; + + return B_OK; +} + +status_t +BGameSoundDevice::StartPlaying(gs_id sound) +{ + status_t error = EALREADY; + + if (!fSounds[sound-1]->IsPlaying()) + { + // tell the producer to start playing the sound + error = fSounds[sound-1]->StartPlaying(); + } + else fSounds[sound-1]->Reset(); + + return error; +} + + +status_t +BGameSoundDevice::StopPlaying(gs_id sound) +{ + status_t error = EALREADY; + + if (fSounds[sound-1]->IsPlaying()) + { + // Tell the producer to stop play this sound + fSounds[sound-1]->Reset(); + error = fSounds[sound-1]->StopPlaying(); + } + + return error; +} + + +bool +BGameSoundDevice::IsPlaying(gs_id sound) +{ + return fSounds[sound-1]->IsPlaying(); +} + + +status_t +BGameSoundDevice::GetAttributes(gs_id sound, + gs_attribute * attributes, + size_t attributeCount) +{ + if (!fSounds[sound-1]) return B_ERROR; + + return fSounds[sound-1]->GetAttributes(attributes, attributeCount); +} + + +status_t +BGameSoundDevice::SetAttributes(gs_id sound, + gs_attribute * attributes, + size_t attributeCount) +{ + if (!fSounds[sound-1]) return B_ERROR; + + return fSounds[sound-1]->SetAttributes(attributes, attributeCount); +} + + +status_t +BGameSoundDevice::Connect() +{ + BMediaRoster* r = BMediaRoster::Roster(); + status_t err; + + // create your own audio mixer + dormant_node_info mixer_dormant_info; + int32 mixer_count = 1; // for now, we only care about the first we find. + err = r->GetDormantNodes(&mixer_dormant_info, &mixer_count, 0, 0, 0, B_SYSTEM_MIXER, 0); + if (err != B_OK) return err; + + //fMixer = new media_node; + err = r->InstantiateDormantNode(mixer_dormant_info, &fConnection->producer); + if (err != B_OK) return err; + + // retieve the system's audio mixer + err = r->GetAudioMixer(&fConnection->consumer); + if (err != B_OK) return err; + + int32 count = 1; + media_input mixerInput; + err = r->GetFreeInputsFor(fConnection->consumer, &mixerInput, 1, &count); + if (err != B_OK) return err; + + count = 1; + media_output mixerOutput; + err = r->GetFreeOutputsFor(fConnection->producer, &mixerOutput, 1, &count); + if (err != B_OK) return err; + + media_format format(mixerOutput.format); + err = r->Connect(mixerOutput.source, mixerInput.destination, &format, &mixerOutput, &mixerInput); + if (err != B_OK) return err; + + // set the producer's time source to be the "default" time source, which + // the Mixer uses too. + r->GetTimeSource(&fConnection->timeSource); + r->SetTimeSourceFor(fConnection->producer.node, fConnection->timeSource.node); + + // Start our mixer's time source if need be. Chances are, it won't need to be, + // but if we forget to do this, our mixer might not do anything at all. + BTimeSource* mixerTimeSource = r->MakeTimeSourceFor(fConnection->producer); + if (! mixerTimeSource) return B_ERROR; + + if (!mixerTimeSource->IsRunning()) + { + status_t err = r->StartNode(mixerTimeSource->Node(), BTimeSource::RealTime()); + if (err != B_OK) + { + mixerTimeSource->Release(); + return err; + } + } + + // Start up our mixer + bigtime_t tpNow = mixerTimeSource->Now(); + err = r->StartNode(fConnection->producer, tpNow + 10000); + mixerTimeSource->Release(); + if (err != B_OK) return err; + + // the inputs and outputs might have been reassigned during the + // nodes' negotiation of the Connect(). That's why we wait until + // after Connect() finishes to save their contents. + fConnection->format = format; + fConnection->source = mixerOutput.source; + fConnection->destination = mixerInput.destination; + + // Set an appropriate run mode for the producer + r->SetRunModeNode(fConnection->producer, BMediaNode::B_INCREASE_LATENCY); + + media_to_gs_format(&fFormat, &format.u.raw_audio); + fIsConnected = true; + return B_OK; +} + + +int32 +BGameSoundDevice::AllocateSound() +{ + for(int32 i = 0; i < fSoundCount; i++) + if (!fSounds[i]) + return i; + + // we need to allocate new space for the sound + GameSoundBuffer ** sounds = new GameSoundBuffer*[fSoundCount + kGrowth]; + for(int32 i = 0; i < fSoundCount; i++) + sounds[i] = fSounds[i]; + + for(int32 i = fSoundCount; i < fSoundCount + kGrowth; i++) + sounds[i] = NULL; + + // replace the old list + delete [] fSounds; + fSounds = sounds; + fSoundCount += kGrowth; + + return fSoundCount - kGrowth; +} + + \ No newline at end of file diff --git a/src/kits/game/GameSoundDevice.h b/src/kits/game/GameSoundDevice.h new file mode 100644 index 0000000000..b768be8975 --- /dev/null +++ b/src/kits/game/GameSoundDevice.h @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSoundDevice.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: Manages the game producer. The class may change with out +// notice and was only inteneded for use by the GameKit at +// this time. Use at your own risk. +//------------------------------------------------------------------------------ + +#ifndef _GAMESOUNDDEVICE_H +#define _GAMESOUNDDEVICE_H + +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- +#include + +// Project Includes ------------------------------------------------------------ + +// Local Includes -------------------------------------------------------------- + +// Local Defines --------------------------------------------------------------- + +// Globals --------------------------------------------------------------------- +class BMediaNode; +class GameSoundBuffer; +struct Connection; + +// BGameSoundDevice ------------------------------------------------------------ +class BGameSoundDevice +{ +public: + BGameSoundDevice(); + virtual ~BGameSoundDevice(); + + status_t InitCheck() const; + virtual const gs_audio_format & Format() const; + virtual const gs_audio_format & Format(gs_id sound) const; + + virtual status_t CreateBuffer(gs_id * sound, + const gs_audio_format * format, + const void * data, + int64 frames); + virtual status_t CreateBuffer(gs_id * sound, + const void * object, + const gs_audio_format * format); + virtual void ReleaseBuffer(gs_id sound); + + virtual status_t Buffer(gs_id sound, + gs_audio_format * format, + void * data); + + virtual bool IsPlaying(gs_id sound); + virtual status_t StartPlaying(gs_id sound); + virtual status_t StopPlaying(gs_id sound); + + virtual status_t GetAttributes(gs_id sound, + gs_attribute * attributes, + size_t attributeCount); + virtual status_t SetAttributes(gs_id sound, + gs_attribute * attributes, + size_t attributeCount); + +protected: + void SetInitError(status_t error); + + gs_audio_format fFormat; +private: + status_t Connect(); + int32 AllocateSound(); + + status_t fInitError; + + Connection * fConnection; + bool fIsConnected; + + int32 fSoundCount; + GameSoundBuffer ** fSounds; +}; + +BGameSoundDevice* GetDefaultDevice(); +void ReleaseDevice(); + +#endif diff --git a/src/kits/game/Jamfile b/src/kits/game/Jamfile index 1b3ae7ca3d..351a73fe23 100644 --- a/src/kits/game/Jamfile +++ b/src/kits/game/Jamfile @@ -3,6 +3,7 @@ SubDir OBOS_TOP src kits game ; UsePublicHeaders game ; SharedLibrary game : + # Public Game Kit DirectWindow.cpp WindowScreen.cpp GameSound.cpp @@ -10,9 +11,14 @@ SharedLibrary game : FileGameSound.cpp PushGameSound.cpp StreamingGameSound.cpp + + # Internal Functionality + GameProducer.cpp + GameSoundBuffer.cpp + GameSoundDevice.cpp + GSUtility.cpp ; LinkSharedOSLibs libgame.so : - be + be media ; - diff --git a/src/kits/game/PushGameSound.cpp b/src/kits/game/PushGameSound.cpp index 0b4f3cd820..61e5f8814e 100644 --- a/src/kits/game/PushGameSound.cpp +++ b/src/kits/game/PushGameSound.cpp @@ -1,40 +1,108 @@ -/* - * Copyright 2002, Marcus Overhagen. All rights reserved. - * Distributed under the terms of the MIT License. - */ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: GameSound.cpp +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BPushGameSound class +//------------------------------------------------------------------------------ +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- +#include + +// Project Includes ------------------------------------------------------------ +#include + +// Local Includes -------------------------------------------------------------- #include -static const size_t default_in_buffer_frame_count = 4096; // XXX change this -static const gs_audio_format default_format = {}; // XXX change this -static const size_t default_in_buffer_count = 2; // XXX change this +// Local Defines --------------------------------------------------------------- +// BPushGameSound -------------------------------------------------------------- BPushGameSound::BPushGameSound(size_t inBufferFrameCount, const gs_audio_format *format, size_t inBufferCount, BGameSoundDevice *device) - : BStreamingGameSound(default_in_buffer_frame_count, &default_format, default_in_buffer_count, device) + : BStreamingGameSound(inBufferFrameCount, format, inBufferCount, device) { + if (InitCheck() == B_OK) + { + status_t error = SetParameters(inBufferFrameCount, format, inBufferCount); + if (error != B_OK) + fPageLocked = new BList; + else + SetInitError(error); + } } +BPushGameSound::BPushGameSound(BGameSoundDevice * device) + : BStreamingGameSound(device), + fLockPos(0), + fPlayPos(0), + fBuffer(NULL), + fPageSize(0), + fPageCount(0), + fBufferSize(0) +{ + fPageLocked = new BList; +} + BPushGameSound::~BPushGameSound() { + delete [] fBuffer; + delete fPageLocked; } BPushGameSound::lock_status BPushGameSound::LockNextPage(void **out_pagePtr, size_t *out_pageSize) -{ - return lock_failed; +{ + // the user can not lock every page + if (fPageLocked->CountItems() > fPageCount - 3) return lock_failed; + + // the user cann't lock a page being played + if (fLockPos < fPlayPos && fLockPos + fPageSize > fPlayPos) return lock_failed; + + // lock the page + char * lockPage = &fBuffer[fLockPos]; + fPageLocked->AddItem(lockPage); + + // move the locker to the next page + fLockPos += fPageSize; + if (fLockPos > fBufferSize) fLockPos = 0; + + *out_pagePtr = lockPage; + *out_pageSize = fPageSize; + + return lock_ok; } status_t BPushGameSound::UnlockPage(void *in_pagePtr) { - return B_ERROR; + return (fPageLocked->RemoveItem(in_pagePtr)) ? B_OK : B_ERROR; } @@ -42,28 +110,34 @@ BPushGameSound::lock_status BPushGameSound::LockForCyclic(void **out_basePtr, size_t *out_size) { - return lock_failed; + *out_basePtr = fBuffer; + *out_size = fBufferSize; + return lock_ok; } status_t BPushGameSound::UnlockCyclic() { - return B_ERROR; + return B_OK; } size_t BPushGameSound::CurrentPosition() { - return 0; + return fPlayPos; } BGameSound * BPushGameSound::Clone() const { - return NULL; + gs_audio_format format = Format(); + size_t frameSize = get_sample_size(format.format) * format.channel_count; + size_t bufferFrameCount = fPageSize / frameSize; + + return new BPushGameSound(bufferFrameCount, &format, fPageCount, Device()); } @@ -80,7 +154,18 @@ BPushGameSound::SetParameters(size_t inBufferFrameCount, const gs_audio_format *format, size_t inBufferCount) { - return B_ERROR; + status_t error = BStreamingGameSound::SetParameters(inBufferFrameCount, format, inBufferCount); + if (error != B_OK) return error; + + size_t frameSize = get_sample_size(format->format) * format->channel_count; + + fPageCount = inBufferCount; + fPageSize = frameSize * inBufferFrameCount; + fBufferSize = fPageSize * fPageCount; + + fBuffer = new char[fBufferSize]; + + return B_OK; } @@ -95,10 +180,70 @@ BPushGameSound::SetStreamHook(void (*hook)(void * inCookie, void * inBuffer, siz void BPushGameSound::FillBuffer(void *inBuffer, size_t inByteCount) -{ +{ + size_t bytes = inByteCount; + + if (BytesReady(&bytes)) + { + if (fPlayPos + bytes > fBufferSize) + { + size_t remainder = fPlayPos + bytes - fBufferSize; + char * buffer = (char*)inBuffer; + + // fill the buffer with the samples left at the end of our buffer + memcpy(buffer, &fBuffer[fPlayPos], remainder); + fPlayPos = 0; + + // fill the remainder of the buffer by looping to the start + // of the buffer if it isn't locked + bytes -= remainder; + if (BytesReady(&bytes)) + { + memcpy(&buffer[remainder], fBuffer, bytes); + fPlayPos += bytes; + } + } + else + { + memcpy(inBuffer, &fBuffer[fPlayPos], bytes); + fPlayPos += bytes; + } + + BStreamingGameSound::FillBuffer(inBuffer, inByteCount); + } } +bool +BPushGameSound::BytesReady(size_t * bytes) +{ + if (fPageLocked->CountItems() > 0) + { + size_t start = fPlayPos; + size_t ready = fPlayPos; + int32 page = int32(start / fPageSize); + + // return if there is nothing to do + if (fPageLocked->HasItem(&fBuffer[page * fPageSize])) return false; + + while (ready < *bytes) + { + ready += fPageSize; + page = int32(ready / fPageSize); + + if (fPageLocked->HasItem(&fBuffer[page * fPageSize])) + { + // we have found a locked page + *bytes = ready - start - (ready - page * fPageSize); + return true; + } + } + } + + // all of the bytes are ready + return true; +} + /* unimplemented for protection of the user: * * BPushGameSound::BPushGameSound() diff --git a/src/kits/game/SimpleGameSound.cpp b/src/kits/game/SimpleGameSound.cpp index 952fc4ff15..cf3e0c942b 100644 --- a/src/kits/game/SimpleGameSound.cpp +++ b/src/kits/game/SimpleGameSound.cpp @@ -1,21 +1,70 @@ -/* - * Copyright 2002, Marcus Overhagen. All rights reserved. - * Distributed under the terms of the MIT License. - */ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: SimpleGameSound.h +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BSimpleGameSound is a class for sound effects that are +// short, and consists of non-changing samples in memory. +//------------------------------------------------------------------------------ +// Standard Includes ----------------------------------------------------------- +#include +#include + +// System Includes ------------------------------------------------------------- +#include +#include +#include + +// Project Includes ------------------------------------------------------------ +#include +#include + +// Local Includes -------------------------------------------------------------- #include +// Local Defines --------------------------------------------------------------- + +// BSimpleGameSound ------------------------------------------------------------ BSimpleGameSound::BSimpleGameSound(const entry_ref *inFile, BGameSoundDevice *device) - : BGameSound(device) + : BGameSound(device) { + if (InitCheck() == B_OK) SetInitError(Init(inFile)); } BSimpleGameSound::BSimpleGameSound(const char *inFile, BGameSoundDevice *device) - : BGameSound(device) + : BGameSound(device) { + if (InitCheck() == B_OK) + { + entry_ref file; + + if (get_ref_for_path(inFile, &file) != B_OK) + SetInitError(B_ENTRY_NOT_FOUND); + else + SetInitError(Init(&file)); + } } @@ -23,17 +72,25 @@ BSimpleGameSound::BSimpleGameSound(const void *inData, size_t inFrameCount, const gs_audio_format *format, BGameSoundDevice *device) - : BGameSound(device) + : BGameSound(device) { + if (InitCheck() == B_OK) SetInitError(Init(inData, inFrameCount, format)); } BSimpleGameSound::BSimpleGameSound(const BSimpleGameSound &other) - : BGameSound(other.Device()) -{ + : BGameSound(other) +{ + gs_audio_format format; + void * data; + + status_t error = other.Device()->Buffer(other.ID(), &format, data); + if (error != B_OK) SetInitError(error); + + Init(data, 0, &format); + free(data); } - - + BSimpleGameSound::~BSimpleGameSound() { } @@ -42,29 +99,176 @@ BSimpleGameSound::~BSimpleGameSound() BGameSound * BSimpleGameSound::Clone() const { - return NULL; + gs_audio_format format; + void * data; + + status_t error = Device()->Buffer(ID(), &format, data); + if (error != B_OK) return NULL; + + BSimpleGameSound * clone = new BSimpleGameSound(data, 0, &format, Device()); + + free(data); + + return clone; } - -status_t -BSimpleGameSound::Perform(int32 selector, - void *data) -{ - return B_ERROR; -} - - status_t BSimpleGameSound::SetIsLooping(bool looping) { - return B_ERROR; + gs_attribute attribute; + + attribute.attribute = B_GS_LOOPING; + attribute.value = (looping) ? -1.0 : 0.0; + attribute.duration = bigtime_t(0); + attribute.flags = 0; + + return Device()->SetAttributes(ID(), &attribute, 1); } bool BSimpleGameSound::IsLooping() const { - return false; + gs_attribute attribute; + + attribute.attribute = B_GS_LOOPING; + attribute.flags = 0; + + if (Device()->GetAttributes(ID(), &attribute, 1) != B_OK) + return false; + + return bool(attribute.value); +} + +status_t +BSimpleGameSound::Perform(int32 selector, + void *data) +{ + return B_ERROR; +} + +status_t +BSimpleGameSound::Init(const entry_ref* inFile) +{ + BMediaFile file(inFile); + gs_audio_format gsformat; + media_format mformat; + int64 framesRead, framesTotal = 0; + + if (file.InitCheck() != B_OK) return file.InitCheck(); + + BMediaTrack* audioStream = file.TrackAt(0); + audioStream->EncodedFormat(&mformat); + if (!mformat.IsAudio()) return B_ERROR; + + int64 frames = audioStream->CountFrames(); + + memset(&mformat, 0, sizeof(media_format)); + mformat.type = B_MEDIA_RAW_AUDIO; + mformat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; + audioStream->DecodedFormat(&mformat); + + memset(&gsformat, 0, sizeof(gs_audio_format)); + gsformat.frame_rate = mformat.u.raw_audio.frame_rate; + gsformat.channel_count = mformat.u.raw_audio.channel_count; + gsformat.byte_order = mformat.u.raw_audio.byte_order; + gsformat.buffer_size = mformat.u.raw_audio.buffer_size; + + if (mformat.u.raw_audio.format == media_raw_audio_format::B_AUDIO_CHAR) + { + // The GameKit doesnt support this format so we will have to reformat + // the data into something the GameKit does support. + char * buffer = new char[gsformat.buffer_size]; + uchar * data = new uchar[frames * gsformat.channel_count]; + + while (framesTotal < frames) + { + // read the next chunck from the stream + memset(buffer, 0, gsformat.buffer_size); + audioStream->ReadFrames(buffer, &framesRead); + + // refomat the buffer from + int64 position = framesTotal * gsformat.channel_count; + for(int32 i = 0; i < (int32)gsformat.buffer_size; i++) + data[i + position] = buffer[i] + 128; + + framesTotal += framesRead; + } + + gsformat.format = gs_audio_format::B_GS_U8; + + status_t error = Init(data, frames, &gsformat); + + // free the buffers we no longer need + delete [] buffer; + delete [] data; + + file.ReleaseTrack(audioStream); + return error; + } + + // We need to detriman the size, in bytes, of a single sample. + // At the same time, we will store the format of the audio buffer + size_t sampleSize; + switch(mformat.u.raw_audio.format) + { + case media_raw_audio_format::B_AUDIO_UCHAR: + sampleSize = sizeof(uchar); + gsformat.format = gs_audio_format::B_GS_U8; + break; + + case media_raw_audio_format::B_AUDIO_SHORT: + sampleSize = sizeof(int16); + gsformat.format = gs_audio_format::B_GS_S16; + break; + + case media_raw_audio_format::B_AUDIO_INT: + sampleSize = sizeof(int32); + gsformat.format = gs_audio_format::B_GS_S32; + break; + + case media_raw_audio_format::B_AUDIO_FLOAT: + sampleSize = sizeof(float); + gsformat.format = gs_audio_format::B_GS_F; + break; + + default: return B_ERROR; + } + + + char * data = (char*)malloc(frames * gsformat.channel_count * sampleSize); + gsformat.buffer_size = frames * gsformat.channel_count * sampleSize; + + while(framesTotal < frames) + { + int64 position = framesTotal * gsformat.channel_count * sampleSize; + audioStream->ReadFrames(&data[position], &framesRead); + + framesTotal += framesRead; + } + + status_t error = Init(data, frames, &gsformat); + + free(data); + + file.ReleaseTrack(audioStream); + return error; +} + + +status_t +BSimpleGameSound::Init(const void* inData, + int64 inFrameCount, + const gs_audio_format* format) +{ + gs_id sound; + + status_t error = Device()->CreateBuffer(&sound, format, inData, inFrameCount); + if (error != B_OK) return error; + + BGameSound::Init(sound); + + return B_OK; } diff --git a/src/kits/game/StreamingGameSound.cpp b/src/kits/game/StreamingGameSound.cpp index 50c9690638..093de7e6db 100644 --- a/src/kits/game/StreamingGameSound.cpp +++ b/src/kits/game/StreamingGameSound.cpp @@ -1,15 +1,61 @@ -/* - * Copyright 2002, Marcus Overhagen. All rights reserved. - * Distributed under the terms of the MIT License. - */ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: StreamingGameSound.cpp +// Author: Christopher ML Zumwalt May (zummy@users.sf.net) +// Description: BStreamingGameSound is a class for all kinds of streaming +// (data not known beforehand) game sounds. +//------------------------------------------------------------------------------ +// Standard Includes ----------------------------------------------------------- + +// System Includes ------------------------------------------------------------- + +// Project Includes ------------------------------------------------------------ +#include + +// Local Includes -------------------------------------------------------------- #include +// Local Defines --------------------------------------------------------------- BStreamingGameSound::BStreamingGameSound(size_t inBufferFrameCount, const gs_audio_format *format, size_t inBufferCount, BGameSoundDevice *device) - : BGameSound(device) + : BGameSound(device), + fStreamHook(NULL), + fStreamCookie(NULL) +{ + if (InitCheck() == B_OK) + { + status_t error = SetParameters(inBufferFrameCount, format, inBufferCount); + SetInitError(error); + } +} + + +BStreamingGameSound::BStreamingGameSound(BGameSoundDevice *device) + : BGameSound(device), + fStreamHook(NULL), + fStreamCookie(NULL) { } @@ -30,7 +76,10 @@ status_t BStreamingGameSound::SetStreamHook(void (*hook)(void * inCookie, void * inBuffer, size_t inByteCount, BStreamingGameSound * me), void * cookie) { - return B_ERROR; + fStreamHook = hook; + fStreamCookie = cookie; + + return B_OK; } @@ -38,14 +87,8 @@ void BStreamingGameSound::FillBuffer(void *inBuffer, size_t inByteCount) { -} - - -status_t -BStreamingGameSound::SetAttributes(gs_attribute *inAttributes, - size_t inAttributeCount) -{ - return B_ERROR; + if (fStreamHook) + (fStreamHook)(fStreamCookie, inBuffer, inByteCount, this); } @@ -57,42 +100,38 @@ BStreamingGameSound::Perform(int32 selector, } -BStreamingGameSound::BStreamingGameSound(BGameSoundDevice *device) - : BGameSound(device) +status_t +BStreamingGameSound::SetAttributes(gs_attribute * inAttributes, + size_t inAttributeCount) { + return BGameSound::SetAttributes(inAttributes, inAttributeCount); } status_t BStreamingGameSound::SetParameters(size_t inBufferFrameCount, - const gs_audio_format *format, - size_t inBufferCount) + const gs_audio_format *format, + size_t inBufferCount) { - return B_ERROR; + gs_id sound; + status_t error = Device()->CreateBuffer(&sound, this, format); + if (error != B_OK) return error; + + return BGameSound::Init(sound); } bool BStreamingGameSound::Lock() { - return false; + return fLock.Lock(); } void BStreamingGameSound::Unlock() { -} - - -void -BStreamingGameSound::stream_callback(void *cookie, - gs_id handle, - void *buffer, - int32 offset, - int32 size, - size_t bufSize) -{ + fLock.Unlock(); }