git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3854 a95241bf-73f2-0310-859d-f6bbb57e9c96
384 lines
9.5 KiB
C++
384 lines
9.5 KiB
C++
//------------------------------------------------------------------------------
|
|
// 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 ([email protected])
|
|
// 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.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include <malloc.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <List.h>
|
|
#include <MediaRoster.h>
|
|
#include <MediaAddOn.h>
|
|
#include <TimeSource.h>
|
|
#include <MediaTheme.h>
|
|
|
|
#include "GSUtility.h"
|
|
#include "GameSoundDevice.h"
|
|
#include "GameSoundBuffer.h"
|
|
#include "GameProducer.h"
|
|
|
|
// 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++)
|
|
{
|
|
if (fSounds[i]) fSounds[i]->StopPlaying();
|
|
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)
|
|
{
|
|
if (fSounds[sound-1])
|
|
{
|
|
// We must stop playback befor destroying the sound or else
|
|
// we may recieve fatel errors.
|
|
fSounds[sound-1]->StopPlaying();
|
|
|
|
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;
|
|
}
|
|
|