111bb5731Sbeveloper /*
211bb5731Sbeveloper ToneProducer.cpp
311bb5731Sbeveloper
411bb5731Sbeveloper Copyright 1999, Be Incorporated. All Rights Reserved.
511bb5731Sbeveloper This file may be used under the terms of the Be Sample Code License.
611bb5731Sbeveloper
711bb5731Sbeveloper NOTE: to compile this code under Genki beta releases, do a search-
811bb5731Sbeveloper and-replace to change "B_PARAMETER" to "B_USER_EVENT+1"
911bb5731Sbeveloper */
1011bb5731Sbeveloper
1111bb5731Sbeveloper #include "ToneProducer.h"
1211bb5731Sbeveloper #include <support/ByteOrder.h>
1311bb5731Sbeveloper #include <media/BufferGroup.h>
1411bb5731Sbeveloper #include <media/Buffer.h>
1511bb5731Sbeveloper #include <media/TimeSource.h>
1611bb5731Sbeveloper #include <media/ParameterWeb.h>
1711bb5731Sbeveloper #include <media/MediaDefs.h>
1811bb5731Sbeveloper #include <string.h>
1911bb5731Sbeveloper #include <stdio.h>
2011bb5731Sbeveloper #include <math.h>
2111bb5731Sbeveloper
2211bb5731Sbeveloper #include <Messenger.h>
2311bb5731Sbeveloper
2411bb5731Sbeveloper #include <Debug.h>
2511bb5731Sbeveloper #if DEBUG
2611bb5731Sbeveloper #define FPRINTF fprintf
2711bb5731Sbeveloper #else
FPRINTF(FILE *,const char *,...)2811bb5731Sbeveloper static inline void FPRINTF(FILE*, const char*, ...) { }
2911bb5731Sbeveloper #endif
3011bb5731Sbeveloper
3111bb5731Sbeveloper // parameter web handling
3211bb5731Sbeveloper static BParameterWeb* make_parameter_web();
3311bb5731Sbeveloper const int32 FREQUENCY_NULL_PARAM = 1;
3411bb5731Sbeveloper const int32 FREQUENCY_PARAM = 2;
3511bb5731Sbeveloper const int32 GAIN_NULL_PARAM = 11;
3611bb5731Sbeveloper const int32 GAIN_PARAM = 12;
3711bb5731Sbeveloper const int32 WAVEFORM_NULL_PARAM = 21;
3811bb5731Sbeveloper const int32 WAVEFORM_PARAM = 22;
3911bb5731Sbeveloper const int32 SINE_WAVE = 90;
4011bb5731Sbeveloper const int32 TRIANGLE_WAVE = 91;
4111bb5731Sbeveloper const int32 SAWTOOTH_WAVE = 92;
4211bb5731Sbeveloper
4311bb5731Sbeveloper // ----------------
4411bb5731Sbeveloper // ToneProducer implementation
4511bb5731Sbeveloper
ToneProducer(BMediaAddOn * pAddOn)4611bb5731Sbeveloper ToneProducer::ToneProducer(BMediaAddOn* pAddOn)
4711bb5731Sbeveloper : BMediaNode("ToneProducer"),
4811bb5731Sbeveloper BBufferProducer(B_MEDIA_RAW_AUDIO),
4911bb5731Sbeveloper BControllable(),
5011bb5731Sbeveloper BMediaEventLooper(),
5111bb5731Sbeveloper mWeb(NULL),
5211bb5731Sbeveloper mBufferGroup(NULL),
5311bb5731Sbeveloper mLatency(0),
5411bb5731Sbeveloper mInternalLatency(0),
5511bb5731Sbeveloper mOutputEnabled(true),
5611bb5731Sbeveloper mTheta(0.0),
5711bb5731Sbeveloper mWaveAscending(true),
5811bb5731Sbeveloper mFrequency(440),
5911bb5731Sbeveloper mGain(0.25),
6011bb5731Sbeveloper mWaveform(SINE_WAVE),
6111bb5731Sbeveloper mFramesSent(0),
6211bb5731Sbeveloper mStartTime(0),
6311bb5731Sbeveloper mGainLastChanged(0),
6411bb5731Sbeveloper mFreqLastChanged(0),
6511bb5731Sbeveloper mWaveLastChanged(0),
6611bb5731Sbeveloper m_pAddOn(pAddOn)
6711bb5731Sbeveloper {
6811bb5731Sbeveloper // initialize our preferred format object
6911bb5731Sbeveloper mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
7011bb5731Sbeveloper mPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
7111bb5731Sbeveloper mPreferredFormat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
7211bb5731Sbeveloper
73*b1d006d7SDario Casalinuovo // we'll use the consumer's preferred buffer size and framerate, if any
74*b1d006d7SDario Casalinuovo mPreferredFormat.u.raw_audio.frame_rate = media_raw_audio_format::wildcard.frame_rate;
7511bb5731Sbeveloper mPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size;
7611bb5731Sbeveloper
7711bb5731Sbeveloper // 20sep99: multiple-channel support
7811bb5731Sbeveloper mPreferredFormat.u.raw_audio.channel_count = media_raw_audio_format::wildcard.channel_count;
7911bb5731Sbeveloper
8011bb5731Sbeveloper
8111bb5731Sbeveloper // we're not connected yet
8211bb5731Sbeveloper mOutput.destination = media_destination::null;
8311bb5731Sbeveloper
8411bb5731Sbeveloper // [e.moon 1dec99]
8511bb5731Sbeveloper mOutput.format = mPreferredFormat;
8611bb5731Sbeveloper
8711bb5731Sbeveloper // set up as much information about our output as we can
8811bb5731Sbeveloper // +++++ wrong; can't call Node() until the node is registered!
8911bb5731Sbeveloper mOutput.source.port = ControlPort();
9011bb5731Sbeveloper mOutput.source.id = 0;
9111bb5731Sbeveloper mOutput.node = Node();
9211bb5731Sbeveloper ::strcpy(mOutput.name, "ToneProducer Output");
9311bb5731Sbeveloper }
9411bb5731Sbeveloper
~ToneProducer()9511bb5731Sbeveloper ToneProducer::~ToneProducer()
9611bb5731Sbeveloper {
9711bb5731Sbeveloper // Stop the BMediaEventLooper thread
9811bb5731Sbeveloper Quit();
9911bb5731Sbeveloper
10011bb5731Sbeveloper // the BControllable destructor deletes our parameter web for us; we just use
10111bb5731Sbeveloper // a little defensive programming here and set our web pointer to be NULL.
10211bb5731Sbeveloper mWeb = NULL;
10311bb5731Sbeveloper }
10411bb5731Sbeveloper
10511bb5731Sbeveloper //#pragma mark -
10611bb5731Sbeveloper
10711bb5731Sbeveloper // BMediaNode methods
10811bb5731Sbeveloper BMediaAddOn *
AddOn(int32 * internal_id) const10911bb5731Sbeveloper ToneProducer::AddOn(int32 *internal_id) const
11011bb5731Sbeveloper {
11111bb5731Sbeveloper // e.moon [8jun99]
11211bb5731Sbeveloper if(m_pAddOn) {
11311bb5731Sbeveloper *internal_id = 0;
11411bb5731Sbeveloper return m_pAddOn;
11511bb5731Sbeveloper } else
11611bb5731Sbeveloper return NULL;
11711bb5731Sbeveloper }
11811bb5731Sbeveloper
11911bb5731Sbeveloper //#pragma mark -
12011bb5731Sbeveloper
12111bb5731Sbeveloper // BControllable methods
12211bb5731Sbeveloper status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * ioSize)12311bb5731Sbeveloper ToneProducer::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
12411bb5731Sbeveloper {
12511bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::GetParameterValue\n");
12611bb5731Sbeveloper
12711bb5731Sbeveloper // floats & int32s are the same size, so this one test of the size of the
12811bb5731Sbeveloper // output buffer is sufficient for all of our parameters
12911bb5731Sbeveloper if (*ioSize < sizeof(float)) return B_ERROR;
13011bb5731Sbeveloper
13111bb5731Sbeveloper // fill in the value of the requested parameter
13211bb5731Sbeveloper switch (id)
13311bb5731Sbeveloper {
13411bb5731Sbeveloper case FREQUENCY_PARAM:
13511bb5731Sbeveloper *last_change = mFreqLastChanged;
13611bb5731Sbeveloper *((float*) value) = mFrequency;
13711bb5731Sbeveloper *ioSize = sizeof(float);
13811bb5731Sbeveloper break;
13911bb5731Sbeveloper
14011bb5731Sbeveloper case GAIN_PARAM:
14111bb5731Sbeveloper *last_change = mGainLastChanged;
14211bb5731Sbeveloper *((float*) value) = mGain;
14311bb5731Sbeveloper *ioSize = sizeof(float);
14411bb5731Sbeveloper break;
14511bb5731Sbeveloper
14611bb5731Sbeveloper case WAVEFORM_PARAM:
14711bb5731Sbeveloper *last_change = mWaveLastChanged;
14811bb5731Sbeveloper *((int32*) value) = mWaveform;
14911bb5731Sbeveloper *ioSize = sizeof(int32);
15011bb5731Sbeveloper break;
15111bb5731Sbeveloper
15211bb5731Sbeveloper default:
15311bb5731Sbeveloper // Hmmm, we were asked for a parameter that we don't actually
15411bb5731Sbeveloper // support. Report an error back to the caller.
1551a7bcf69SOliver Tappe FPRINTF(stderr, "\terror - asked for illegal parameter %" B_PRId32 "\n",
1561a7bcf69SOliver Tappe id);
15711bb5731Sbeveloper return B_ERROR;
15811bb5731Sbeveloper break;
15911bb5731Sbeveloper }
16011bb5731Sbeveloper
16111bb5731Sbeveloper return B_OK;
16211bb5731Sbeveloper }
16311bb5731Sbeveloper
16411bb5731Sbeveloper void
SetParameterValue(int32 id,bigtime_t performance_time,const void * value,size_t size)16511bb5731Sbeveloper ToneProducer::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
16611bb5731Sbeveloper {
16711bb5731Sbeveloper switch (id)
16811bb5731Sbeveloper {
16911bb5731Sbeveloper case FREQUENCY_PARAM:
17011bb5731Sbeveloper case GAIN_PARAM:
17111bb5731Sbeveloper case WAVEFORM_PARAM:
17211bb5731Sbeveloper {
17311bb5731Sbeveloper // floats and int32s are the same size, so we need only check the block's size once
17411bb5731Sbeveloper if (size > sizeof(float)) size = sizeof(float);
17511bb5731Sbeveloper
17611bb5731Sbeveloper // submit the parameter change as a performance event, to be handled at the
17711bb5731Sbeveloper // appropriate time
17811bb5731Sbeveloper media_timed_event event(performance_time, _PARAMETER_EVENT,
17911bb5731Sbeveloper NULL, BTimedEventQueue::B_NO_CLEANUP, size, id, (char*) value, size);
18011bb5731Sbeveloper EventQueue()->AddEvent(event);
18111bb5731Sbeveloper }
18211bb5731Sbeveloper break;
18311bb5731Sbeveloper
18411bb5731Sbeveloper default:
18511bb5731Sbeveloper break;
18611bb5731Sbeveloper }
18711bb5731Sbeveloper }
18811bb5731Sbeveloper
18911bb5731Sbeveloper // e.moon [17jun99]
StartControlPanel(BMessenger * pMessenger)19011bb5731Sbeveloper status_t ToneProducer::StartControlPanel(
19111bb5731Sbeveloper BMessenger* pMessenger) {
19211bb5731Sbeveloper PRINT(("ToneProducer::StartControlPanel(%p)\n", pMessenger));
19311bb5731Sbeveloper status_t err = BControllable::StartControlPanel(pMessenger);
19411bb5731Sbeveloper if(pMessenger && pMessenger->IsValid()) {
19511bb5731Sbeveloper PRINT(("\tgot valid control panel\n"));
19611bb5731Sbeveloper }
19711bb5731Sbeveloper
19811bb5731Sbeveloper return err;
19911bb5731Sbeveloper }
20011bb5731Sbeveloper
20111bb5731Sbeveloper //#pragma mark -
20211bb5731Sbeveloper
20311bb5731Sbeveloper // BBufferProducer methods
20411bb5731Sbeveloper status_t
FormatSuggestionRequested(media_type type,int32,media_format * format)20511bb5731Sbeveloper ToneProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
20611bb5731Sbeveloper {
20711bb5731Sbeveloper // FormatSuggestionRequested() is not necessarily part of the format negotiation
20811bb5731Sbeveloper // process; it's simply an interrogation -- the caller wants to see what the node's
20911bb5731Sbeveloper // preferred data format is, given a suggestion by the caller.
21011bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::FormatSuggestionRequested\n");
21111bb5731Sbeveloper
21211bb5731Sbeveloper if (!format)
21311bb5731Sbeveloper {
21411bb5731Sbeveloper FPRINTF(stderr, "\tERROR - NULL format pointer passed in!\n");
21511bb5731Sbeveloper return B_BAD_VALUE;
21611bb5731Sbeveloper }
21711bb5731Sbeveloper
21811bb5731Sbeveloper // this is the format we'll be returning (our preferred format)
21911bb5731Sbeveloper *format = mPreferredFormat;
22011bb5731Sbeveloper
22111bb5731Sbeveloper // a wildcard type is okay; we can specialize it
22211bb5731Sbeveloper if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO;
22311bb5731Sbeveloper
22411bb5731Sbeveloper // we only support raw audio
22511bb5731Sbeveloper if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT;
22611bb5731Sbeveloper else return B_OK;
22711bb5731Sbeveloper }
22811bb5731Sbeveloper
22911bb5731Sbeveloper status_t
FormatProposal(const media_source & output,media_format * format)23011bb5731Sbeveloper ToneProducer::FormatProposal(const media_source& output, media_format* format)
23111bb5731Sbeveloper {
23211bb5731Sbeveloper // FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand
23311bb5731Sbeveloper // out a suggested format, with wildcards for any variations we support.
23411bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::FormatProposal\n");
23511bb5731Sbeveloper
23611bb5731Sbeveloper // is this a proposal for our one output?
23711bb5731Sbeveloper if (output != mOutput.source)
23811bb5731Sbeveloper {
23911bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
24011bb5731Sbeveloper return B_MEDIA_BAD_SOURCE;
24111bb5731Sbeveloper }
24211bb5731Sbeveloper
24311bb5731Sbeveloper // we only support floating-point raw audio, so we always return that, but we
24411bb5731Sbeveloper // supply an error code depending on whether we found the proposal acceptable.
24511bb5731Sbeveloper
24611bb5731Sbeveloper media_type requestedType = format->type;
24711bb5731Sbeveloper *format = mPreferredFormat;
24811bb5731Sbeveloper if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO))
24911bb5731Sbeveloper {
25011bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
25111bb5731Sbeveloper return B_MEDIA_BAD_FORMAT;
25211bb5731Sbeveloper }
25311bb5731Sbeveloper else return B_OK; // raw audio or wildcard type, either is okay by us
25411bb5731Sbeveloper }
25511bb5731Sbeveloper
25611bb5731Sbeveloper status_t
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * io_format,int32 * _deprecated_)25711bb5731Sbeveloper ToneProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
25811bb5731Sbeveloper {
25911bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::FormatChangeRequested\n");
26011bb5731Sbeveloper
26111bb5731Sbeveloper // we don't support any other formats, so we just reject any format changes.
26211bb5731Sbeveloper return B_ERROR;
26311bb5731Sbeveloper }
26411bb5731Sbeveloper
26511bb5731Sbeveloper status_t
GetNextOutput(int32 * cookie,media_output * out_output)26611bb5731Sbeveloper ToneProducer::GetNextOutput(int32* cookie, media_output* out_output)
26711bb5731Sbeveloper {
26811bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::GetNextOutput\n");
26911bb5731Sbeveloper
27011bb5731Sbeveloper // we have only a single output; if we supported multiple outputs, we'd
27111bb5731Sbeveloper // iterate over whatever data structure we were using to keep track of
27211bb5731Sbeveloper // them.
27311bb5731Sbeveloper if (0 == *cookie)
27411bb5731Sbeveloper {
27511bb5731Sbeveloper *out_output = mOutput;
27611bb5731Sbeveloper *cookie += 1;
27711bb5731Sbeveloper return B_OK;
27811bb5731Sbeveloper }
27911bb5731Sbeveloper else return B_BAD_INDEX;
28011bb5731Sbeveloper }
28111bb5731Sbeveloper
28211bb5731Sbeveloper status_t
DisposeOutputCookie(int32 cookie)28311bb5731Sbeveloper ToneProducer::DisposeOutputCookie(int32 cookie)
28411bb5731Sbeveloper {
28511bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::DisposeOutputCookie\n");
28611bb5731Sbeveloper
28711bb5731Sbeveloper // do nothing because we don't use the cookie for anything special
28811bb5731Sbeveloper return B_OK;
28911bb5731Sbeveloper }
29011bb5731Sbeveloper
29111bb5731Sbeveloper status_t
SetBufferGroup(const media_source & for_source,BBufferGroup * newGroup)29211bb5731Sbeveloper ToneProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
29311bb5731Sbeveloper {
29411bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::SetBufferGroup\n");
29511bb5731Sbeveloper
29611bb5731Sbeveloper // verify that we didn't get bogus arguments before we proceed
29711bb5731Sbeveloper if (for_source != mOutput.source) return B_MEDIA_BAD_SOURCE;
29811bb5731Sbeveloper
29911bb5731Sbeveloper // Are we being passed the buffer group we're already using?
30011bb5731Sbeveloper if (newGroup == mBufferGroup) return B_OK;
30111bb5731Sbeveloper
30211bb5731Sbeveloper // Ahh, someone wants us to use a different buffer group. At this point we delete
30311bb5731Sbeveloper // the one we are using and use the specified one instead. If the specified group is
30411bb5731Sbeveloper // NULL, we need to recreate one ourselves, and use *that*. Note that if we're
30511bb5731Sbeveloper // caching a BBuffer that we requested earlier, we have to Recycle() that buffer
30611bb5731Sbeveloper // *before* deleting the buffer group, otherwise we'll deadlock waiting for that
30711bb5731Sbeveloper // buffer to be recycled!
30811bb5731Sbeveloper delete mBufferGroup; // waits for all buffers to recycle
30911bb5731Sbeveloper if (newGroup != NULL)
31011bb5731Sbeveloper {
31111bb5731Sbeveloper // we were given a valid group; just use that one from now on
31211bb5731Sbeveloper mBufferGroup = newGroup;
31311bb5731Sbeveloper }
31411bb5731Sbeveloper else
31511bb5731Sbeveloper {
31611bb5731Sbeveloper // we were passed a NULL group pointer; that means we construct
31711bb5731Sbeveloper // our own buffer group to use from now on
31811bb5731Sbeveloper size_t size = mOutput.format.u.raw_audio.buffer_size;
31911bb5731Sbeveloper int32 count = int32(mLatency / BufferDuration() + 1 + 1);
32011bb5731Sbeveloper mBufferGroup = new BBufferGroup(size, count);
32111bb5731Sbeveloper }
32211bb5731Sbeveloper
32311bb5731Sbeveloper return B_OK;
32411bb5731Sbeveloper }
32511bb5731Sbeveloper
32611bb5731Sbeveloper status_t
GetLatency(bigtime_t * out_latency)32711bb5731Sbeveloper ToneProducer::GetLatency(bigtime_t* out_latency)
32811bb5731Sbeveloper {
32911bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::GetLatency\n");
33011bb5731Sbeveloper
33111bb5731Sbeveloper // report our *total* latency: internal plus downstream plus scheduling
33211bb5731Sbeveloper *out_latency = EventLatency() + SchedulingLatency();
33311bb5731Sbeveloper return B_OK;
33411bb5731Sbeveloper }
33511bb5731Sbeveloper
33611bb5731Sbeveloper status_t
PrepareToConnect(const media_source & what,const media_destination & where,media_format * format,media_source * out_source,char * out_name)33711bb5731Sbeveloper ToneProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
33811bb5731Sbeveloper {
33911bb5731Sbeveloper // PrepareToConnect() is the second stage of format negotiations that happens
34011bb5731Sbeveloper // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat()
34111bb5731Sbeveloper // method has been called, and that node has potentially changed the proposed
34211bb5731Sbeveloper // format. It may also have left wildcards in the format. PrepareToConnect()
34311bb5731Sbeveloper // *must* fully specialize the format before returning!
34411bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::PrepareToConnect\n");
34511bb5731Sbeveloper
34611bb5731Sbeveloper // trying to connect something that isn't our source?
34711bb5731Sbeveloper if (what != mOutput.source) return B_MEDIA_BAD_SOURCE;
34811bb5731Sbeveloper
34911bb5731Sbeveloper // are we already connected?
35011bb5731Sbeveloper if (mOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED;
35111bb5731Sbeveloper
35211bb5731Sbeveloper // the format may not yet be fully specialized (the consumer might have
35311bb5731Sbeveloper // passed back some wildcards). Finish specializing it now, and return an
35411bb5731Sbeveloper // error if we don't support the requested format.
35511bb5731Sbeveloper if (format->type != B_MEDIA_RAW_AUDIO)
35611bb5731Sbeveloper {
35711bb5731Sbeveloper FPRINTF(stderr, "\tnon-raw-audio format?!\n");
35811bb5731Sbeveloper return B_MEDIA_BAD_FORMAT;
35911bb5731Sbeveloper }
36011bb5731Sbeveloper else if (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_FLOAT)
36111bb5731Sbeveloper {
36211bb5731Sbeveloper FPRINTF(stderr, "\tnon-float-audio format?!\n");
36311bb5731Sbeveloper return B_MEDIA_BAD_FORMAT;
36411bb5731Sbeveloper }
36511bb5731Sbeveloper else if(format->u.raw_audio.channel_count > 2) {
36611bb5731Sbeveloper format->u.raw_audio.channel_count = 2;
36711bb5731Sbeveloper return B_MEDIA_BAD_FORMAT;
36811bb5731Sbeveloper }
36911bb5731Sbeveloper
37011bb5731Sbeveloper
37111bb5731Sbeveloper // !!! validate all other fields except for buffer_size here, because the consumer might have
37211bb5731Sbeveloper // supplied different values from AcceptFormat()?
37311bb5731Sbeveloper
37411bb5731Sbeveloper // *** e.moon [11jun99]: filling in sensible field values.
37511bb5731Sbeveloper // Connect() doesn't take kindly to a frame_rate of 0.
37611bb5731Sbeveloper
37711bb5731Sbeveloper if(format->u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) {
378*b1d006d7SDario Casalinuovo format->u.raw_audio.frame_rate = 44100.0f;
37911bb5731Sbeveloper FPRINTF(stderr, "\tno frame rate provided, suggesting %.1f\n", format->u.raw_audio.frame_rate);
38011bb5731Sbeveloper }
38111bb5731Sbeveloper if(format->u.raw_audio.channel_count == media_raw_audio_format::wildcard.channel_count) {
38211bb5731Sbeveloper //format->u.raw_audio.channel_count = mPreferredFormat.u.raw_audio.channel_count;
38311bb5731Sbeveloper format->u.raw_audio.channel_count = 1;
3841a7bcf69SOliver Tappe FPRINTF(stderr, "\tno channel count provided, suggesting %" B_PRIu32 "\n", format->u.raw_audio.channel_count);
38511bb5731Sbeveloper }
38611bb5731Sbeveloper if(format->u.raw_audio.byte_order == media_raw_audio_format::wildcard.byte_order) {
38711bb5731Sbeveloper format->u.raw_audio.byte_order = mPreferredFormat.u.raw_audio.byte_order;
38811bb5731Sbeveloper FPRINTF(stderr, "\tno channel count provided, suggesting %s\n",
38911bb5731Sbeveloper (format->u.raw_audio.byte_order == B_MEDIA_BIG_ENDIAN) ? "B_MEDIA_BIG_ENDIAN" : "B_MEDIA_LITTLE_ENDIAN");
39011bb5731Sbeveloper }
39111bb5731Sbeveloper
39211bb5731Sbeveloper // check the buffer size, which may still be wildcarded
39311bb5731Sbeveloper if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
39411bb5731Sbeveloper {
39511bb5731Sbeveloper format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest
39611bb5731Sbeveloper FPRINTF(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size);
39711bb5731Sbeveloper }
39811bb5731Sbeveloper else
39911bb5731Sbeveloper {
40011bb5731Sbeveloper FPRINTF(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size);
40111bb5731Sbeveloper }
40211bb5731Sbeveloper
40311bb5731Sbeveloper // Now reserve the connection, and return information about it
40411bb5731Sbeveloper mOutput.destination = where;
40511bb5731Sbeveloper mOutput.format = *format;
40611bb5731Sbeveloper *out_source = mOutput.source;
40711bb5731Sbeveloper strncpy(out_name, mOutput.name, B_MEDIA_NAME_LENGTH);
40811bb5731Sbeveloper
40911bb5731Sbeveloper char formatStr[256];
41011bb5731Sbeveloper string_for_format(*format, formatStr, 255);
41111bb5731Sbeveloper FPRINTF(stderr, "\treturning format: %s\n", formatStr);
41211bb5731Sbeveloper
41311bb5731Sbeveloper return B_OK;
41411bb5731Sbeveloper }
41511bb5731Sbeveloper
41611bb5731Sbeveloper void
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * io_name)41711bb5731Sbeveloper ToneProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
41811bb5731Sbeveloper {
41911bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::Connect\n");
42011bb5731Sbeveloper
42111bb5731Sbeveloper // If something earlier failed, Connect() might still be called, but with a non-zero
42211bb5731Sbeveloper // error code. When that happens we simply unreserve the connection and do
42311bb5731Sbeveloper // nothing else.
42411bb5731Sbeveloper if (error)
42511bb5731Sbeveloper {
42611bb5731Sbeveloper mOutput.destination = media_destination::null;
42711bb5731Sbeveloper mOutput.format = mPreferredFormat;
42811bb5731Sbeveloper return;
42911bb5731Sbeveloper }
43011bb5731Sbeveloper
43111bb5731Sbeveloper // old workaround for format bug: Connect() receives the format data from the
43211bb5731Sbeveloper // input returned from BBufferConsumer::Connected().
43311bb5731Sbeveloper //
43411bb5731Sbeveloper // char formatStr[256];
43511bb5731Sbeveloper // string_for_format(format, formatStr, 255);
43611bb5731Sbeveloper // FPRINTF(stderr, "\trequested format: %s\n", formatStr);
43711bb5731Sbeveloper // if(format.type != B_MEDIA_RAW_AUDIO) {
43811bb5731Sbeveloper // // +++++ this is NOT proper behavior
43911bb5731Sbeveloper // // but it works
44011bb5731Sbeveloper // FPRINTF(stderr, "\tcorrupted format; falling back to last suggested format\n");
44111bb5731Sbeveloper // format = mOutput.format;
44211bb5731Sbeveloper // }
44311bb5731Sbeveloper //
44411bb5731Sbeveloper
44511bb5731Sbeveloper // Okay, the connection has been confirmed. Record the destination and format
44611bb5731Sbeveloper // that we agreed on, and report our connection name again.
44711bb5731Sbeveloper mOutput.destination = destination;
44811bb5731Sbeveloper mOutput.format = format;
44911bb5731Sbeveloper strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH);
45011bb5731Sbeveloper
45111bb5731Sbeveloper // Now that we're connected, we can determine our downstream latency.
45211bb5731Sbeveloper // Do so, then make sure we get our events early enough.
45311bb5731Sbeveloper media_node_id id;
45411bb5731Sbeveloper FindLatencyFor(mOutput.destination, &mLatency, &id);
4551a7bcf69SOliver Tappe FPRINTF(stderr, "\tdownstream latency = %" B_PRIdBIGTIME "\n", mLatency);
45611bb5731Sbeveloper
45711bb5731Sbeveloper // Use a dry run to see how long it takes me to fill a buffer of data
45811bb5731Sbeveloper bigtime_t start, produceLatency;
45911bb5731Sbeveloper size_t samplesPerBuffer = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
46011bb5731Sbeveloper size_t framesPerBuffer = samplesPerBuffer / mOutput.format.u.raw_audio.channel_count;
46111bb5731Sbeveloper float* data = new float[samplesPerBuffer];
46211bb5731Sbeveloper mTheta = 0;
46311bb5731Sbeveloper start = ::system_time();
46411bb5731Sbeveloper FillSineBuffer(data, framesPerBuffer, mOutput.format.u.raw_audio.channel_count==2);
46511bb5731Sbeveloper produceLatency = ::system_time();
46611bb5731Sbeveloper mInternalLatency = produceLatency - start;
46711bb5731Sbeveloper
46811bb5731Sbeveloper // +++++ e.moon [13jun99]: fiddling with latency, ick
46911bb5731Sbeveloper mInternalLatency += 20000LL;
47011bb5731Sbeveloper
47111bb5731Sbeveloper delete [] data;
4721a7bcf69SOliver Tappe FPRINTF(stderr, "\tbuffer-filling took %" B_PRIdBIGTIME
4731a7bcf69SOliver Tappe " usec on this machine\n", mInternalLatency);
47411bb5731Sbeveloper SetEventLatency(mLatency + mInternalLatency);
47511bb5731Sbeveloper
47611bb5731Sbeveloper // reset our buffer duration, etc. to avoid later calculations
47711bb5731Sbeveloper // +++++ e.moon 11jun99: crashes w/ divide-by-zero when connecting to LoggingConsumer
47811bb5731Sbeveloper ASSERT(mOutput.format.u.raw_audio.frame_rate);
47911bb5731Sbeveloper
48011bb5731Sbeveloper bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate);
48111bb5731Sbeveloper SetBufferDuration(duration);
48211bb5731Sbeveloper
48311bb5731Sbeveloper // Set up the buffer group for our connection, as long as nobody handed us a
48411bb5731Sbeveloper // buffer group (via SetBufferGroup()) prior to this. That can happen, for example,
48511bb5731Sbeveloper // if the consumer calls SetOutputBuffersFor() on us from within its Connected()
48611bb5731Sbeveloper // method.
48711bb5731Sbeveloper if (!mBufferGroup) AllocateBuffers();
48811bb5731Sbeveloper }
48911bb5731Sbeveloper
49011bb5731Sbeveloper void
Disconnect(const media_source & what,const media_destination & where)49111bb5731Sbeveloper ToneProducer::Disconnect(const media_source& what, const media_destination& where)
49211bb5731Sbeveloper {
49311bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::Disconnect\n");
49411bb5731Sbeveloper
49511bb5731Sbeveloper // Make sure that our connection is the one being disconnected
49611bb5731Sbeveloper if ((where == mOutput.destination) && (what == mOutput.source))
49711bb5731Sbeveloper {
49811bb5731Sbeveloper mOutput.destination = media_destination::null;
49911bb5731Sbeveloper mOutput.format = mPreferredFormat;
50011bb5731Sbeveloper delete mBufferGroup;
50111bb5731Sbeveloper mBufferGroup = NULL;
50211bb5731Sbeveloper }
50311bb5731Sbeveloper else
50411bb5731Sbeveloper {
5051a7bcf69SOliver Tappe FPRINTF(stderr, "\tDisconnect() called with wrong source/destination (%"
5061a7bcf69SOliver Tappe B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32 ")\n",
50711bb5731Sbeveloper what.id, where.id, mOutput.source.id, mOutput.destination.id);
50811bb5731Sbeveloper }
50911bb5731Sbeveloper }
51011bb5731Sbeveloper
51111bb5731Sbeveloper void
LateNoticeReceived(const media_source & what,bigtime_t how_much,bigtime_t performance_time)51211bb5731Sbeveloper ToneProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
51311bb5731Sbeveloper {
51411bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::LateNoticeReceived\n");
51511bb5731Sbeveloper
51611bb5731Sbeveloper // If we're late, we need to catch up. Respond in a manner appropriate to our
51711bb5731Sbeveloper // current run mode.
51811bb5731Sbeveloper if (what == mOutput.source)
51911bb5731Sbeveloper {
52011bb5731Sbeveloper if (RunMode() == B_RECORDING)
52111bb5731Sbeveloper {
52211bb5731Sbeveloper // A hardware capture node can't adjust; it simply emits buffers at
52311bb5731Sbeveloper // appropriate points. We (partially) simulate this by not adjusting
52411bb5731Sbeveloper // our behavior upon receiving late notices -- after all, the hardware
52511bb5731Sbeveloper // can't choose to capture "sooner"....
52611bb5731Sbeveloper }
52711bb5731Sbeveloper else if (RunMode() == B_INCREASE_LATENCY)
52811bb5731Sbeveloper {
52911bb5731Sbeveloper // We're late, and our run mode dictates that we try to produce buffers
53011bb5731Sbeveloper // earlier in order to catch up. This argues that the downstream nodes are
53111bb5731Sbeveloper // not properly reporting their latency, but there's not much we can do about
53211bb5731Sbeveloper // that at the moment, so we try to start producing buffers earlier to
53311bb5731Sbeveloper // compensate.
53411bb5731Sbeveloper mInternalLatency += how_much;
535b63f90a8Sbeveloper if (mInternalLatency > 50000)
536b63f90a8Sbeveloper mInternalLatency = 50000;
53711bb5731Sbeveloper SetEventLatency(mLatency + mInternalLatency);
53811bb5731Sbeveloper
5391a7bcf69SOliver Tappe FPRINTF(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n",
5401a7bcf69SOliver Tappe mLatency + mInternalLatency);
54111bb5731Sbeveloper }
54211bb5731Sbeveloper else
54311bb5731Sbeveloper {
54411bb5731Sbeveloper // The other run modes dictate various strategies for sacrificing data quality
54511bb5731Sbeveloper // in the interests of timely data delivery. The way *we* do this is to skip
54611bb5731Sbeveloper // a buffer, which catches us up in time by one buffer duration.
54711bb5731Sbeveloper size_t nSamples = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
54811bb5731Sbeveloper mFramesSent += nSamples;
54911bb5731Sbeveloper
55011bb5731Sbeveloper FPRINTF(stderr, "\tskipping a buffer to try to catch up\n");
55111bb5731Sbeveloper }
55211bb5731Sbeveloper }
55311bb5731Sbeveloper }
55411bb5731Sbeveloper
55511bb5731Sbeveloper void
EnableOutput(const media_source & what,bool enabled,int32 * _deprecated_)55611bb5731Sbeveloper ToneProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
55711bb5731Sbeveloper {
55811bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::EnableOutput\n");
55911bb5731Sbeveloper
56011bb5731Sbeveloper // If I had more than one output, I'd have to walk my list of output records to see
56111bb5731Sbeveloper // which one matched the given source, and then enable/disable that one. But this
56211bb5731Sbeveloper // node only has one output, so I just make sure the given source matches, then set
56311bb5731Sbeveloper // the enable state accordingly.
56411bb5731Sbeveloper if (what == mOutput.source)
56511bb5731Sbeveloper {
56611bb5731Sbeveloper mOutputEnabled = enabled;
56711bb5731Sbeveloper }
56811bb5731Sbeveloper }
56911bb5731Sbeveloper
57011bb5731Sbeveloper status_t
SetPlayRate(int32 numer,int32 denom)57111bb5731Sbeveloper ToneProducer::SetPlayRate(int32 numer, int32 denom)
57211bb5731Sbeveloper {
57311bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::SetPlayRate\n");
57411bb5731Sbeveloper
57511bb5731Sbeveloper // Play rates are weird. We don't support them. Maybe we will in a
57611bb5731Sbeveloper // later newsletter article....
57711bb5731Sbeveloper return B_ERROR;
57811bb5731Sbeveloper }
57911bb5731Sbeveloper
58011bb5731Sbeveloper status_t
HandleMessage(int32 message,const void * data,size_t size)58111bb5731Sbeveloper ToneProducer::HandleMessage(int32 message, const void* data, size_t size)
58211bb5731Sbeveloper {
5831a7bcf69SOliver Tappe FPRINTF(stderr, "ToneProducer::HandleMessage(%" B_PRId32 " = 0x%" B_PRIx32
5841a7bcf69SOliver Tappe ")\n", message, message);
58511bb5731Sbeveloper // HandleMessage() is where you implement any private message protocols
58611bb5731Sbeveloper // that you want to use. When messages are written to your node's control
58711bb5731Sbeveloper // port that are not recognized by any of the node superclasses, they'll be
58811bb5731Sbeveloper // passed to this method in your node's implementation for handling. The
58911bb5731Sbeveloper // ToneProducer node doesn't support any private messages, so we just
59011bb5731Sbeveloper // return an error, indicating that the message wasn't handled.
59111bb5731Sbeveloper return B_ERROR;
59211bb5731Sbeveloper }
59311bb5731Sbeveloper
59411bb5731Sbeveloper void
AdditionalBufferRequested(const media_source & source,media_buffer_id prev_buffer,bigtime_t prev_time,const media_seek_tag * prev_tag)59511bb5731Sbeveloper ToneProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
59611bb5731Sbeveloper {
59711bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::AdditionalBufferRequested\n");
59811bb5731Sbeveloper
59911bb5731Sbeveloper // we don't support offline mode (yet...)
60011bb5731Sbeveloper return;
60111bb5731Sbeveloper }
60211bb5731Sbeveloper
60311bb5731Sbeveloper void
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t new_latency,uint32 flags)60411bb5731Sbeveloper ToneProducer::LatencyChanged(
60511bb5731Sbeveloper const media_source& source,
60611bb5731Sbeveloper const media_destination& destination,
60711bb5731Sbeveloper bigtime_t new_latency,
60811bb5731Sbeveloper uint32 flags)
60911bb5731Sbeveloper {
6101a7bcf69SOliver Tappe PRINT(("ToneProducer::LatencyChanged(): %" B_PRIdBIGTIME "\n",
6111a7bcf69SOliver Tappe new_latency));
61211bb5731Sbeveloper
61311bb5731Sbeveloper // something downstream changed latency, so we need to start producing
61411bb5731Sbeveloper // buffers earlier (or later) than we were previously. Make sure that the
61511bb5731Sbeveloper // connection that changed is ours, and adjust to the new downstream
61611bb5731Sbeveloper // latency if so.
61711bb5731Sbeveloper if ((source == mOutput.source) && (destination == mOutput.destination))
61811bb5731Sbeveloper {
61911bb5731Sbeveloper mLatency = new_latency;
62011bb5731Sbeveloper SetEventLatency(mLatency + mInternalLatency);
62111bb5731Sbeveloper }
62211bb5731Sbeveloper }
62311bb5731Sbeveloper
62411bb5731Sbeveloper //#pragma mark -
62511bb5731Sbeveloper
62611bb5731Sbeveloper // BMediaEventLooper methods
62711bb5731Sbeveloper void
NodeRegistered()62811bb5731Sbeveloper ToneProducer::NodeRegistered()
62911bb5731Sbeveloper {
63011bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::NodeRegistered\n");
63111bb5731Sbeveloper
63211bb5731Sbeveloper // output init moved to ctor
63311bb5731Sbeveloper // e.moon [4jun99]
63411bb5731Sbeveloper
63511bb5731Sbeveloper // Set up our parameter web
63611bb5731Sbeveloper mWeb = make_parameter_web();
63711bb5731Sbeveloper SetParameterWeb(mWeb);
6386d2f2ec1SDario Casalinuovo
6396d2f2ec1SDario Casalinuovo // Start the BMediaEventLooper thread
6406d2f2ec1SDario Casalinuovo SetPriority(B_REAL_TIME_PRIORITY);
6416d2f2ec1SDario Casalinuovo Run();
64211bb5731Sbeveloper }
64311bb5731Sbeveloper
64411bb5731Sbeveloper void
Start(bigtime_t performance_time)64511bb5731Sbeveloper ToneProducer::Start(bigtime_t performance_time)
64611bb5731Sbeveloper {
6471a7bcf69SOliver Tappe PRINT(("ToneProducer::Start(%" B_PRIdBIGTIME "): now %" B_PRIdBIGTIME "\n",
6481a7bcf69SOliver Tappe performance_time, TimeSource()->Now()));
64911bb5731Sbeveloper
65011bb5731Sbeveloper // send 'data available' message
65111bb5731Sbeveloper if(mOutput.destination != media_destination::null)
65211bb5731Sbeveloper SendDataStatus(B_DATA_AVAILABLE, mOutput.destination, performance_time);
65311bb5731Sbeveloper
65411bb5731Sbeveloper // A bug in the current PowerPC compiler demands that we implement
65511bb5731Sbeveloper // this, even though it just calls up to the inherited implementation.
65611bb5731Sbeveloper BMediaEventLooper::Start(performance_time);
65711bb5731Sbeveloper }
65811bb5731Sbeveloper
65911bb5731Sbeveloper void
Stop(bigtime_t performance_time,bool immediate)66011bb5731Sbeveloper ToneProducer::Stop(bigtime_t performance_time, bool immediate)
66111bb5731Sbeveloper {
66211bb5731Sbeveloper // send 'data not available' message
66311bb5731Sbeveloper if(mOutput.destination != media_destination::null) {
664e393a169SJérôme Duval printf("ToneProducer: B_PRODUCER_STOPPED at %" B_PRIdBIGTIME "\n",
665e393a169SJérôme Duval performance_time);
66611bb5731Sbeveloper SendDataStatus(B_PRODUCER_STOPPED, mOutput.destination, performance_time);
66711bb5731Sbeveloper }
66811bb5731Sbeveloper
66911bb5731Sbeveloper // A bug in the current PowerPC compiler demands that we implement
67011bb5731Sbeveloper // this, even though it just calls up to the inherited implementation.
67111bb5731Sbeveloper BMediaEventLooper::Stop(performance_time, immediate);
67211bb5731Sbeveloper }
67311bb5731Sbeveloper
67411bb5731Sbeveloper void
SetRunMode(run_mode mode)67511bb5731Sbeveloper ToneProducer::SetRunMode(run_mode mode)
67611bb5731Sbeveloper {
67711bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::SetRunMode\n");
67811bb5731Sbeveloper
67911bb5731Sbeveloper // We don't support offline run mode, so broadcast an error if we're set to
68011bb5731Sbeveloper // B_OFFLINE. Unfortunately, we can't actually reject the mode change...
68111bb5731Sbeveloper if (B_OFFLINE == mode)
68211bb5731Sbeveloper {
68311bb5731Sbeveloper ReportError(B_NODE_FAILED_SET_RUN_MODE);
68411bb5731Sbeveloper }
68511bb5731Sbeveloper }
68611bb5731Sbeveloper
68711bb5731Sbeveloper void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)68811bb5731Sbeveloper ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent)
68911bb5731Sbeveloper {
69011bb5731Sbeveloper // FPRINTF(stderr, "ToneProducer::HandleEvent\n");
69111bb5731Sbeveloper switch (event->type)
69211bb5731Sbeveloper {
69311bb5731Sbeveloper case BTimedEventQueue::B_START:
69411bb5731Sbeveloper // don't do anything if we're already running
69511bb5731Sbeveloper if (RunState() != B_STARTED)
69611bb5731Sbeveloper {
69711bb5731Sbeveloper // We want to start sending buffers now, so we set up the buffer-sending bookkeeping
69811bb5731Sbeveloper // and fire off the first "produce a buffer" event.
69911bb5731Sbeveloper mFramesSent = 0;
70011bb5731Sbeveloper mTheta = 0;
70111bb5731Sbeveloper mStartTime = event->event_time;
70211bb5731Sbeveloper media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER);
70311bb5731Sbeveloper
70411bb5731Sbeveloper // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
70511bb5731Sbeveloper // the event queue, like this:
70611bb5731Sbeveloper //
70711bb5731Sbeveloper // this->HandleEvent(&firstBufferEvent, 0, false);
70811bb5731Sbeveloper //
70911bb5731Sbeveloper EventQueue()->AddEvent(firstBufferEvent);
71011bb5731Sbeveloper }
71111bb5731Sbeveloper break;
71211bb5731Sbeveloper
71311bb5731Sbeveloper case BTimedEventQueue::B_STOP:
71411bb5731Sbeveloper FPRINTF(stderr, "Handling B_STOP event\n");
71511bb5731Sbeveloper
71611bb5731Sbeveloper // When we handle a stop, we must ensure that downstream consumers don't
71711bb5731Sbeveloper // get any more buffers from us. This means we have to flush any pending
71811bb5731Sbeveloper // buffer-producing events from the queue.
71911bb5731Sbeveloper EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
72011bb5731Sbeveloper break;
72111bb5731Sbeveloper
72211bb5731Sbeveloper case _PARAMETER_EVENT:
72311bb5731Sbeveloper {
72411bb5731Sbeveloper size_t dataSize = size_t(event->data);
72511bb5731Sbeveloper int32 param = int32(event->bigdata);
72611bb5731Sbeveloper if (dataSize >= sizeof(float)) switch (param)
72711bb5731Sbeveloper {
72811bb5731Sbeveloper case FREQUENCY_PARAM:
72911bb5731Sbeveloper {
73011bb5731Sbeveloper float newValue = *((float*) event->user_data);
73111bb5731Sbeveloper if (mFrequency != newValue) // an actual change in the value?
73211bb5731Sbeveloper {
73311bb5731Sbeveloper mFrequency = newValue;
73411bb5731Sbeveloper mFreqLastChanged = TimeSource()->Now();
73511bb5731Sbeveloper BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency));
73611bb5731Sbeveloper }
73711bb5731Sbeveloper }
73811bb5731Sbeveloper break;
73911bb5731Sbeveloper
74011bb5731Sbeveloper case GAIN_PARAM:
74111bb5731Sbeveloper {
74211bb5731Sbeveloper float newValue = *((float*) event->user_data);
74311bb5731Sbeveloper if (mGain != newValue)
74411bb5731Sbeveloper {
74511bb5731Sbeveloper mGain = newValue;
74611bb5731Sbeveloper mGainLastChanged = TimeSource()->Now();
74711bb5731Sbeveloper BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain));
74811bb5731Sbeveloper }
74911bb5731Sbeveloper }
75011bb5731Sbeveloper break;
75111bb5731Sbeveloper
75211bb5731Sbeveloper case WAVEFORM_PARAM:
75311bb5731Sbeveloper {
75411bb5731Sbeveloper int32 newValue = *((int32*) event->user_data);
75511bb5731Sbeveloper if (mWaveform != newValue)
75611bb5731Sbeveloper {
75711bb5731Sbeveloper mWaveform = newValue;
75811bb5731Sbeveloper mTheta = 0; // reset the generator parameters when we change waveforms
75911bb5731Sbeveloper mWaveAscending = true;
76011bb5731Sbeveloper mWaveLastChanged = TimeSource()->Now();
76111bb5731Sbeveloper BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform));
76211bb5731Sbeveloper }
76311bb5731Sbeveloper }
76411bb5731Sbeveloper break;
76511bb5731Sbeveloper
76611bb5731Sbeveloper default:
7671a7bcf69SOliver Tappe FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%" B_PRId32 ")\n", param);
76811bb5731Sbeveloper break;
76911bb5731Sbeveloper }
77011bb5731Sbeveloper }
77111bb5731Sbeveloper break;
77211bb5731Sbeveloper
77311bb5731Sbeveloper case BTimedEventQueue::B_HANDLE_BUFFER:
77411bb5731Sbeveloper {
77511bb5731Sbeveloper // make sure we're both started *and* connected before delivering a buffer
776b289aaf6SAxel Dörfler if (RunState() == BMediaEventLooper::B_STARTED
777b289aaf6SAxel Dörfler && mOutput.destination != media_destination::null) {
77811bb5731Sbeveloper // Get the next buffer of data
77911bb5731Sbeveloper BBuffer* buffer = FillNextBuffer(event->event_time);
780b289aaf6SAxel Dörfler if (buffer) {
78111bb5731Sbeveloper // send the buffer downstream if and only if output is enabled
78211bb5731Sbeveloper status_t err = B_ERROR;
783b289aaf6SAxel Dörfler if (mOutputEnabled) {
784b289aaf6SAxel Dörfler err = SendBuffer(buffer, mOutput.source,
785b289aaf6SAxel Dörfler mOutput.destination);
786b289aaf6SAxel Dörfler }
787b289aaf6SAxel Dörfler if (err) {
78811bb5731Sbeveloper // we need to recycle the buffer ourselves if output is disabled or
78911bb5731Sbeveloper // if the call to SendBuffer() fails
79011bb5731Sbeveloper buffer->Recycle();
79111bb5731Sbeveloper }
79211bb5731Sbeveloper }
79311bb5731Sbeveloper
79411bb5731Sbeveloper // track how much media we've delivered so far
79511bb5731Sbeveloper size_t nFrames = mOutput.format.u.raw_audio.buffer_size /
79611bb5731Sbeveloper (sizeof(float) * mOutput.format.u.raw_audio.channel_count);
79711bb5731Sbeveloper mFramesSent += nFrames;
79811bb5731Sbeveloper
79911bb5731Sbeveloper // The buffer is on its way; now schedule the next one to go
800b289aaf6SAxel Dörfler bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent)
801b289aaf6SAxel Dörfler / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
802b289aaf6SAxel Dörfler media_timed_event nextBufferEvent(nextEvent,
803b289aaf6SAxel Dörfler BTimedEventQueue::B_HANDLE_BUFFER);
80411bb5731Sbeveloper EventQueue()->AddEvent(nextBufferEvent);
80511bb5731Sbeveloper }
80611bb5731Sbeveloper }
80711bb5731Sbeveloper break;
80811bb5731Sbeveloper
80911bb5731Sbeveloper default:
81011bb5731Sbeveloper break;
81111bb5731Sbeveloper }
81211bb5731Sbeveloper }
81311bb5731Sbeveloper
81411bb5731Sbeveloper //#pragma mark -
81511bb5731Sbeveloper
81611bb5731Sbeveloper void
AllocateBuffers()81711bb5731Sbeveloper ToneProducer::AllocateBuffers()
81811bb5731Sbeveloper {
81911bb5731Sbeveloper FPRINTF(stderr, "ToneProducer::AllocateBuffers\n");
82011bb5731Sbeveloper
82111bb5731Sbeveloper // allocate enough buffers to span our downstream latency, plus one
82211bb5731Sbeveloper size_t size = mOutput.format.u.raw_audio.buffer_size;
82311bb5731Sbeveloper int32 count = int32(mLatency / BufferDuration() + 1 + 1);
82411bb5731Sbeveloper
8251a7bcf69SOliver Tappe FPRINTF(stderr, "\tlatency = %" B_PRIdBIGTIME ", buffer duration = %"
8261a7bcf69SOliver Tappe B_PRIdBIGTIME "\n", mLatency, BufferDuration());
8271a7bcf69SOliver Tappe FPRINTF(stderr, "\tcreating group of %" B_PRId32 " buffers, size = %"
8281a7bcf69SOliver Tappe B_PRIuSIZE "\n", count, size);
82911bb5731Sbeveloper mBufferGroup = new BBufferGroup(size, count);
83011bb5731Sbeveloper }
83111bb5731Sbeveloper
83211bb5731Sbeveloper BBuffer*
FillNextBuffer(bigtime_t event_time)83311bb5731Sbeveloper ToneProducer::FillNextBuffer(bigtime_t event_time)
83411bb5731Sbeveloper {
83511bb5731Sbeveloper // get a buffer from our buffer group
83611bb5731Sbeveloper BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration());
83711bb5731Sbeveloper
83811bb5731Sbeveloper // if we fail to get a buffer (for example, if the request times out), we skip this
83911bb5731Sbeveloper // buffer and go on to the next, to avoid locking up the control thread
84011bb5731Sbeveloper if (!buf)
84111bb5731Sbeveloper {
84211bb5731Sbeveloper return NULL;
84311bb5731Sbeveloper }
84411bb5731Sbeveloper
84511bb5731Sbeveloper // now fill it with data, continuing where the last buffer left off
84611bb5731Sbeveloper // 20sep99: multichannel support
84711bb5731Sbeveloper
84811bb5731Sbeveloper size_t numFrames =
84911bb5731Sbeveloper mOutput.format.u.raw_audio.buffer_size /
85011bb5731Sbeveloper (sizeof(float)*mOutput.format.u.raw_audio.channel_count);
85111bb5731Sbeveloper bool stereo = (mOutput.format.u.raw_audio.channel_count == 2);
85211bb5731Sbeveloper if(!stereo) {
85311bb5731Sbeveloper ASSERT(mOutput.format.u.raw_audio.channel_count == 1);
85411bb5731Sbeveloper }
85511bb5731Sbeveloper // PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono"));
85611bb5731Sbeveloper
85711bb5731Sbeveloper float* data = (float*) buf->Data();
85811bb5731Sbeveloper
85911bb5731Sbeveloper switch (mWaveform)
86011bb5731Sbeveloper {
86111bb5731Sbeveloper case SINE_WAVE:
86211bb5731Sbeveloper FillSineBuffer(data, numFrames, stereo);
86311bb5731Sbeveloper break;
86411bb5731Sbeveloper
86511bb5731Sbeveloper case TRIANGLE_WAVE:
86611bb5731Sbeveloper FillTriangleBuffer(data, numFrames, stereo);
86711bb5731Sbeveloper break;
86811bb5731Sbeveloper
86911bb5731Sbeveloper case SAWTOOTH_WAVE:
87011bb5731Sbeveloper FillSawtoothBuffer(data, numFrames, stereo);
87111bb5731Sbeveloper break;
87211bb5731Sbeveloper }
87311bb5731Sbeveloper
87411bb5731Sbeveloper // fill in the buffer header
87511bb5731Sbeveloper media_header* hdr = buf->Header();
87611bb5731Sbeveloper hdr->type = B_MEDIA_RAW_AUDIO;
87711bb5731Sbeveloper hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
87811bb5731Sbeveloper hdr->time_source = TimeSource()->ID();
87911bb5731Sbeveloper
88011bb5731Sbeveloper bigtime_t stamp;
88111bb5731Sbeveloper if (RunMode() == B_RECORDING)
88211bb5731Sbeveloper {
88311bb5731Sbeveloper // In B_RECORDING mode, we stamp with the capture time. We're not
88411bb5731Sbeveloper // really a hardware capture node, but we simulate it by using the (precalculated)
88511bb5731Sbeveloper // time at which this buffer "should" have been created.
88611bb5731Sbeveloper stamp = event_time;
88711bb5731Sbeveloper }
88811bb5731Sbeveloper else
88911bb5731Sbeveloper {
89011bb5731Sbeveloper // okay, we're in one of the "live" performance run modes. in these modes, we
89111bb5731Sbeveloper // stamp the buffer with the time at which the buffer should be rendered to the
89211bb5731Sbeveloper // output, not with the capture time. mStartTime is the cached value of the
89311bb5731Sbeveloper // first buffer's performance time; we calculate this buffer's performance time as
89411bb5731Sbeveloper // an offset from that time, based on the amount of media we've created so far.
89511bb5731Sbeveloper // Recalculating every buffer like this avoids accumulation of error.
89611bb5731Sbeveloper stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
89711bb5731Sbeveloper }
89811bb5731Sbeveloper hdr->start_time = stamp;
89911bb5731Sbeveloper
90011bb5731Sbeveloper return buf;
90111bb5731Sbeveloper }
90211bb5731Sbeveloper
90311bb5731Sbeveloper // waveform generators - fill buffers with various waveforms
90411bb5731Sbeveloper void
FillSineBuffer(float * data,size_t numFrames,bool stereo)90511bb5731Sbeveloper ToneProducer::FillSineBuffer(float *data, size_t numFrames, bool stereo)
90611bb5731Sbeveloper {
90711bb5731Sbeveloper
90811bb5731Sbeveloper
90911bb5731Sbeveloper // cover 2pi radians in one period
91011bb5731Sbeveloper double dTheta = 2*M_PI * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
91111bb5731Sbeveloper
91211bb5731Sbeveloper // Fill the buffer!
91311bb5731Sbeveloper for (size_t i = 0; i < numFrames; i++, data++)
91411bb5731Sbeveloper {
91511bb5731Sbeveloper float val = mGain * float(sin(mTheta));
91611bb5731Sbeveloper *data = val;
91711bb5731Sbeveloper if(stereo) {
91811bb5731Sbeveloper ++data;
91911bb5731Sbeveloper *data = val;
92011bb5731Sbeveloper }
92111bb5731Sbeveloper
92211bb5731Sbeveloper mTheta += dTheta;
92311bb5731Sbeveloper if (mTheta > 2*M_PI)
92411bb5731Sbeveloper {
92511bb5731Sbeveloper mTheta -= 2*M_PI;
92611bb5731Sbeveloper }
92711bb5731Sbeveloper }
92811bb5731Sbeveloper }
92911bb5731Sbeveloper
93011bb5731Sbeveloper void
FillTriangleBuffer(float * data,size_t numFrames,bool stereo)93111bb5731Sbeveloper ToneProducer::FillTriangleBuffer(float *data, size_t numFrames, bool stereo)
93211bb5731Sbeveloper {
93311bb5731Sbeveloper // ramp from -1 to 1 and back in one period
93411bb5731Sbeveloper double dTheta = 4.0 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
93511bb5731Sbeveloper if (!mWaveAscending) dTheta = -dTheta;
93611bb5731Sbeveloper
93711bb5731Sbeveloper // fill the buffer!
93811bb5731Sbeveloper for (size_t i = 0; i < numFrames; i++, data++)
93911bb5731Sbeveloper {
94011bb5731Sbeveloper float val = mGain * mTheta;
94111bb5731Sbeveloper *data = val;
94211bb5731Sbeveloper if(stereo) {
94311bb5731Sbeveloper ++data;
94411bb5731Sbeveloper *data = val;
94511bb5731Sbeveloper }
94611bb5731Sbeveloper
94711bb5731Sbeveloper mTheta += dTheta;
94811bb5731Sbeveloper if (mTheta >= 1)
94911bb5731Sbeveloper {
95011bb5731Sbeveloper mTheta = 2 - mTheta; // reflect across the mTheta=1 line to preserve drift
95111bb5731Sbeveloper mWaveAscending = false;
95211bb5731Sbeveloper dTheta = -dTheta;
95311bb5731Sbeveloper }
95411bb5731Sbeveloper else if (mTheta <= -1)
95511bb5731Sbeveloper {
95611bb5731Sbeveloper mTheta = -2 - mTheta; // reflect across mTheta=-1
95711bb5731Sbeveloper mWaveAscending = true;
95811bb5731Sbeveloper dTheta = -dTheta;
95911bb5731Sbeveloper }
96011bb5731Sbeveloper }
96111bb5731Sbeveloper }
96211bb5731Sbeveloper
96311bb5731Sbeveloper void
FillSawtoothBuffer(float * data,size_t numFrames,bool stereo)96411bb5731Sbeveloper ToneProducer::FillSawtoothBuffer(float *data, size_t numFrames, bool stereo)
96511bb5731Sbeveloper {
96611bb5731Sbeveloper // ramp from -1 to 1 in one period
96711bb5731Sbeveloper double dTheta = 2 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
96811bb5731Sbeveloper mWaveAscending = true;
96911bb5731Sbeveloper
97011bb5731Sbeveloper // fill the buffer!
97111bb5731Sbeveloper for (size_t i = 0; i < numFrames; i++, data++)
97211bb5731Sbeveloper {
97311bb5731Sbeveloper float val = mGain * mTheta;
97411bb5731Sbeveloper *data = val;
97511bb5731Sbeveloper if(stereo) {
97611bb5731Sbeveloper ++data;
97711bb5731Sbeveloper *data = val;
97811bb5731Sbeveloper }
97911bb5731Sbeveloper
98011bb5731Sbeveloper mTheta += dTheta;
98111bb5731Sbeveloper if (mTheta > 1)
98211bb5731Sbeveloper {
98311bb5731Sbeveloper mTheta -= 2; // back to the base of the sawtooth, including cumulative drift
98411bb5731Sbeveloper }
98511bb5731Sbeveloper }
98611bb5731Sbeveloper }
98711bb5731Sbeveloper
98811bb5731Sbeveloper // utility - build the ToneProducer's parameter web
make_parameter_web()98911bb5731Sbeveloper static BParameterWeb* make_parameter_web()
99011bb5731Sbeveloper {
99111bb5731Sbeveloper FPRINTF(stderr, "make_parameter_web() called\n");
99211bb5731Sbeveloper
99311bb5731Sbeveloper BParameterWeb* web = new BParameterWeb;
99411bb5731Sbeveloper BParameterGroup* mainGroup = web->MakeGroup("Tone Generator Parameters");
99511bb5731Sbeveloper
99611bb5731Sbeveloper BParameterGroup* group = mainGroup->MakeGroup("Frequency");
99711bb5731Sbeveloper BParameter* nullParam = group->MakeNullParameter(FREQUENCY_NULL_PARAM, B_MEDIA_NO_TYPE, "Frequency", B_GENERIC);
99811bb5731Sbeveloper BContinuousParameter* param = group->MakeContinuousParameter(FREQUENCY_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "Hz", 0, 2500, 0.1);
99911bb5731Sbeveloper nullParam->AddOutput(param);
100011bb5731Sbeveloper param->AddInput(nullParam);
100111bb5731Sbeveloper
100211bb5731Sbeveloper group = mainGroup->MakeGroup("Amplitude");
100311bb5731Sbeveloper nullParam = group->MakeNullParameter(GAIN_NULL_PARAM, B_MEDIA_NO_TYPE, "Amplitude", B_GENERIC);
100411bb5731Sbeveloper param = group->MakeContinuousParameter(GAIN_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "", 0, 1, 0.01);
100511bb5731Sbeveloper nullParam->AddOutput(param);
100611bb5731Sbeveloper param->AddInput(nullParam);
100711bb5731Sbeveloper
100811bb5731Sbeveloper group = mainGroup->MakeGroup("Waveform");
100911bb5731Sbeveloper nullParam = group->MakeNullParameter(WAVEFORM_NULL_PARAM, B_MEDIA_NO_TYPE, "Waveform", B_GENERIC);
101011bb5731Sbeveloper BDiscreteParameter* waveParam = group->MakeDiscreteParameter(WAVEFORM_PARAM, B_MEDIA_NO_TYPE, "", B_GENERIC);
101111bb5731Sbeveloper waveParam->AddItem(SINE_WAVE, "Sine wave");
101211bb5731Sbeveloper waveParam->AddItem(TRIANGLE_WAVE, "Triangle");
101311bb5731Sbeveloper waveParam->AddItem(SAWTOOTH_WAVE, "Sawtooth");
101411bb5731Sbeveloper nullParam->AddOutput(waveParam);
101511bb5731Sbeveloper waveParam->AddInput(nullParam);
101611bb5731Sbeveloper
101711bb5731Sbeveloper return web;
101811bb5731Sbeveloper }
1019