1 /*
2 ToneProducer.cpp
3
4 Copyright 1999, Be Incorporated. All Rights Reserved.
5 This file may be used under the terms of the Be Sample Code License.
6
7 NOTE: to compile this code under Genki beta releases, do a search-
8 and-replace to change "B_PARAMETER" to "B_USER_EVENT+1"
9 */
10
11 #include "ToneProducer.h"
12 #include <support/ByteOrder.h>
13 #include <media/BufferGroup.h>
14 #include <media/Buffer.h>
15 #include <media/TimeSource.h>
16 #include <media/ParameterWeb.h>
17 #include <media/MediaDefs.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <math.h>
21
22 #include <Messenger.h>
23
24 #include <Debug.h>
25 #if DEBUG
26 #define FPRINTF fprintf
27 #else
FPRINTF(FILE *,const char *,...)28 static inline void FPRINTF(FILE*, const char*, ...) { }
29 #endif
30
31 // parameter web handling
32 static BParameterWeb* make_parameter_web();
33 const int32 FREQUENCY_NULL_PARAM = 1;
34 const int32 FREQUENCY_PARAM = 2;
35 const int32 GAIN_NULL_PARAM = 11;
36 const int32 GAIN_PARAM = 12;
37 const int32 WAVEFORM_NULL_PARAM = 21;
38 const int32 WAVEFORM_PARAM = 22;
39 const int32 SINE_WAVE = 90;
40 const int32 TRIANGLE_WAVE = 91;
41 const int32 SAWTOOTH_WAVE = 92;
42
43 // ----------------
44 // ToneProducer implementation
45
ToneProducer(BMediaAddOn * pAddOn)46 ToneProducer::ToneProducer(BMediaAddOn* pAddOn)
47 : BMediaNode("ToneProducer"),
48 BBufferProducer(B_MEDIA_RAW_AUDIO),
49 BControllable(),
50 BMediaEventLooper(),
51 mWeb(NULL),
52 mBufferGroup(NULL),
53 mLatency(0),
54 mInternalLatency(0),
55 mOutputEnabled(true),
56 mTheta(0.0),
57 mWaveAscending(true),
58 mFrequency(440),
59 mGain(0.25),
60 mWaveform(SINE_WAVE),
61 mFramesSent(0),
62 mStartTime(0),
63 mGainLastChanged(0),
64 mFreqLastChanged(0),
65 mWaveLastChanged(0),
66 m_pAddOn(pAddOn)
67 {
68 // initialize our preferred format object
69 mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
70 mPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
71 mPreferredFormat.u.raw_audio.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
72
73 // we'll use the consumer's preferred buffer size and framerate, if any
74 mPreferredFormat.u.raw_audio.frame_rate = media_raw_audio_format::wildcard.frame_rate;
75 mPreferredFormat.u.raw_audio.buffer_size = media_raw_audio_format::wildcard.buffer_size;
76
77 // 20sep99: multiple-channel support
78 mPreferredFormat.u.raw_audio.channel_count = media_raw_audio_format::wildcard.channel_count;
79
80
81 // we're not connected yet
82 mOutput.destination = media_destination::null;
83
84 // [e.moon 1dec99]
85 mOutput.format = mPreferredFormat;
86
87 // set up as much information about our output as we can
88 // +++++ wrong; can't call Node() until the node is registered!
89 mOutput.source.port = ControlPort();
90 mOutput.source.id = 0;
91 mOutput.node = Node();
92 ::strcpy(mOutput.name, "ToneProducer Output");
93 }
94
~ToneProducer()95 ToneProducer::~ToneProducer()
96 {
97 // Stop the BMediaEventLooper thread
98 Quit();
99
100 // the BControllable destructor deletes our parameter web for us; we just use
101 // a little defensive programming here and set our web pointer to be NULL.
102 mWeb = NULL;
103 }
104
105 //#pragma mark -
106
107 // BMediaNode methods
108 BMediaAddOn *
AddOn(int32 * internal_id) const109 ToneProducer::AddOn(int32 *internal_id) const
110 {
111 // e.moon [8jun99]
112 if(m_pAddOn) {
113 *internal_id = 0;
114 return m_pAddOn;
115 } else
116 return NULL;
117 }
118
119 //#pragma mark -
120
121 // BControllable methods
122 status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * ioSize)123 ToneProducer::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
124 {
125 FPRINTF(stderr, "ToneProducer::GetParameterValue\n");
126
127 // floats & int32s are the same size, so this one test of the size of the
128 // output buffer is sufficient for all of our parameters
129 if (*ioSize < sizeof(float)) return B_ERROR;
130
131 // fill in the value of the requested parameter
132 switch (id)
133 {
134 case FREQUENCY_PARAM:
135 *last_change = mFreqLastChanged;
136 *((float*) value) = mFrequency;
137 *ioSize = sizeof(float);
138 break;
139
140 case GAIN_PARAM:
141 *last_change = mGainLastChanged;
142 *((float*) value) = mGain;
143 *ioSize = sizeof(float);
144 break;
145
146 case WAVEFORM_PARAM:
147 *last_change = mWaveLastChanged;
148 *((int32*) value) = mWaveform;
149 *ioSize = sizeof(int32);
150 break;
151
152 default:
153 // Hmmm, we were asked for a parameter that we don't actually
154 // support. Report an error back to the caller.
155 FPRINTF(stderr, "\terror - asked for illegal parameter %" B_PRId32 "\n",
156 id);
157 return B_ERROR;
158 break;
159 }
160
161 return B_OK;
162 }
163
164 void
SetParameterValue(int32 id,bigtime_t performance_time,const void * value,size_t size)165 ToneProducer::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
166 {
167 switch (id)
168 {
169 case FREQUENCY_PARAM:
170 case GAIN_PARAM:
171 case WAVEFORM_PARAM:
172 {
173 // floats and int32s are the same size, so we need only check the block's size once
174 if (size > sizeof(float)) size = sizeof(float);
175
176 // submit the parameter change as a performance event, to be handled at the
177 // appropriate time
178 media_timed_event event(performance_time, _PARAMETER_EVENT,
179 NULL, BTimedEventQueue::B_NO_CLEANUP, size, id, (char*) value, size);
180 EventQueue()->AddEvent(event);
181 }
182 break;
183
184 default:
185 break;
186 }
187 }
188
189 // e.moon [17jun99]
StartControlPanel(BMessenger * pMessenger)190 status_t ToneProducer::StartControlPanel(
191 BMessenger* pMessenger) {
192 PRINT(("ToneProducer::StartControlPanel(%p)\n", pMessenger));
193 status_t err = BControllable::StartControlPanel(pMessenger);
194 if(pMessenger && pMessenger->IsValid()) {
195 PRINT(("\tgot valid control panel\n"));
196 }
197
198 return err;
199 }
200
201 //#pragma mark -
202
203 // BBufferProducer methods
204 status_t
FormatSuggestionRequested(media_type type,int32,media_format * format)205 ToneProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/, media_format* format)
206 {
207 // FormatSuggestionRequested() is not necessarily part of the format negotiation
208 // process; it's simply an interrogation -- the caller wants to see what the node's
209 // preferred data format is, given a suggestion by the caller.
210 FPRINTF(stderr, "ToneProducer::FormatSuggestionRequested\n");
211
212 if (!format)
213 {
214 FPRINTF(stderr, "\tERROR - NULL format pointer passed in!\n");
215 return B_BAD_VALUE;
216 }
217
218 // this is the format we'll be returning (our preferred format)
219 *format = mPreferredFormat;
220
221 // a wildcard type is okay; we can specialize it
222 if (type == B_MEDIA_UNKNOWN_TYPE) type = B_MEDIA_RAW_AUDIO;
223
224 // we only support raw audio
225 if (type != B_MEDIA_RAW_AUDIO) return B_MEDIA_BAD_FORMAT;
226 else return B_OK;
227 }
228
229 status_t
FormatProposal(const media_source & output,media_format * format)230 ToneProducer::FormatProposal(const media_source& output, media_format* format)
231 {
232 // FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand
233 // out a suggested format, with wildcards for any variations we support.
234 FPRINTF(stderr, "ToneProducer::FormatProposal\n");
235
236 // is this a proposal for our one output?
237 if (output != mOutput.source)
238 {
239 FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_SOURCE\n");
240 return B_MEDIA_BAD_SOURCE;
241 }
242
243 // we only support floating-point raw audio, so we always return that, but we
244 // supply an error code depending on whether we found the proposal acceptable.
245
246 media_type requestedType = format->type;
247 *format = mPreferredFormat;
248 if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO))
249 {
250 FPRINTF(stderr, "ToneProducer::FormatProposal returning B_MEDIA_BAD_FORMAT\n");
251 return B_MEDIA_BAD_FORMAT;
252 }
253 else return B_OK; // raw audio or wildcard type, either is okay by us
254 }
255
256 status_t
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * io_format,int32 * _deprecated_)257 ToneProducer::FormatChangeRequested(const media_source& source, const media_destination& destination, media_format* io_format, int32* _deprecated_)
258 {
259 FPRINTF(stderr, "ToneProducer::FormatChangeRequested\n");
260
261 // we don't support any other formats, so we just reject any format changes.
262 return B_ERROR;
263 }
264
265 status_t
GetNextOutput(int32 * cookie,media_output * out_output)266 ToneProducer::GetNextOutput(int32* cookie, media_output* out_output)
267 {
268 FPRINTF(stderr, "ToneProducer::GetNextOutput\n");
269
270 // we have only a single output; if we supported multiple outputs, we'd
271 // iterate over whatever data structure we were using to keep track of
272 // them.
273 if (0 == *cookie)
274 {
275 *out_output = mOutput;
276 *cookie += 1;
277 return B_OK;
278 }
279 else return B_BAD_INDEX;
280 }
281
282 status_t
DisposeOutputCookie(int32 cookie)283 ToneProducer::DisposeOutputCookie(int32 cookie)
284 {
285 FPRINTF(stderr, "ToneProducer::DisposeOutputCookie\n");
286
287 // do nothing because we don't use the cookie for anything special
288 return B_OK;
289 }
290
291 status_t
SetBufferGroup(const media_source & for_source,BBufferGroup * newGroup)292 ToneProducer::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup)
293 {
294 FPRINTF(stderr, "ToneProducer::SetBufferGroup\n");
295
296 // verify that we didn't get bogus arguments before we proceed
297 if (for_source != mOutput.source) return B_MEDIA_BAD_SOURCE;
298
299 // Are we being passed the buffer group we're already using?
300 if (newGroup == mBufferGroup) return B_OK;
301
302 // Ahh, someone wants us to use a different buffer group. At this point we delete
303 // the one we are using and use the specified one instead. If the specified group is
304 // NULL, we need to recreate one ourselves, and use *that*. Note that if we're
305 // caching a BBuffer that we requested earlier, we have to Recycle() that buffer
306 // *before* deleting the buffer group, otherwise we'll deadlock waiting for that
307 // buffer to be recycled!
308 delete mBufferGroup; // waits for all buffers to recycle
309 if (newGroup != NULL)
310 {
311 // we were given a valid group; just use that one from now on
312 mBufferGroup = newGroup;
313 }
314 else
315 {
316 // we were passed a NULL group pointer; that means we construct
317 // our own buffer group to use from now on
318 size_t size = mOutput.format.u.raw_audio.buffer_size;
319 int32 count = int32(mLatency / BufferDuration() + 1 + 1);
320 mBufferGroup = new BBufferGroup(size, count);
321 }
322
323 return B_OK;
324 }
325
326 status_t
GetLatency(bigtime_t * out_latency)327 ToneProducer::GetLatency(bigtime_t* out_latency)
328 {
329 FPRINTF(stderr, "ToneProducer::GetLatency\n");
330
331 // report our *total* latency: internal plus downstream plus scheduling
332 *out_latency = EventLatency() + SchedulingLatency();
333 return B_OK;
334 }
335
336 status_t
PrepareToConnect(const media_source & what,const media_destination & where,media_format * format,media_source * out_source,char * out_name)337 ToneProducer::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name)
338 {
339 // PrepareToConnect() is the second stage of format negotiations that happens
340 // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat()
341 // method has been called, and that node has potentially changed the proposed
342 // format. It may also have left wildcards in the format. PrepareToConnect()
343 // *must* fully specialize the format before returning!
344 FPRINTF(stderr, "ToneProducer::PrepareToConnect\n");
345
346 // trying to connect something that isn't our source?
347 if (what != mOutput.source) return B_MEDIA_BAD_SOURCE;
348
349 // are we already connected?
350 if (mOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED;
351
352 // the format may not yet be fully specialized (the consumer might have
353 // passed back some wildcards). Finish specializing it now, and return an
354 // error if we don't support the requested format.
355 if (format->type != B_MEDIA_RAW_AUDIO)
356 {
357 FPRINTF(stderr, "\tnon-raw-audio format?!\n");
358 return B_MEDIA_BAD_FORMAT;
359 }
360 else if (format->u.raw_audio.format != media_raw_audio_format::B_AUDIO_FLOAT)
361 {
362 FPRINTF(stderr, "\tnon-float-audio format?!\n");
363 return B_MEDIA_BAD_FORMAT;
364 }
365 else if(format->u.raw_audio.channel_count > 2) {
366 format->u.raw_audio.channel_count = 2;
367 return B_MEDIA_BAD_FORMAT;
368 }
369
370
371 // !!! validate all other fields except for buffer_size here, because the consumer might have
372 // supplied different values from AcceptFormat()?
373
374 // *** e.moon [11jun99]: filling in sensible field values.
375 // Connect() doesn't take kindly to a frame_rate of 0.
376
377 if(format->u.raw_audio.frame_rate == media_raw_audio_format::wildcard.frame_rate) {
378 format->u.raw_audio.frame_rate = 44100.0f;
379 FPRINTF(stderr, "\tno frame rate provided, suggesting %.1f\n", format->u.raw_audio.frame_rate);
380 }
381 if(format->u.raw_audio.channel_count == media_raw_audio_format::wildcard.channel_count) {
382 //format->u.raw_audio.channel_count = mPreferredFormat.u.raw_audio.channel_count;
383 format->u.raw_audio.channel_count = 1;
384 FPRINTF(stderr, "\tno channel count provided, suggesting %" B_PRIu32 "\n", format->u.raw_audio.channel_count);
385 }
386 if(format->u.raw_audio.byte_order == media_raw_audio_format::wildcard.byte_order) {
387 format->u.raw_audio.byte_order = mPreferredFormat.u.raw_audio.byte_order;
388 FPRINTF(stderr, "\tno channel count provided, suggesting %s\n",
389 (format->u.raw_audio.byte_order == B_MEDIA_BIG_ENDIAN) ? "B_MEDIA_BIG_ENDIAN" : "B_MEDIA_LITTLE_ENDIAN");
390 }
391
392 // check the buffer size, which may still be wildcarded
393 if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size)
394 {
395 format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest
396 FPRINTF(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size);
397 }
398 else
399 {
400 FPRINTF(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size);
401 }
402
403 // Now reserve the connection, and return information about it
404 mOutput.destination = where;
405 mOutput.format = *format;
406 *out_source = mOutput.source;
407 strncpy(out_name, mOutput.name, B_MEDIA_NAME_LENGTH);
408
409 char formatStr[256];
410 string_for_format(*format, formatStr, 255);
411 FPRINTF(stderr, "\treturning format: %s\n", formatStr);
412
413 return B_OK;
414 }
415
416 void
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * io_name)417 ToneProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name)
418 {
419 FPRINTF(stderr, "ToneProducer::Connect\n");
420
421 // If something earlier failed, Connect() might still be called, but with a non-zero
422 // error code. When that happens we simply unreserve the connection and do
423 // nothing else.
424 if (error)
425 {
426 mOutput.destination = media_destination::null;
427 mOutput.format = mPreferredFormat;
428 return;
429 }
430
431 // old workaround for format bug: Connect() receives the format data from the
432 // input returned from BBufferConsumer::Connected().
433 //
434 // char formatStr[256];
435 // string_for_format(format, formatStr, 255);
436 // FPRINTF(stderr, "\trequested format: %s\n", formatStr);
437 // if(format.type != B_MEDIA_RAW_AUDIO) {
438 // // +++++ this is NOT proper behavior
439 // // but it works
440 // FPRINTF(stderr, "\tcorrupted format; falling back to last suggested format\n");
441 // format = mOutput.format;
442 // }
443 //
444
445 // Okay, the connection has been confirmed. Record the destination and format
446 // that we agreed on, and report our connection name again.
447 mOutput.destination = destination;
448 mOutput.format = format;
449 strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH);
450
451 // Now that we're connected, we can determine our downstream latency.
452 // Do so, then make sure we get our events early enough.
453 media_node_id id;
454 FindLatencyFor(mOutput.destination, &mLatency, &id);
455 FPRINTF(stderr, "\tdownstream latency = %" B_PRIdBIGTIME "\n", mLatency);
456
457 // Use a dry run to see how long it takes me to fill a buffer of data
458 bigtime_t start, produceLatency;
459 size_t samplesPerBuffer = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
460 size_t framesPerBuffer = samplesPerBuffer / mOutput.format.u.raw_audio.channel_count;
461 float* data = new float[samplesPerBuffer];
462 mTheta = 0;
463 start = ::system_time();
464 FillSineBuffer(data, framesPerBuffer, mOutput.format.u.raw_audio.channel_count==2);
465 produceLatency = ::system_time();
466 mInternalLatency = produceLatency - start;
467
468 // +++++ e.moon [13jun99]: fiddling with latency, ick
469 mInternalLatency += 20000LL;
470
471 delete [] data;
472 FPRINTF(stderr, "\tbuffer-filling took %" B_PRIdBIGTIME
473 " usec on this machine\n", mInternalLatency);
474 SetEventLatency(mLatency + mInternalLatency);
475
476 // reset our buffer duration, etc. to avoid later calculations
477 // +++++ e.moon 11jun99: crashes w/ divide-by-zero when connecting to LoggingConsumer
478 ASSERT(mOutput.format.u.raw_audio.frame_rate);
479
480 bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate);
481 SetBufferDuration(duration);
482
483 // Set up the buffer group for our connection, as long as nobody handed us a
484 // buffer group (via SetBufferGroup()) prior to this. That can happen, for example,
485 // if the consumer calls SetOutputBuffersFor() on us from within its Connected()
486 // method.
487 if (!mBufferGroup) AllocateBuffers();
488 }
489
490 void
Disconnect(const media_source & what,const media_destination & where)491 ToneProducer::Disconnect(const media_source& what, const media_destination& where)
492 {
493 FPRINTF(stderr, "ToneProducer::Disconnect\n");
494
495 // Make sure that our connection is the one being disconnected
496 if ((where == mOutput.destination) && (what == mOutput.source))
497 {
498 mOutput.destination = media_destination::null;
499 mOutput.format = mPreferredFormat;
500 delete mBufferGroup;
501 mBufferGroup = NULL;
502 }
503 else
504 {
505 FPRINTF(stderr, "\tDisconnect() called with wrong source/destination (%"
506 B_PRId32 "/%" B_PRId32 "), ours is (%" B_PRId32 "/%" B_PRId32 ")\n",
507 what.id, where.id, mOutput.source.id, mOutput.destination.id);
508 }
509 }
510
511 void
LateNoticeReceived(const media_source & what,bigtime_t how_much,bigtime_t performance_time)512 ToneProducer::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time)
513 {
514 FPRINTF(stderr, "ToneProducer::LateNoticeReceived\n");
515
516 // If we're late, we need to catch up. Respond in a manner appropriate to our
517 // current run mode.
518 if (what == mOutput.source)
519 {
520 if (RunMode() == B_RECORDING)
521 {
522 // A hardware capture node can't adjust; it simply emits buffers at
523 // appropriate points. We (partially) simulate this by not adjusting
524 // our behavior upon receiving late notices -- after all, the hardware
525 // can't choose to capture "sooner"....
526 }
527 else if (RunMode() == B_INCREASE_LATENCY)
528 {
529 // We're late, and our run mode dictates that we try to produce buffers
530 // earlier in order to catch up. This argues that the downstream nodes are
531 // not properly reporting their latency, but there's not much we can do about
532 // that at the moment, so we try to start producing buffers earlier to
533 // compensate.
534 mInternalLatency += how_much;
535 if (mInternalLatency > 50000)
536 mInternalLatency = 50000;
537 SetEventLatency(mLatency + mInternalLatency);
538
539 FPRINTF(stderr, "\tincreasing latency to %" B_PRIdBIGTIME "\n",
540 mLatency + mInternalLatency);
541 }
542 else
543 {
544 // The other run modes dictate various strategies for sacrificing data quality
545 // in the interests of timely data delivery. The way *we* do this is to skip
546 // a buffer, which catches us up in time by one buffer duration.
547 size_t nSamples = mOutput.format.u.raw_audio.buffer_size / sizeof(float);
548 mFramesSent += nSamples;
549
550 FPRINTF(stderr, "\tskipping a buffer to try to catch up\n");
551 }
552 }
553 }
554
555 void
EnableOutput(const media_source & what,bool enabled,int32 * _deprecated_)556 ToneProducer::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_)
557 {
558 FPRINTF(stderr, "ToneProducer::EnableOutput\n");
559
560 // If I had more than one output, I'd have to walk my list of output records to see
561 // which one matched the given source, and then enable/disable that one. But this
562 // node only has one output, so I just make sure the given source matches, then set
563 // the enable state accordingly.
564 if (what == mOutput.source)
565 {
566 mOutputEnabled = enabled;
567 }
568 }
569
570 status_t
SetPlayRate(int32 numer,int32 denom)571 ToneProducer::SetPlayRate(int32 numer, int32 denom)
572 {
573 FPRINTF(stderr, "ToneProducer::SetPlayRate\n");
574
575 // Play rates are weird. We don't support them. Maybe we will in a
576 // later newsletter article....
577 return B_ERROR;
578 }
579
580 status_t
HandleMessage(int32 message,const void * data,size_t size)581 ToneProducer::HandleMessage(int32 message, const void* data, size_t size)
582 {
583 FPRINTF(stderr, "ToneProducer::HandleMessage(%" B_PRId32 " = 0x%" B_PRIx32
584 ")\n", message, message);
585 // HandleMessage() is where you implement any private message protocols
586 // that you want to use. When messages are written to your node's control
587 // port that are not recognized by any of the node superclasses, they'll be
588 // passed to this method in your node's implementation for handling. The
589 // ToneProducer node doesn't support any private messages, so we just
590 // return an error, indicating that the message wasn't handled.
591 return B_ERROR;
592 }
593
594 void
AdditionalBufferRequested(const media_source & source,media_buffer_id prev_buffer,bigtime_t prev_time,const media_seek_tag * prev_tag)595 ToneProducer::AdditionalBufferRequested(const media_source& source, media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag* prev_tag)
596 {
597 FPRINTF(stderr, "ToneProducer::AdditionalBufferRequested\n");
598
599 // we don't support offline mode (yet...)
600 return;
601 }
602
603 void
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t new_latency,uint32 flags)604 ToneProducer::LatencyChanged(
605 const media_source& source,
606 const media_destination& destination,
607 bigtime_t new_latency,
608 uint32 flags)
609 {
610 PRINT(("ToneProducer::LatencyChanged(): %" B_PRIdBIGTIME "\n",
611 new_latency));
612
613 // something downstream changed latency, so we need to start producing
614 // buffers earlier (or later) than we were previously. Make sure that the
615 // connection that changed is ours, and adjust to the new downstream
616 // latency if so.
617 if ((source == mOutput.source) && (destination == mOutput.destination))
618 {
619 mLatency = new_latency;
620 SetEventLatency(mLatency + mInternalLatency);
621 }
622 }
623
624 //#pragma mark -
625
626 // BMediaEventLooper methods
627 void
NodeRegistered()628 ToneProducer::NodeRegistered()
629 {
630 FPRINTF(stderr, "ToneProducer::NodeRegistered\n");
631
632 // output init moved to ctor
633 // e.moon [4jun99]
634
635 // Set up our parameter web
636 mWeb = make_parameter_web();
637 SetParameterWeb(mWeb);
638
639 // Start the BMediaEventLooper thread
640 SetPriority(B_REAL_TIME_PRIORITY);
641 Run();
642 }
643
644 void
Start(bigtime_t performance_time)645 ToneProducer::Start(bigtime_t performance_time)
646 {
647 PRINT(("ToneProducer::Start(%" B_PRIdBIGTIME "): now %" B_PRIdBIGTIME "\n",
648 performance_time, TimeSource()->Now()));
649
650 // send 'data available' message
651 if(mOutput.destination != media_destination::null)
652 SendDataStatus(B_DATA_AVAILABLE, mOutput.destination, performance_time);
653
654 // A bug in the current PowerPC compiler demands that we implement
655 // this, even though it just calls up to the inherited implementation.
656 BMediaEventLooper::Start(performance_time);
657 }
658
659 void
Stop(bigtime_t performance_time,bool immediate)660 ToneProducer::Stop(bigtime_t performance_time, bool immediate)
661 {
662 // send 'data not available' message
663 if(mOutput.destination != media_destination::null) {
664 printf("ToneProducer: B_PRODUCER_STOPPED at %" B_PRIdBIGTIME "\n",
665 performance_time);
666 SendDataStatus(B_PRODUCER_STOPPED, mOutput.destination, performance_time);
667 }
668
669 // A bug in the current PowerPC compiler demands that we implement
670 // this, even though it just calls up to the inherited implementation.
671 BMediaEventLooper::Stop(performance_time, immediate);
672 }
673
674 void
SetRunMode(run_mode mode)675 ToneProducer::SetRunMode(run_mode mode)
676 {
677 FPRINTF(stderr, "ToneProducer::SetRunMode\n");
678
679 // We don't support offline run mode, so broadcast an error if we're set to
680 // B_OFFLINE. Unfortunately, we can't actually reject the mode change...
681 if (B_OFFLINE == mode)
682 {
683 ReportError(B_NODE_FAILED_SET_RUN_MODE);
684 }
685 }
686
687 void
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)688 ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent)
689 {
690 // FPRINTF(stderr, "ToneProducer::HandleEvent\n");
691 switch (event->type)
692 {
693 case BTimedEventQueue::B_START:
694 // don't do anything if we're already running
695 if (RunState() != B_STARTED)
696 {
697 // We want to start sending buffers now, so we set up the buffer-sending bookkeeping
698 // and fire off the first "produce a buffer" event.
699 mFramesSent = 0;
700 mTheta = 0;
701 mStartTime = event->event_time;
702 media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER);
703
704 // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through
705 // the event queue, like this:
706 //
707 // this->HandleEvent(&firstBufferEvent, 0, false);
708 //
709 EventQueue()->AddEvent(firstBufferEvent);
710 }
711 break;
712
713 case BTimedEventQueue::B_STOP:
714 FPRINTF(stderr, "Handling B_STOP event\n");
715
716 // When we handle a stop, we must ensure that downstream consumers don't
717 // get any more buffers from us. This means we have to flush any pending
718 // buffer-producing events from the queue.
719 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
720 break;
721
722 case _PARAMETER_EVENT:
723 {
724 size_t dataSize = size_t(event->data);
725 int32 param = int32(event->bigdata);
726 if (dataSize >= sizeof(float)) switch (param)
727 {
728 case FREQUENCY_PARAM:
729 {
730 float newValue = *((float*) event->user_data);
731 if (mFrequency != newValue) // an actual change in the value?
732 {
733 mFrequency = newValue;
734 mFreqLastChanged = TimeSource()->Now();
735 BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency));
736 }
737 }
738 break;
739
740 case GAIN_PARAM:
741 {
742 float newValue = *((float*) event->user_data);
743 if (mGain != newValue)
744 {
745 mGain = newValue;
746 mGainLastChanged = TimeSource()->Now();
747 BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain));
748 }
749 }
750 break;
751
752 case WAVEFORM_PARAM:
753 {
754 int32 newValue = *((int32*) event->user_data);
755 if (mWaveform != newValue)
756 {
757 mWaveform = newValue;
758 mTheta = 0; // reset the generator parameters when we change waveforms
759 mWaveAscending = true;
760 mWaveLastChanged = TimeSource()->Now();
761 BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform));
762 }
763 }
764 break;
765
766 default:
767 FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%" B_PRId32 ")\n", param);
768 break;
769 }
770 }
771 break;
772
773 case BTimedEventQueue::B_HANDLE_BUFFER:
774 {
775 // make sure we're both started *and* connected before delivering a buffer
776 if (RunState() == BMediaEventLooper::B_STARTED
777 && mOutput.destination != media_destination::null) {
778 // Get the next buffer of data
779 BBuffer* buffer = FillNextBuffer(event->event_time);
780 if (buffer) {
781 // send the buffer downstream if and only if output is enabled
782 status_t err = B_ERROR;
783 if (mOutputEnabled) {
784 err = SendBuffer(buffer, mOutput.source,
785 mOutput.destination);
786 }
787 if (err) {
788 // we need to recycle the buffer ourselves if output is disabled or
789 // if the call to SendBuffer() fails
790 buffer->Recycle();
791 }
792 }
793
794 // track how much media we've delivered so far
795 size_t nFrames = mOutput.format.u.raw_audio.buffer_size /
796 (sizeof(float) * mOutput.format.u.raw_audio.channel_count);
797 mFramesSent += nFrames;
798
799 // The buffer is on its way; now schedule the next one to go
800 bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent)
801 / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
802 media_timed_event nextBufferEvent(nextEvent,
803 BTimedEventQueue::B_HANDLE_BUFFER);
804 EventQueue()->AddEvent(nextBufferEvent);
805 }
806 }
807 break;
808
809 default:
810 break;
811 }
812 }
813
814 //#pragma mark -
815
816 void
AllocateBuffers()817 ToneProducer::AllocateBuffers()
818 {
819 FPRINTF(stderr, "ToneProducer::AllocateBuffers\n");
820
821 // allocate enough buffers to span our downstream latency, plus one
822 size_t size = mOutput.format.u.raw_audio.buffer_size;
823 int32 count = int32(mLatency / BufferDuration() + 1 + 1);
824
825 FPRINTF(stderr, "\tlatency = %" B_PRIdBIGTIME ", buffer duration = %"
826 B_PRIdBIGTIME "\n", mLatency, BufferDuration());
827 FPRINTF(stderr, "\tcreating group of %" B_PRId32 " buffers, size = %"
828 B_PRIuSIZE "\n", count, size);
829 mBufferGroup = new BBufferGroup(size, count);
830 }
831
832 BBuffer*
FillNextBuffer(bigtime_t event_time)833 ToneProducer::FillNextBuffer(bigtime_t event_time)
834 {
835 // get a buffer from our buffer group
836 BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration());
837
838 // if we fail to get a buffer (for example, if the request times out), we skip this
839 // buffer and go on to the next, to avoid locking up the control thread
840 if (!buf)
841 {
842 return NULL;
843 }
844
845 // now fill it with data, continuing where the last buffer left off
846 // 20sep99: multichannel support
847
848 size_t numFrames =
849 mOutput.format.u.raw_audio.buffer_size /
850 (sizeof(float)*mOutput.format.u.raw_audio.channel_count);
851 bool stereo = (mOutput.format.u.raw_audio.channel_count == 2);
852 if(!stereo) {
853 ASSERT(mOutput.format.u.raw_audio.channel_count == 1);
854 }
855 // PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono"));
856
857 float* data = (float*) buf->Data();
858
859 switch (mWaveform)
860 {
861 case SINE_WAVE:
862 FillSineBuffer(data, numFrames, stereo);
863 break;
864
865 case TRIANGLE_WAVE:
866 FillTriangleBuffer(data, numFrames, stereo);
867 break;
868
869 case SAWTOOTH_WAVE:
870 FillSawtoothBuffer(data, numFrames, stereo);
871 break;
872 }
873
874 // fill in the buffer header
875 media_header* hdr = buf->Header();
876 hdr->type = B_MEDIA_RAW_AUDIO;
877 hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
878 hdr->time_source = TimeSource()->ID();
879
880 bigtime_t stamp;
881 if (RunMode() == B_RECORDING)
882 {
883 // In B_RECORDING mode, we stamp with the capture time. We're not
884 // really a hardware capture node, but we simulate it by using the (precalculated)
885 // time at which this buffer "should" have been created.
886 stamp = event_time;
887 }
888 else
889 {
890 // okay, we're in one of the "live" performance run modes. in these modes, we
891 // stamp the buffer with the time at which the buffer should be rendered to the
892 // output, not with the capture time. mStartTime is the cached value of the
893 // first buffer's performance time; we calculate this buffer's performance time as
894 // an offset from that time, based on the amount of media we've created so far.
895 // Recalculating every buffer like this avoids accumulation of error.
896 stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0);
897 }
898 hdr->start_time = stamp;
899
900 return buf;
901 }
902
903 // waveform generators - fill buffers with various waveforms
904 void
FillSineBuffer(float * data,size_t numFrames,bool stereo)905 ToneProducer::FillSineBuffer(float *data, size_t numFrames, bool stereo)
906 {
907
908
909 // cover 2pi radians in one period
910 double dTheta = 2*M_PI * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
911
912 // Fill the buffer!
913 for (size_t i = 0; i < numFrames; i++, data++)
914 {
915 float val = mGain * float(sin(mTheta));
916 *data = val;
917 if(stereo) {
918 ++data;
919 *data = val;
920 }
921
922 mTheta += dTheta;
923 if (mTheta > 2*M_PI)
924 {
925 mTheta -= 2*M_PI;
926 }
927 }
928 }
929
930 void
FillTriangleBuffer(float * data,size_t numFrames,bool stereo)931 ToneProducer::FillTriangleBuffer(float *data, size_t numFrames, bool stereo)
932 {
933 // ramp from -1 to 1 and back in one period
934 double dTheta = 4.0 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
935 if (!mWaveAscending) dTheta = -dTheta;
936
937 // fill the buffer!
938 for (size_t i = 0; i < numFrames; i++, data++)
939 {
940 float val = mGain * mTheta;
941 *data = val;
942 if(stereo) {
943 ++data;
944 *data = val;
945 }
946
947 mTheta += dTheta;
948 if (mTheta >= 1)
949 {
950 mTheta = 2 - mTheta; // reflect across the mTheta=1 line to preserve drift
951 mWaveAscending = false;
952 dTheta = -dTheta;
953 }
954 else if (mTheta <= -1)
955 {
956 mTheta = -2 - mTheta; // reflect across mTheta=-1
957 mWaveAscending = true;
958 dTheta = -dTheta;
959 }
960 }
961 }
962
963 void
FillSawtoothBuffer(float * data,size_t numFrames,bool stereo)964 ToneProducer::FillSawtoothBuffer(float *data, size_t numFrames, bool stereo)
965 {
966 // ramp from -1 to 1 in one period
967 double dTheta = 2 * double(mFrequency) / mOutput.format.u.raw_audio.frame_rate;
968 mWaveAscending = true;
969
970 // fill the buffer!
971 for (size_t i = 0; i < numFrames; i++, data++)
972 {
973 float val = mGain * mTheta;
974 *data = val;
975 if(stereo) {
976 ++data;
977 *data = val;
978 }
979
980 mTheta += dTheta;
981 if (mTheta > 1)
982 {
983 mTheta -= 2; // back to the base of the sawtooth, including cumulative drift
984 }
985 }
986 }
987
988 // utility - build the ToneProducer's parameter web
make_parameter_web()989 static BParameterWeb* make_parameter_web()
990 {
991 FPRINTF(stderr, "make_parameter_web() called\n");
992
993 BParameterWeb* web = new BParameterWeb;
994 BParameterGroup* mainGroup = web->MakeGroup("Tone Generator Parameters");
995
996 BParameterGroup* group = mainGroup->MakeGroup("Frequency");
997 BParameter* nullParam = group->MakeNullParameter(FREQUENCY_NULL_PARAM, B_MEDIA_NO_TYPE, "Frequency", B_GENERIC);
998 BContinuousParameter* param = group->MakeContinuousParameter(FREQUENCY_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "Hz", 0, 2500, 0.1);
999 nullParam->AddOutput(param);
1000 param->AddInput(nullParam);
1001
1002 group = mainGroup->MakeGroup("Amplitude");
1003 nullParam = group->MakeNullParameter(GAIN_NULL_PARAM, B_MEDIA_NO_TYPE, "Amplitude", B_GENERIC);
1004 param = group->MakeContinuousParameter(GAIN_PARAM, B_MEDIA_NO_TYPE, "", B_GAIN, "", 0, 1, 0.01);
1005 nullParam->AddOutput(param);
1006 param->AddInput(nullParam);
1007
1008 group = mainGroup->MakeGroup("Waveform");
1009 nullParam = group->MakeNullParameter(WAVEFORM_NULL_PARAM, B_MEDIA_NO_TYPE, "Waveform", B_GENERIC);
1010 BDiscreteParameter* waveParam = group->MakeDiscreteParameter(WAVEFORM_PARAM, B_MEDIA_NO_TYPE, "", B_GENERIC);
1011 waveParam->AddItem(SINE_WAVE, "Sine wave");
1012 waveParam->AddItem(TRIANGLE_WAVE, "Triangle");
1013 waveParam->AddItem(SAWTOOTH_WAVE, "Sawtooth");
1014 nullParam->AddOutput(waveParam);
1015 waveParam->AddInput(nullParam);
1016
1017 return web;
1018 }
1019