* Copyright 2002-2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christopher ML Zumwalt May (zummy@users.sf.net)
*/
and sends them to the audio mixer
*/
#include "GameProducer.h"
#include <string.h>
#include <stdio.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <ByteOrder.h>
#include <List.h>
#include <MediaDefs.h>
#include <TimeSource.h>
#include "GameSoundBuffer.h"
#include "GameSoundDevice.h"
#include "GSUtility.h"
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)
{
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;
fPreferredFormat.u.raw_audio.byte_order = format->byte_order;
fPreferredFormat.u.raw_audio.buffer_size
= media_raw_audio_format::wildcard.buffer_size;
fOutput.destination = media_destination::null;
fOutput.format = fPreferredFormat;
fFrameSize = get_sample_size(format->format) * format->channel_count;
fObject = object;
}
GameProducer::~GameProducer()
{
Quit();
}
BMediaAddOn*
GameProducer::AddOn(int32* internal_id) const
{
return NULL;
}
status_t
GameProducer::GetNextOutput(int32* cookie, media_output* _output)
{
if (0 != *cookie)
return B_BAD_INDEX;
*_output = fOutput;
*cookie += 1;
return B_OK;
}
status_t
GameProducer::DisposeOutputCookie(int32 cookie)
{
return B_OK;
}
void
GameProducer::EnableOutput(const media_source& what, bool enabled,
int32* _deprecated_)
{
if (what == fOutput.source)
{
fOutputEnabled = enabled;
}
}
status_t
GameProducer::FormatSuggestionRequested(media_type type, int32 ,
media_format* format)
{
if (!format)
return B_BAD_VALUE;
*format = fPreferredFormat;
if (type == B_MEDIA_UNKNOWN_TYPE)
return B_OK;
return (type != B_MEDIA_RAW_AUDIO) ? B_MEDIA_BAD_FORMAT : B_OK;
}
status_t
GameProducer::FormatProposal(const media_source& output, media_format* format)
{
if (output != fOutput.source)
return B_MEDIA_BAD_SOURCE;
*format = fPreferredFormat;
media_type requestedType = format->type;
if ((requestedType != B_MEDIA_UNKNOWN_TYPE)
&& (requestedType != B_MEDIA_RAW_AUDIO)) {
return B_MEDIA_BAD_FORMAT;
}
return B_OK;
}
status_t
GameProducer::PrepareToConnect(const media_source& what,
const media_destination& where, media_format* format,
media_source* _source, char* out_name)
{
if (what != fOutput.source)
return B_MEDIA_BAD_SOURCE;
if (fOutput.destination != media_destination::null)
return B_MEDIA_ALREADY_CONNECTED;
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;
if (format->u.raw_audio.buffer_size
== media_raw_audio_format::wildcard.buffer_size) {
format->u.raw_audio.buffer_size = 4096;
}
fOutput.destination = where;
fOutput.format = *format;
*_source = fOutput.source;
strlcpy(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* ioName)
{
if (error) {
fOutput.destination = media_destination::null;
fOutput.format = fPreferredFormat;
return;
}
fOutput.destination = destination;
fOutput.format = format;
strlcpy(ioName, fOutput.name, B_MEDIA_NAME_LENGTH);
media_node_id id;
FindLatencyFor(fOutput.destination, &fLatency, &id);
if (!fBufferGroup)
fBufferSize = fOutput.format.u.raw_audio.buffer_size;
bigtime_t start, produceLatency;
int32 frames = int32(fBufferSize / fFrameSize);
float* data = new float[frames * 2];
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();
fInternalLatency = produceLatency - start;
SetEventLatency(fLatency + fInternalLatency);
delete [] data;
bigtime_t duration = bigtime_t(1000000) * frames
/ bigtime_t(fOutput.format.u.raw_audio.frame_rate);
SetBufferDuration(duration);
if (!fBufferGroup) {
int32 count = int32(fLatency / BufferDuration() + 2);
fBufferGroup = new BBufferGroup(fBufferSize, count);
}
}
void
GameProducer::Disconnect(const media_source& what,
const media_destination& where)
{
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_)
{
return B_ERROR;
}
status_t
GameProducer::SetBufferGroup(const media_source& forSource,
BBufferGroup* newGroup)
{
if (forSource != fOutput.source)
return B_MEDIA_BAD_SOURCE;
if (newGroup == fBufferGroup)
return B_OK;
delete fBufferGroup;
if (newGroup != NULL) {
fBufferGroup = newGroup;
BBuffer* buffers[1];
if (newGroup->GetBufferList(1, buffers) != B_OK)
return B_BAD_VALUE;
fBufferSize = buffers[0]->SizeAvailable();
} else {
fBufferSize = fOutput.format.u.raw_audio.buffer_size;
int32 count = int32(fLatency / BufferDuration() + 2);
fBufferGroup = new BBufferGroup(fBufferSize, count);
}
return B_OK;
}
status_t
GameProducer::GetLatency(bigtime_t* _latency)
{
*_latency = EventLatency() + SchedulingLatency();
return B_OK;
}
void
GameProducer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
bigtime_t performanceDuration)
{
if (what == fOutput.source) {
if (RunMode() == B_RECORDING) {
} else if (RunMode() == B_INCREASE_LATENCY) {
fInternalLatency += howMuch;
SetEventLatency(fLatency + fInternalLatency);
} else {
size_t nSamples = fBufferSize / fFrameSize;
fFramesSent += nSamples;
}
}
}
void
GameProducer::LatencyChanged(const media_source& source,
const media_destination& destination, bigtime_t new_latency, uint32 flags)
{
if ((source == fOutput.source) && (destination == fOutput.destination)) {
fLatency = new_latency;
SetEventLatency(fLatency + fInternalLatency);
}
}
status_t
GameProducer::SetPlayRate(int32 numerator, int32 denominator)
{
return B_ERROR;
}
status_t
GameProducer::HandleMessage(int32 message, const void* data, size_t size)
{
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)
{
return;
}
void
GameProducer::NodeRegistered()
{
fOutput.source.port = ControlPort();
fOutput.source.id = 0;
fOutput.node = Node();
strlcpy(fOutput.name, "GameProducer Output", B_MEDIA_NAME_LENGTH);
SetPriority(B_REAL_TIME_PRIORITY);
Run();
}
void
GameProducer::SetRunMode(run_mode mode)
{
if (B_OFFLINE == mode) {
ReportError(B_NODE_FAILED_SET_RUN_MODE);
}
}
void
GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
bool realTimeEvent)
{
switch (event->type)
{
case BTimedEventQueue::B_START:
if (RunState() != B_STARTED) {
fFramesSent = 0;
fStartTime = event->event_time;
media_timed_event firstBufferEvent(fStartTime,
BTimedEventQueue::B_HANDLE_BUFFER);
EventQueue()->AddEvent(firstBufferEvent);
}
break;
case BTimedEventQueue::B_STOP:
EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
BTimedEventQueue::B_HANDLE_BUFFER);
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
{
if ((RunState() == BMediaEventLooper::B_STARTED)
&& (fOutput.destination != media_destination::null)) {
BBuffer* buffer = FillNextBuffer(event->event_time);
if (buffer) {
status_t err = B_ERROR;
if (fOutputEnabled) {
err = SendBuffer(buffer, fOutput.source,
fOutput.destination);
}
if (err) {
buffer->Recycle();
}
}
size_t nFrames = fBufferSize / fFrameSize;
fFramesSent += nFrames;
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;
}
}
BBuffer*
GameProducer::FillNextBuffer(bigtime_t event_time)
{
BBuffer* buf = fBufferGroup->RequestBuffer(fBufferSize, BufferDuration());
if (!buf)
return NULL;
int64 frames = int64(fBufferSize / fFrameSize);
memset(buf->Data(), 0, fBufferSize);
fObject->Play(buf->Data(), frames);
media_header* hdr = buf->Header();
hdr->type = B_MEDIA_RAW_AUDIO;
hdr->size_used = fBufferSize;
hdr->time_source = TimeSource()->ID();
bigtime_t stamp;
if (RunMode() == B_RECORDING) {
stamp = event_time;
} else {
stamp = fStartTime + bigtime_t(double(fFramesSent)
/ double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0);
}
hdr->start_time = stamp;
return buf;
}