From 71c77851c93a10711107777f0325db11f1845923 Mon Sep 17 00:00:00 2001 From: threedeyes <3dEyes@gmail.com> Date: Sat, 13 Oct 2012 17:42:09 +1100 Subject: [PATCH] Added Equalizer media add-on. --- src/add-ons/media/media-add-ons/Jamfile | 1 + .../media-add-ons/equalizer/Equalizer.cpp | 155 ++++ .../media/media-add-ons/equalizer/Equalizer.h | 46 ++ .../media-add-ons/equalizer/Equalizer.rdef | 15 + .../equalizer/EqualizerAddOn.cpp | 95 +++ .../media-add-ons/equalizer/EqualizerAddOn.h | 26 + .../media-add-ons/equalizer/EqualizerNode.cpp | 721 ++++++++++++++++++ .../media-add-ons/equalizer/EqualizerNode.h | 148 ++++ .../media/media-add-ons/equalizer/Jamfile | 10 + 9 files changed, 1217 insertions(+) create mode 100644 src/add-ons/media/media-add-ons/equalizer/Equalizer.cpp create mode 100644 src/add-ons/media/media-add-ons/equalizer/Equalizer.h create mode 100644 src/add-ons/media/media-add-ons/equalizer/Equalizer.rdef create mode 100644 src/add-ons/media/media-add-ons/equalizer/EqualizerAddOn.cpp create mode 100644 src/add-ons/media/media-add-ons/equalizer/EqualizerAddOn.h create mode 100644 src/add-ons/media/media-add-ons/equalizer/EqualizerNode.cpp create mode 100644 src/add-ons/media/media-add-ons/equalizer/EqualizerNode.h create mode 100644 src/add-ons/media/media-add-ons/equalizer/Jamfile diff --git a/src/add-ons/media/media-add-ons/Jamfile b/src/add-ons/media/media-add-ons/Jamfile index b6dd98ae47..656bc2844f 100644 --- a/src/add-ons/media/media-add-ons/Jamfile +++ b/src/add-ons/media/media-add-ons/Jamfile @@ -17,4 +17,5 @@ SubInclude HAIKU_TOP src add-ons media media-add-ons usb_webcam ; SubInclude HAIKU_TOP src add-ons media media-add-ons video_producer_demo ; SubInclude HAIKU_TOP src add-ons media media-add-ons videowindow ; SubInclude HAIKU_TOP src add-ons media media-add-ons video_mixer ; +SubInclude HAIKU_TOP src add-ons media media-add-ons equalizer ; diff --git a/src/add-ons/media/media-add-ons/equalizer/Equalizer.cpp b/src/add-ons/media/media-add-ons/equalizer/Equalizer.cpp new file mode 100644 index 0000000000..c0a516687c --- /dev/null +++ b/src/add-ons/media/media-add-ons/equalizer/Equalizer.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include + +#include "Equalizer.h" + +Equalizer::Equalizer() +{ + fActivated = false; + CleanUp(); +} + +Equalizer::~Equalizer() +{ +} + +void +Equalizer::SetFormat(int channels, float framerate) +{ + fChannels = channels; + fRate = framerate; + + memset(fWDataVector[0][0], 0, sizeof(fWDataVector)); + + fActiveBands = BandCount(); + while(fFrequency[fActiveBands-1] * 2.0 > fRate) { + fActiveBands--; + } + + for(int i = 0; i < fActiveBands; i++) { + BandPassFilterCalcs(fAWeights[i], fBWeights[i], + fFrequency[i] / (float)fRate); + } +} + +void +Equalizer::SetPreAmp(double value) +{ + fPreAmp = value; + RecalcGains(); +} + +double +Equalizer::PreAmp(void) +{ + return fPreAmp; +} + +void +Equalizer::SetBand(int band, double value) +{ + if(band < 0 || band >= BandCount()) { + return; + } + fBands[band] = value; + RecalcGains(); +} + +double +Equalizer::Band(int band) +{ + if(band < 0 || band >= BandCount()) { + return 0.0; + } + return fBands[band]; +} + +int +Equalizer::BandCount(void) +{ + return EQ_BANDS; +} + +float +Equalizer::BandFrequency(int band) +{ + if(band < 0 || band >= BandCount()) { + return 0.0; + } + return fFrequency[band]; +} + +void +Equalizer::ProcessBuffer(float *buffer, int samples) +{ + if(!fActivated || samples <= 0) { + return; + } + + for(int i = 0; i < fChannels; i++) { + float *g = fGainVector[i]; + float *end = buffer + samples; + for(float *fptr = buffer + i; fptr < end; fptr += fChannels) { + float yt = *fptr; + for (int k = 0; k < fActiveBands; k ++) { + float *Wq = fWDataVector[i][k]; + float w = yt * fBWeights[k][0] + + Wq[0] * fAWeights[k][0] + + Wq[1] * fAWeights[k][1]; + yt += (w + Wq[1] * fBWeights[k][1]) * g[k]; + Wq[1] = Wq[0]; + Wq[0] = w; + } + *fptr = yt; + } + } +} + +void +Equalizer::CleanUp() +{ + float freq[EQ_BANDS] = {31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000}; + + for(int i = 0; i < BandCount(); i++) { + fFrequency[i] = freq[i]; + fBands[i] = 0.0; + } + + fPreAmp = 0.0; + RecalcGains(); +} + +void +Equalizer::RecalcGains(void) +{ + float adjust[EQ_BANDS]; + for(int i = 0; i < BandCount(); i++) { + adjust[i] = fPreAmp + fBands[i]; + } + + for(int c = 0; c < MAX_CHANNELS; c++) { + for(int i = 0; i + +#include "EqualizerNode.h" +#include "EqualizerAddOn.h" + +extern "C" _EXPORT BMediaAddOn* make_media_addon(image_id image) +{ + return new EqualizerAddOn(image); +} + +EqualizerAddOn::~EqualizerAddOn() +{ +} + +EqualizerAddOn::EqualizerAddOn(image_id image) : BMediaAddOn(image) +{ +} + +status_t +EqualizerAddOn::InitCheck(const char **text) +{ + return B_OK; +} + +int32 +EqualizerAddOn::CountFlavors() +{ + return 1; +} + +status_t EqualizerAddOn::GetFlavorAt(int32 idx, const flavor_info **info) +{ + if(idx < 0 || idx >= CountFlavors()) + return B_ERROR; + + flavor_info *f_info = new flavor_info; + f_info->internal_id = idx; + f_info->kinds = B_BUFFER_CONSUMER | B_BUFFER_PRODUCER | B_CONTROLLABLE; + f_info->possible_count = 0; + f_info->flavor_flags = 0; + f_info->info = (char *)"10 Band Equalizer.\nby 3dEyes**"; + f_info->name = (char *)"Equalizer (10 Band)"; + + media_format *format = new media_format; + format->type = B_MEDIA_RAW_AUDIO; + format->u.raw_audio = media_raw_audio_format::wildcard; + format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + + f_info->in_format_count = 1; + f_info->in_formats = format; + + format = new media_format; + format->type = B_MEDIA_RAW_AUDIO; + format->u.raw_audio = media_raw_audio_format::wildcard; + format->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + + f_info->out_format_count = 1; + f_info->out_formats = format; + + *info = f_info; + + return B_OK; +} + +BMediaNode* +EqualizerAddOn::InstantiateNodeFor(const flavor_info *info, BMessage *config, + status_t *err) +{ + EqualizerNode *node = new EqualizerNode(this); + return node; +} + +status_t +EqualizerAddOn::GetConfigurationFor(BMediaNode *node, BMessage *message) +{ + return B_OK; +} + +bool +EqualizerAddOn::WantsAutoStart() +{ + return false; +} + +status_t +EqualizerAddOn::AutoStart(int count, BMediaNode **node, int32 *id, bool *more) +{ + return B_OK; +} + diff --git a/src/add-ons/media/media-add-ons/equalizer/EqualizerAddOn.h b/src/add-ons/media/media-add-ons/equalizer/EqualizerAddOn.h new file mode 100644 index 0000000000..e45f96fedb --- /dev/null +++ b/src/add-ons/media/media-add-ons/equalizer/EqualizerAddOn.h @@ -0,0 +1,26 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef __EQUALIZER_ADDON_H__ +#define __EQUALIZER_ADDON_H__ + +#include + +class EqualizerAddOn : public BMediaAddOn +{ +public: + virtual ~EqualizerAddOn(); + explicit EqualizerAddOn(image_id image); + virtual status_t InitCheck(const char **text); + virtual int32 CountFlavors(); + virtual status_t GetFlavorAt(int32 idx, const flavor_info **info); + virtual BMediaNode* InstantiateNodeFor(const flavor_info *info, BMessage *config, + status_t *err); + virtual status_t GetConfigurationFor(BMediaNode *node, BMessage *message); + virtual bool WantsAutoStart(); + virtual status_t AutoStart(int count, BMediaNode **node, int32 *id, bool *more); +}; + +#endif //__EQUALIZER_ADDON_H__ diff --git a/src/add-ons/media/media-add-ons/equalizer/EqualizerNode.cpp b/src/add-ons/media/media-add-ons/equalizer/EqualizerNode.cpp new file mode 100644 index 0000000000..79eb6fa6c5 --- /dev/null +++ b/src/add-ons/media/media-add-ons/equalizer/EqualizerNode.cpp @@ -0,0 +1,721 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "EqualizerNode.h" + +//EqualizerNode +EqualizerNode::~EqualizerNode() { + Quit(); +} + +EqualizerNode::EqualizerNode(BMediaAddOn* addon) : + BMediaNode("10 Band Equalizer"), + BBufferConsumer(B_MEDIA_RAW_AUDIO), + BBufferProducer(B_MEDIA_RAW_AUDIO), + BControllable(), + BMediaEventLooper(), + fAddOn(addon), + fProcessLatency(0), + fDownstreamLatency(0), + fOutputMediaEnabled(true) +{ +} + +//BMediaNode +BMediaAddOn* +EqualizerNode::AddOn(int32 *id) const +{ + if(fAddOn) + *id = 0; + return fAddOn; +} + +status_t +EqualizerNode::HandleMessage(int32 message, const void *data, size_t size) +{ + if((BControllable::HandleMessage(message, data, size)!=B_OK) && + (BBufferConsumer::HandleMessage(message, data, size) != B_OK) && + (BBufferProducer::HandleMessage(message, data, size) != B_OK) && + (BControllable::HandleMessage(message, data, size) != B_OK) ) { + BMediaNode::HandleMessage(message, data, size); + return B_OK; + } else { + BMediaNode::HandleBadMessage(message, data, size); + return B_ERROR; + } +} + +void +EqualizerNode::NodeRegistered() +{ + SetPriority(B_REAL_TIME_PRIORITY); + Run(); + + fPreferredFormat.type = B_MEDIA_RAW_AUDIO; + fPreferredFormat.u.raw_audio.buffer_size = BUFF_SIZE; + fPreferredFormat.u.raw_audio = media_raw_audio_format::wildcard; + fPreferredFormat.u.raw_audio.channel_count = media_raw_audio_format::wildcard.channel_count; + fPreferredFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + + fFormat.type = B_MEDIA_RAW_AUDIO; + fFormat.u.raw_audio = media_raw_audio_format::wildcard; + + fInputMedia.destination.port = ControlPort(); + fInputMedia.destination.id = ID_AUDIO_INPUT; + fInputMedia.node = Node(); + fInputMedia.source = media_source::null; + fInputMedia.format = fFormat; + strncpy(fInputMedia.name, "Audio Input", B_MEDIA_NAME_LENGTH); + + fOutputMedia.source.port = ControlPort(); + fOutputMedia.source.id = ID_AUDIO_OUTPUT; + fOutputMedia.node = Node(); + fOutputMedia.destination = media_destination::null; + fOutputMedia.format = fFormat; + strncpy(fOutputMedia.name, "Audio Output", B_MEDIA_NAME_LENGTH); + + InitParameterValues(); + InitParameterWeb(); +} + +//BControllable +status_t +EqualizerNode::GetParameterValue(int32 id, bigtime_t *lastChangeTime, void *value, size_t *size) +{ + if(*size < sizeof(float)) { + return B_NO_MEMORY; + } + + if(id == P_MUTE) { + *(int32*)value = fMute; + *lastChangeTime = fMuteLastChanged; + *size = sizeof(int32); + return B_OK; + } else if(id == P_BYPASS) { + *(int32*)value = fByPass; + *lastChangeTime = fByPassLastChanged; + *size = sizeof(int32); + return B_OK; + } else if(id == P_PREAMP) { + *(float*)value = (float)fEqualizer.PreAmp(); + *lastChangeTime = fPreAmpLastChanged; + *size = sizeof(float); + return B_OK; + } else if(id >= P_BANDS && id < P_BANDS + fEqualizer.BandCount()) { + int band = id - P_BANDS; + *(float*)value = (float)fEqualizer.Band(band); + *lastChangeTime = fBandsLastChanged[band]; + *size = sizeof(float); + return B_OK; + } + + return B_ERROR; +} + +void +EqualizerNode::SetParameterValue(int32 id, bigtime_t time, const void *value, size_t size) +{ + if(id == P_PREAMP || id == P_BYPASS || id == P_MUTE || + (id >= P_BANDS && id < P_BANDS + fEqualizer.BandCount())) { + media_timed_event ev(time, BTimedEventQueue::B_PARAMETER, (void*)value, + BTimedEventQueue::B_NO_CLEANUP, size, id, "EQ"); + ParameterEventProcessing(&ev); //dirty hack for parameter processing (mediakit bug????) + EventQueue()->AddEvent(ev); + } +} + +//BBufferConsumer +void +EqualizerNode::BufferReceived(BBuffer *buffer) +{ + if(buffer->Header()->destination != fInputMedia.destination.id) { + buffer->Recycle(); + return; + } + + if(fOutputMedia.destination == media_destination::null || !fOutputMediaEnabled) { + buffer->Recycle(); + return; + } + + FilterBuffer(buffer); + + status_t err = SendBuffer(buffer, fOutputMedia.source, fOutputMedia.destination); + if (err < B_OK) { + buffer->Recycle(); + } +} + +status_t +EqualizerNode::AcceptFormat(const media_destination &dst, media_format *format) +{ + if(dst != fInputMedia.destination) { + return B_MEDIA_BAD_DESTINATION; + } + if(format->type != B_MEDIA_RAW_AUDIO) { + return B_MEDIA_BAD_FORMAT; + } + + ValidateFormat((fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ? + fFormat : fPreferredFormat, *format); + + return B_OK; +} + +status_t +EqualizerNode::GetNextInput(int32* cookie, media_input* input) +{ + if(*cookie) { + return B_BAD_INDEX; + } + ++*cookie; + *input = fInputMedia; + return B_OK; +} + +void +EqualizerNode::DisposeInputCookie(int32 cookie) +{ +} + +status_t +EqualizerNode::FormatChanged(const media_source &src, const media_destination &dst, + int32 changeTag, const media_format &format) +{ + return B_MEDIA_BAD_FORMAT; +} + +void +EqualizerNode::ProducerDataStatus(const media_destination &dst, int32 status, bigtime_t when) +{ + if(fOutputMedia.destination != media_destination::null) { + SendDataStatus(status, fOutputMedia.destination, when); + } +} + +status_t +EqualizerNode::GetLatencyFor( const media_destination &dst, bigtime_t *latency, + media_node_id* outTimeSource) +{ + + if(dst != fInputMedia.destination) { + return B_MEDIA_BAD_DESTINATION; + } + + *latency = fDownstreamLatency + fProcessLatency; + *outTimeSource = TimeSource()->ID(); + return B_OK; +} + +status_t +EqualizerNode::Connected(const media_source& source, + const media_destination& destination, const media_format& format, + media_input* poInput) +{ + if(destination != fInputMedia.destination) { + return B_MEDIA_BAD_DESTINATION; + } + if(fInputMedia.source != media_source::null) { + return B_MEDIA_ALREADY_CONNECTED; + } + + fInputMedia.source = source; + fInputMedia.format = format; + *poInput = fInputMedia; + fFormat = format; + + return B_OK; +} + +void +EqualizerNode::Disconnected(const media_source &src, const media_destination &dst) +{ + if(fInputMedia.source != src || dst != fInputMedia.destination) { + return; + } + fInputMedia.source = media_source::null; + + if(fOutputMedia.destination == media_destination::null) { + fFormat.u.raw_audio = media_raw_audio_format::wildcard; + } + fInputMedia.format = fFormat; +} + +//BBufferProducer +status_t +EqualizerNode::FormatSuggestionRequested(media_type type, int32 quality, media_format *format) +{ + if(type != B_MEDIA_RAW_AUDIO) { + return B_MEDIA_BAD_FORMAT; + } + + if(fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) { + *format = fFormat; + } else { + *format = fPreferredFormat; + } + return B_OK; +} + +status_t +EqualizerNode::FormatProposal(const media_source &src, media_format* format) +{ + if(src != fOutputMedia.source) { + return B_MEDIA_BAD_SOURCE; + } + + if(format->type != B_MEDIA_RAW_AUDIO) { + return B_MEDIA_BAD_FORMAT; + } + + ValidateFormat((fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ? + fFormat:fPreferredFormat,*format); + + return B_OK; +} + +status_t +EqualizerNode::FormatChangeRequested(const media_source &src, const media_destination &dst, + media_format* format, int32* _deprecated_) +{ + return B_MEDIA_BAD_FORMAT; +} + +void +EqualizerNode::LateNoticeReceived(const media_source &src, bigtime_t late, bigtime_t when) +{ + if(src != fOutputMedia.source || fInputMedia.source == media_source::null) { + return; + } + NotifyLateProducer(fInputMedia.source, late, when); +} + +status_t +EqualizerNode::GetNextOutput(int32 *cookie, media_output* output) +{ + if(*cookie) { + return B_BAD_INDEX; + } + ++*cookie; + *output = fOutputMedia; + return B_OK; +} + +status_t +EqualizerNode::DisposeOutputCookie(int32 cookie) +{ + return B_OK; +} + +status_t +EqualizerNode::SetBufferGroup(const media_source &src, BBufferGroup *group) +{ + int32 changeTag; + status_t ret = B_OK; + + if(src != fOutputMedia.source) { + return B_MEDIA_BAD_SOURCE; + } + if(fInputMedia.source == media_source::null) { + return B_ERROR; + } + + ret = SetOutputBuffersFor(fInputMedia.source, fInputMedia.destination, group, 0, &changeTag); + return ret; +} + +status_t +EqualizerNode::PrepareToConnect( const media_source &src, const media_destination &dst, + media_format* format, media_source* outSource, char* outName) +{ + if(src != fOutputMedia.source) { + return B_MEDIA_BAD_SOURCE; + } + + if(format->type != B_MEDIA_RAW_AUDIO) { + return B_MEDIA_BAD_FORMAT; + } + + if(fOutputMedia.destination != media_destination::null) { + return B_MEDIA_ALREADY_CONNECTED; + } + + status_t err = ValidateFormat((fFormat.u.raw_audio.format != media_raw_audio_format::wildcard.format) ? + fFormat : fPreferredFormat, *format); + + if(err < B_OK) { + return err; + } + + SetOutputFormat(*format); + + fOutputMedia.destination = dst; + fOutputMedia.format = *format; + + *outSource = fOutputMedia.source; + strncpy(outName, fOutputMedia.name, B_MEDIA_NAME_LENGTH); + + return B_OK; +} + +void +EqualizerNode::Connect(status_t status, const media_source &src, const media_destination &dst, + const media_format &format, char *outName) +{ + status_t err; + if(status < B_OK) { + fOutputMedia.destination = media_destination::null; + return; + } + + strncpy(outName, fOutputMedia.name, B_MEDIA_NAME_LENGTH); + fOutputMedia.destination = dst; + fFormat = format; + + media_node_id timeSource; + err = FindLatencyFor(fOutputMedia.destination, &fDownstreamLatency, &timeSource); + + InitFilter(); + + fProcessLatency = GetFilterLatency(); + SetEventLatency(fDownstreamLatency + fProcessLatency); + + if(fInputMedia.source != media_source::null) { + SendLatencyChange(fInputMedia.source, fInputMedia.destination, + EventLatency() + SchedulingLatency()); + } + + bigtime_t duration = 0; + + int sample_size = (fFormat.u.raw_audio.format & 0xf) * + fFormat.u.raw_audio.channel_count; + + if (fFormat.u.raw_audio.buffer_size > 0 && + fFormat.u.raw_audio.frame_rate > 0 && + sample_size > 0) { + duration = (bigtime_t)(((fFormat.u.raw_audio.buffer_size / sample_size) / + fFormat.u.raw_audio.frame_rate) * 1000000.0); + } + + SetBufferDuration(duration); +} + +void +EqualizerNode::Disconnect(const media_source &src, const media_destination &dst) +{ + if(src != fOutputMedia.source) { + return; + } + + if(dst != fOutputMedia.destination) { + return; + } + + fOutputMedia.destination = media_destination::null; + + if(fInputMedia.source == media_source::null) { + fFormat.u.raw_audio = media_raw_audio_format::wildcard; + } + + fOutputMedia.format = fFormat; +} + +void +EqualizerNode::EnableOutput(const media_source &src, bool enabled, int32* _deprecated_) +{ + if(src != fOutputMedia.source) { + return; + } + fOutputMediaEnabled = enabled; +} + +status_t +EqualizerNode::GetLatency(bigtime_t *latency) +{ + *latency = EventLatency() + SchedulingLatency(); + return B_OK; +} + +void +EqualizerNode::LatencyChanged(const media_source &src, const media_destination &dst, + bigtime_t latency, uint32 flags) +{ + if(src != fOutputMedia.source || dst != fOutputMedia.destination) { + return; + } + + fDownstreamLatency = latency; + SetEventLatency(fDownstreamLatency + fProcessLatency); + + if(fInputMedia.source != media_source::null) { + SendLatencyChange(fInputMedia.source, + fInputMedia.destination,EventLatency() + SchedulingLatency()); + } +} + +//BMediaEventLooper +bigtime_t +EqualizerNode::OfflineTime() +{ + return 0LL; +} + +//EqualizerNode +void +EqualizerNode::HandleEvent(const media_timed_event *event, bigtime_t late, bool realTime) +{ + if(event->type == BTimedEventQueue::B_PARAMETER) { + ParameterEventProcessing(event); + } +} + + + +void +EqualizerNode::ParameterEventProcessing(const media_timed_event* event) +{ + float value = 0.0; + int32 value32 = 0; + + int32 id = event->bigdata; + size_t size = event->data; + bigtime_t now = TimeSource()->Now(); + + type_code v_type = B_FLOAT_TYPE; + + BParameter *web_param; + + for(int i=0; iCountParameters(); i++) { + web_param = fWeb->ParameterAt(i); + if(web_param->ID() == id) { + v_type=web_param->ValueType(); + break; + } + } + + if(v_type == B_FLOAT_TYPE) + value = *((float*)event->pointer); + if(v_type == B_INT32_TYPE) { + value32 = *((int32*)event->pointer); + value = (float)value32; + } + + if(id == P_MUTE) { + fMute = value32; + fMuteLastChanged = now; + BroadcastNewParameterValue(now, id, event->pointer, size); + } else if(id == P_BYPASS) { + fByPass = value32; + fByPassLastChanged = now; + BroadcastNewParameterValue(now, id, event->pointer, size); + } else if(id == P_PREAMP) { + if(value != fEqualizer.PreAmp()) { + fEqualizer.SetPreAmp(value); + fPreAmpLastChanged = now; + BroadcastNewParameterValue(now, id, &value, size); + } + } else if(id >= P_BANDS && id < P_BANDS + fEqualizer.BandCount()) { + int band = id - P_BANDS; + if(value != fEqualizer.Band(band)) { + fEqualizer.SetBand(band, value); + fBandsLastChanged[band] = now; + BroadcastNewParameterValue(now, id, &value, size); + } + } +} + +status_t +EqualizerNode::ValidateFormat(const media_format &preferredFormat, + media_format &format) +{ + status_t ret = B_OK; + + if(format.type != B_MEDIA_RAW_AUDIO) { + format = preferredFormat; + return B_MEDIA_BAD_FORMAT; + } + + media_raw_audio_format &wild = media_raw_audio_format::wildcard; + media_raw_audio_format &f = format.u.raw_audio; + const media_raw_audio_format &pref = format.u.raw_audio; + + if(pref.frame_rate != wild.frame_rate && f.frame_rate != pref.frame_rate) { + if(f.frame_rate != wild.frame_rate) { + ret = B_MEDIA_BAD_FORMAT; + } + f.frame_rate = pref.frame_rate; + } + + if(pref.channel_count != wild.channel_count && f.channel_count != pref.channel_count) { + if(f.channel_count != wild.channel_count) { + ret = B_MEDIA_BAD_FORMAT; + } + f.channel_count = pref.channel_count; + } + + if(pref.byte_order != wild.byte_order && f.byte_order != pref.byte_order) { + if(f.byte_order != wild.byte_order) { + ret = B_MEDIA_BAD_FORMAT; + } + f.byte_order = pref.byte_order; + } + + if(pref.format != wild.format && f.format != pref.format) { + if(f.format != wild.format) { + ret = B_MEDIA_BAD_FORMAT; + } + f.format = pref.format; + } + + if(pref.buffer_size != wild.buffer_size && f.buffer_size != pref.buffer_size) { + if(f.buffer_size != wild.buffer_size) { + ret = B_MEDIA_BAD_FORMAT; + } + f.buffer_size = pref.buffer_size; + } + + return ret; +} + +void +EqualizerNode::SetOutputFormat(media_format &format) +{ + media_raw_audio_format &f = format.u.raw_audio; + media_raw_audio_format &w = media_raw_audio_format::wildcard; + + if(f.frame_rate == w.frame_rate) { + f.frame_rate = 44100.0; + } + + if(f.channel_count == w.channel_count) { + if(fInputMedia.source != media_source::null) { + f.channel_count = fInputMedia.format.u.raw_audio.channel_count; + } else { + f.channel_count = 2; + } + } + + if(f.format == w.format) { + f.format = media_raw_audio_format::B_AUDIO_FLOAT; + } + + if(f.byte_order == w.format) { + f.byte_order = (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; + } + + if(f.buffer_size == w.buffer_size) { + f.buffer_size = BUFF_SIZE; + } +} + +void +EqualizerNode::InitParameterValues() +{ + fMute = 0; + fByPass = 0; + fMuteLastChanged = 0LL; + fByPassLastChanged = 0LL; + fPreAmpLastChanged = 0LL; + + for(int i=0;iMakeGroup("EqualizerNode Parameters"); + BParameterGroup *fFControlGroup = fParamGroup->MakeGroup("FilterControl"); + + fFControlGroup->MakeDiscreteParameter(P_MUTE,B_MEDIA_NO_TYPE,"Mute", B_ENABLE); + fFControlGroup->MakeDiscreteParameter(P_BYPASS,B_MEDIA_NO_TYPE,"ByPass", B_ENABLE); + + BNullParameter *label; + BParameterGroup *group; + BContinuousParameter *value; + + group = fParamGroup->MakeGroup("Pre Amp"); + label = group->MakeNullParameter(P_PREAMP_LABEL, B_MEDIA_NO_TYPE, + "Pre Amp", B_GENERIC); + value = group->MakeContinuousParameter(P_PREAMP, B_MEDIA_NO_TYPE, + "", B_GAIN, "dB", -8.0, 8.0, 0.1); + label->AddOutput(value); + value->AddInput(label); + + for(int i=0; iMakeGroup(freq); + label = group->MakeNullParameter(P_BAND_LABELS + i, B_MEDIA_NO_TYPE, + freq, B_GENERIC); + value = group->MakeContinuousParameter(P_BANDS + i, B_MEDIA_NO_TYPE, + "", B_GAIN, "dB", -16.0, 16.0, 0.1); + label->AddOutput(value); + value->AddInput(label); + } + + SetParameterWeb(fWeb); +} + +void +EqualizerNode::InitFilter(void) +{ + fEqualizer.SetFormat(fFormat.u.raw_audio.channel_count, fFormat.u.raw_audio.frame_rate); +} + +bigtime_t +EqualizerNode::GetFilterLatency(void) +{ + if(fOutputMedia.destination == media_destination::null) { + return 0LL; + } + + BBufferGroup *test_group = new BBufferGroup(fOutputMedia.format.u.raw_audio.buffer_size, 1); + + BBuffer *buffer = test_group->RequestBuffer(fOutputMedia.format.u.raw_audio.buffer_size); + buffer->Header()->type = B_MEDIA_RAW_AUDIO; + buffer->Header()->size_used = fOutputMedia.format.u.raw_audio.buffer_size; + + bigtime_t begin = system_time(); + FilterBuffer(buffer); + bigtime_t latency = system_time() - begin; + + buffer->Recycle(); + delete test_group; + + InitFilter(); + + return latency; +} + +void +EqualizerNode::FilterBuffer(BBuffer* buffer) +{ + uint32 m_frameSize = (fFormat.u.raw_audio.format & 0x0f) * fFormat.u.raw_audio.channel_count; + uint32 samples = buffer->Header()->size_used / m_frameSize; + uint32 channels = fFormat.u.raw_audio.channel_count; + if(fMute !=0 ) { + memset(buffer->Data(), 0, buffer->Header()->size_used); + } else { + if(fByPass == 0) { + fEqualizer.ProcessBuffer((float*)buffer->Data(), samples*channels); + } + } +} diff --git a/src/add-ons/media/media-add-ons/equalizer/EqualizerNode.h b/src/add-ons/media/media-add-ons/equalizer/EqualizerNode.h new file mode 100644 index 0000000000..dbd6b9a897 --- /dev/null +++ b/src/add-ons/media/media-add-ons/equalizer/EqualizerNode.h @@ -0,0 +1,148 @@ +/* + * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef __EQUALIZER_NODE_H__ +#define __EQUALIZER_NODE_H__ + +#include +#include +#include +#include + +#include "Equalizer.h" + +class BMediaAddOn; +class BBufferGroup; + +#define BUFF_SIZE 0x2000 + +#define ID_AUDIO_INPUT 1 +#define ID_AUDIO_OUTPUT 2 + +#define P_PREAMP_LABEL 100 +#define P_PREAMP 101 +#define P_MUTE 102 +#define P_BYPASS 103 +#define P_BAND_LABELS 200 +#define P_BANDS 300 + +class EqualizerNode : public BBufferConsumer, + public BBufferProducer, + public BControllable, + public BMediaEventLooper +{ +public: + virtual ~EqualizerNode(); + EqualizerNode(BMediaAddOn *pAddOn=NULL); + //BMediaNode +public: + virtual BMediaAddOn *AddOn(int32 *id) const; + virtual status_t HandleMessage(int32 code, const void *data, size_t size); +protected: + virtual void NodeRegistered(); + //BControllable +public: + virtual status_t GetParameterValue(int32 id, bigtime_t *lastChangeTime, + void *value, size_t *size); + virtual void SetParameterValue(int32 id, bigtime_t changeTime, + const void *value, size_t size); + //BBufferConsumer +public: + virtual void BufferReceived(BBuffer *buffer); + virtual status_t AcceptFormat(const media_destination &dst, + media_format *format); + virtual status_t GetNextInput(int32 *cookie, media_input *input); + virtual void DisposeInputCookie(int32 cookie); + virtual status_t FormatChanged(const media_source &src, + const media_destination &dst, + int32 change_tag, const media_format &format); + virtual void ProducerDataStatus(const media_destination &dst, + int32 status, + bigtime_t when); + virtual status_t GetLatencyFor(const media_destination &dst, + bigtime_t *latency, + media_node_id *time_src); + virtual status_t Connected(const media_source &src, + const media_destination &dst, + const media_format &format, + media_input *input); + virtual void Disconnected(const media_source &src, + const media_destination &dst); + //BBufferProducer +public: + virtual status_t FormatSuggestionRequested(media_type type, + int32 quality, media_format *format); + virtual status_t FormatProposal(const media_source &src, + media_format *format); + virtual status_t FormatChangeRequested(const media_source &src, + const media_destination &dst, + media_format *format, + int32* _deprecated_); + virtual void LateNoticeReceived(const media_source &src, + bigtime_t late, bigtime_t when); + virtual status_t GetNextOutput(int32 *cookie, media_output *output); + virtual status_t DisposeOutputCookie(int32 cookie); + virtual status_t SetBufferGroup(const media_source &src, + BBufferGroup *group); + virtual status_t PrepareToConnect(const media_source &src, + const media_destination &dst, + media_format *format, media_source *source, + char *name); + virtual void Connect(status_t status, + const media_source &src, + const media_destination &dst, + const media_format &format, + char* name); + virtual void Disconnect(const media_source &src, + const media_destination &dst); + virtual void EnableOutput(const media_source &src, + bool enabled, int32* _deprecated_); + virtual status_t GetLatency(bigtime_t *outLatency); + virtual void LatencyChanged( const media_source &src, + const media_destination &dst, + bigtime_t latency, uint32 flags); + //BMediaEventLooper +protected: + virtual bigtime_t OfflineTime(); + //EqualizerNode +protected: + virtual void HandleEvent(const media_timed_event *event, + bigtime_t late, + bool realTimeEvent=false); + void ParameterEventProcessing(const media_timed_event *event); + status_t ValidateFormat(const media_format &preferredFormat, + media_format &format); + void SetOutputFormat(media_format &format); + virtual void InitParameterValues(void); + virtual void InitParameterWeb(void); + virtual void InitFilter(void); + virtual bigtime_t GetFilterLatency(void); + virtual void FilterBuffer(BBuffer* pBuffer); + +private: + BMediaAddOn *fAddOn; + BParameterWeb *fWeb; + + Equalizer fEqualizer; + + media_format fPreferredFormat; + media_format fFormat; + media_input fInputMedia; + media_output fOutputMedia; + + int32 fMute; + int32 fByPass; + + bigtime_t fMuteLastChanged; + bigtime_t fByPassLastChanged; + bigtime_t fPreAmpLastChanged; + bigtime_t fBandsLastChanged[EQ_BANDS]; + + bigtime_t fProcessLatency; + bigtime_t fDownstreamLatency; + bool fOutputMediaEnabled; +}; + +#endif //__EQUALIZER_NODE_H__ diff --git a/src/add-ons/media/media-add-ons/equalizer/Jamfile b/src/add-ons/media/media-add-ons/equalizer/Jamfile new file mode 100644 index 0000000000..e746ec0dd5 --- /dev/null +++ b/src/add-ons/media/media-add-ons/equalizer/Jamfile @@ -0,0 +1,10 @@ +SubDir HAIKU_TOP src add-ons media media-add-ons equalizer ; + +AddResources equalizer.media_addon : Equalizer.rdef ; + +Addon equalizer.media_addon : + Equalizer.cpp + EqualizerAddOn.cpp + EqualizerNode.cpp + : be media +;