/* * Copyright (c) 1999-2000, Eric Moon. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions, and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // AudioFilterNode.h // * PURPOSE // A framework class designed to make it easy to develop simple // audio filters in the BeOS Media Kit. The actual DSP work // is done externally by an IAudioOp object. // // * NOT SUPPORTED YET... // - multiple inputs or outputs // - chaining multiple operations (yum) // // * HISTORY // e.moon 7sep99 Begun: abstracting from FlangerNode and // implementing IAudioOp. #ifndef __AudioFilterNode_H__ #define __AudioFilterNode_H__ #include #include #include #include #include #include "IAudioOpHost.h" // forwards class BBufferGroup; class BMediaAddOn; class AudioBuffer; class IAudioOpFactory; class IAudioOp; class IParameterSet; class AudioFilterNode : public BBufferConsumer, public BBufferProducer, public BControllable, public BMediaEventLooper, public IAudioOpHost { public: // *** HOOKS // *** FORMAT NEGOTIATION // +++++ 8sep99: there's currently no differentiation made between input // and output formats. there probably should be, though for // now you can do extra format restriction in your factory's // createOp() implementation... which is too late. // +++++ // +++++ okay, time to split out into getRequiredInputFormat(), ... etc // subclasses allowing format conversion will need to restrict buffer // sizes based on current connection (input or output.) // requests the required format for the given type (ioFormat.type must be // filled in!) // upon returning, any fields left as wildcards are negotiable. // Default implementation hands off to getPreferredFormat(), // yielding a rather strict node. virtual status_t getRequiredInputFormat( media_format& ioFormat) { return getPreferredInputFormat(ioFormat); } virtual status_t getRequiredOutputFormat( media_format& ioFormat) { return getPreferredOutputFormat(ioFormat); } // requests the required format for the given type (ioFormat.type must be // filled in!) // upon returning, all fields must be filled in. // // Default raw audio format: // 44100hz // host-endian // 1 channel // float // 1k buffers // virtual status_t getPreferredInputFormat( media_format& ioFormat); virtual status_t getPreferredOutputFormat( media_format& ioFormat); // test the given template format against a proposed format. // specialize wildcards for fields where the template contains // non-wildcard data; write required fields into proposed format // if they mismatch. // Returns B_OK if the proposed format doesn't conflict with the // template, or B_MEDIA_BAD_FORMAT otherwise. virtual status_t validateProposedInputFormat( const media_format& preferredFormat, media_format& ioProposedFormat); virtual status_t validateProposedOutputFormat( const media_format& preferredFormat, media_format& ioProposedFormat); // fill in wildcards in the given format. // (assumes the format passes validateProposedFormat().) // Default implementation: specializes all wildcards to format returned by // getPreferredXXXFormat() virtual void specializeInputFormat( media_format& ioFormat) { media_format preferred; preferred.type = B_MEDIA_RAW_AUDIO; #ifdef DEBUG status_t err = #endif getPreferredInputFormat(preferred); ASSERT(err == B_OK); _specialize_raw_audio_format(preferred, ioFormat); } virtual void specializeOutputFormat( media_format& ioFormat) { char fmt_buffer[256]; string_for_format(ioFormat, fmt_buffer, 255); PRINT(( "### specializeOutputFormat:\n" " given '%s'\n", fmt_buffer)); media_format preferred; preferred.type = B_MEDIA_RAW_AUDIO; #ifdef DEBUG status_t err = #endif getPreferredOutputFormat(preferred); ASSERT(err == B_OK); string_for_format(preferred, fmt_buffer, 255); PRINT(( " pref '%s'\n", fmt_buffer)); // ioFormat.SpecializeTo(&preferred); _specialize_raw_audio_format(preferred, ioFormat); string_for_format(ioFormat, fmt_buffer, 255); PRINT(( " writing '%s'\n", fmt_buffer)); } public: // *** ctor/dtor virtual ~AudioFilterNode(); // the node acquires ownership of opFactory AudioFilterNode( const char* name, IAudioOpFactory* opFactory, BMediaAddOn* addOn=0); public: // *** accessors const media_input& input() const { return m_input; } const media_output& output() const { return m_output; } public: // *** BMediaNode virtual status_t HandleMessage( int32 code, const void* data, size_t size); virtual BMediaAddOn* AddOn( int32* outID) const; virtual void SetRunMode( run_mode mode); protected: // *** BMediaEventLooper virtual void HandleEvent( const media_timed_event* event, bigtime_t howLate, bool realTimeEvent=false); // "The Media Server calls this hook function after the node has // been registered. This is derived from BMediaNode; BMediaEventLooper // implements it to call Run() automatically when the node is registered; // if you implement NodeRegistered() you should call through to // BMediaEventLooper::NodeRegistered() after you've done your custom // operations." virtual void NodeRegistered(); // "Augment OfflineTime() to compute the node's current time; it's called // by the Media Kit when it's in offline mode. Update any appropriate // internal information as well, then call through to the BMediaEventLooper // implementation." virtual bigtime_t OfflineTime(); //nyi public: // *** BBufferConsumer virtual status_t AcceptFormat( const media_destination& destination, media_format* ioFormat); // "If you're writing a node, and receive a buffer with the B_SMALL_BUFFER // flag set, you must recycle the buffer before returning." virtual void BufferReceived( BBuffer* buffer); // * make sure to fill in poInput->format with the contents of // pFormat; as of R4.5 the Media Kit passes poInput->format to // the producer in BBufferProducer::Connect(). virtual status_t Connected( const media_source& source, const media_destination& destination, const media_format& format, media_input* outInput); virtual void Disconnected( const media_source& source, const media_destination& destination); virtual void DisposeInputCookie( int32 cookie); // "You should implement this function so your node will know that the data // format is going to change. Note that this may be called in response to // your AcceptFormat() call, if your AcceptFormat() call alters any wildcard // fields in the specified format. // // Because FormatChanged() is called by the producer, you don't need to (and // shouldn't) ask it if the new format is acceptable. // // If the format change isn't possible, return an appropriate error from // FormatChanged(); this error will be passed back to the producer that // initiated the new format negotiation in the first place." virtual status_t FormatChanged( const media_source& source, const media_destination& destination, int32 changeTag, const media_format& newFormat); virtual status_t GetLatencyFor( const media_destination& destination, bigtime_t* outLatency, media_node_id* outTimeSource); virtual status_t GetNextInput( int32* ioCookie, media_input* outInput); virtual void ProducerDataStatus( const media_destination& destination, int32 status, bigtime_t tpWhen); // "This function is provided to aid in supporting media formats in which the // outer encapsulation layer doesn't supply timing information. Producers will // tag the buffers they generate with seek tags; these tags can be used to // locate key frames in the media data." virtual status_t SeekTagRequested( const media_destination& destination, bigtime_t targetTime, uint32 flags, media_seek_tag* outSeekTag, bigtime_t* outTaggedTime, uint32* outFlags); public: // *** BBufferProducer // "When a consumer calls BBufferConsumer::RequestAdditionalBuffer(), this // function is called as a result. Its job is to call SendBuffer() to // immediately send the next buffer to the consumer. The previousBufferID, // previousTime, and previousTag arguments identify the last buffer the // consumer received. Your node should respond by sending the next buffer // after the one described. // // The previousTag may be NULL. // Return B_OK if all is well; otherwise return an appropriate error code." virtual void AdditionalBufferRequested( const media_source& source, media_buffer_id previousBufferID, bigtime_t previousTime, const media_seek_tag* previousTag); //nyi virtual void Connect( status_t status, const media_source& source, const media_destination& destination, const media_format& format, char* ioName); virtual void Disconnect( const media_source& source, const media_destination& destination); virtual status_t DisposeOutputCookie( int32 cookie); virtual void EnableOutput( const media_source& source, bool enabled, int32* _deprecated_); virtual status_t FormatChangeRequested( const media_source& source, const media_destination& destination, media_format* ioFormat, int32* _deprecated_); //nyi virtual status_t FormatProposal( const media_source& source, media_format* ioFormat); virtual status_t FormatSuggestionRequested( media_type type, int32 quality, media_format* outFormat); virtual status_t GetLatency( bigtime_t* outLatency); virtual status_t GetNextOutput( int32* ioCookie, media_output* outOutput); // "This hook function is called when a BBufferConsumer that's receiving data // from you determines that its latency has changed. It will call its // BBufferConsumer::SendLatencyChange() function, and in response, the Media // Server will call your LatencyChanged() function. The source argument // indicates your output that's involved in the connection, and destination // specifies the input on the consumer to which the connection is linked. // newLatency is the consumer's new latency. The flags are currently unused." virtual void LatencyChanged( const media_source& source, const media_destination& destination, bigtime_t newLatency, uint32 flags); virtual void LateNoticeReceived( const media_source& source, bigtime_t howLate, bigtime_t tpWhen); // PrepareToConnect() is the second stage of format negotiations that happens // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() // method has been called, and that node has potentially changed the proposed // format. It may also have left wildcards in the format. PrepareToConnect() // *must* fully specialize the format before returning! virtual status_t PrepareToConnect( const media_source& source, const media_destination& destination, media_format* ioFormat, media_source* outSource, char* outName); virtual status_t SetBufferGroup( const media_source& source, BBufferGroup* group); virtual status_t SetPlayRate( int32 numerator, int32 denominator); virtual status_t VideoClippingChanged( const media_source& source, int16 numShorts, int16* clipData, const media_video_display_info& display, int32* outFromChangeTag); public: // *** BControllable virtual status_t GetParameterValue( int32 id, bigtime_t* outLastChangeTime, void* outValue, size_t* ioSize); virtual void SetParameterValue( int32 id, bigtime_t changeTime, const void* value, size_t size); public: // *** IAudioOpHost virtual IParameterSet* parameterSet() const; protected: // HandleEvent() impl. void handleParameterEvent( const media_timed_event* event); void handleStartEvent( const media_timed_event* event); void handleStopEvent( const media_timed_event* event); void ignoreEvent( const media_timed_event* event); protected: // *** internal operations status_t prepareFormatChange( const media_format& newFormat); void doFormatChange( const media_format& newFormat); // create and register a parameter web // +++++ 7sep99: hand off to IParameterSet::makeGroup() virtual void initParameterWeb(); // [re-]initialize operation if necessary virtual void updateOperation(); // create or discard buffer group if necessary virtual void updateBufferGroup(); // // construct delay line if necessary, reset filter state // virtual void initFilter(); // // virtual void startFilter(); // virtual void stopFilter(); // figure processing latency by doing 'dry runs' of processBuffer() virtual bigtime_t calcProcessingLatency(); // filter buffer data; inputBuffer and outputBuffer may be the same virtual void processBuffer( BBuffer* inputBuffer, BBuffer* outputBuffer); status_t _validate_raw_audio_format( const media_format& preferredFormat, media_format& ioProposedFormat); void _specialize_raw_audio_format( const media_format& templateFormat, media_format& ioFormat); private: // *** connection/format members // +++++ expose input & output to subclasses [8sep99] // Connections & associated state variables media_input m_input; media_output m_output; bool m_outputEnabled; // Time required by downstream consumer(s) to properly deliver a buffer bigtime_t m_downstreamLatency; // Worst-case time needed to fill a buffer bigtime_t m_processingLatency; // buffer group for on-the-fly conversion [8sep99] // (only created when necessary, ie. more data going out than coming in) BBufferGroup* m_bufferGroup; private: // *** parameters IParameterSet* m_parameterSet; private: // *** operations IAudioOpFactory* m_opFactory; // the current operation IAudioOp* m_op; private: // *** add-on stuff // host add-on BMediaAddOn* m_addOn; }; #endif /*__AudioFilterNode_H__*/