⛏️ index : haiku.git

/*
 * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com.
 * All rights reserved.
 * Distributed under the terms of the MIT License.
 */ 

#include <ByteOrder.h>
#include <Buffer.h>
#include <BufferGroup.h>
#include <TimeSource.h>
#include <ParameterWeb.h>
#include <String.h>

#include <stdio.h>
#include <string.h>

#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;
	}
	BMediaNode::HandleBadMessage(message, data, size);
	return B_ERROR;	
}


void
EqualizerNode::NodeRegistered()
{
	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();

	SetPriority(B_REAL_TIME_PRIORITY);
	Run();
}


//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);
	} else if (id == P_BYPASS) {
		*(int32*)value = fByPass;
		*lastChangeTime = fByPassLastChanged;
		*size = sizeof(int32);
	} else if (id == P_PREAMP) {
		*(float*)value = (float)fEqualizer.PreAmp();
		*lastChangeTime = fPreAmpLastChanged;
		*size = sizeof(float);
	} 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);
	} else
		return B_ERROR;
	return B_OK;
}


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, (char*)"EQ");
		//dirty hack for parameter processing (mediakit bug????)
		ParameterEventProcessing(&ev);
		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* name)
{
	if (status < B_OK) {
		fOutputMedia.destination = media_destination::null;
		return;
	}

	strncpy(name, fOutputMedia.name, B_MEDIA_NAME_LENGTH);
	fOutputMedia.destination = dst;
	fFormat = format;

	media_node_id timeSource;
	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; i < fWeb->CountParameters(); 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);
	else 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 &preferred_format,
							media_format &proposed_format)
{
	status_t ret = B_OK;
		
	if (proposed_format.type != B_MEDIA_RAW_AUDIO) {
		proposed_format = preferred_format;
		return B_MEDIA_BAD_FORMAT;
	}

	const media_raw_audio_format &wild = media_raw_audio_format::wildcard;
	media_raw_audio_format &f = proposed_format.u.raw_audio;
	const media_raw_audio_format &pref = preferred_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.format != wild.format && f.format != pref.format) {
		if(f.format != wild.format)
			ret = B_MEDIA_BAD_FORMAT;
		f.format = pref.format;
	}

	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.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;
	const 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; i < EQ_BANDS; i++)
		fBandsLastChanged[i] = 0LL;
	
	fEqualizer.CleanUp();
}


void 
EqualizerNode::InitParameterWeb(void)
{
	fWeb = new BParameterWeb();
	
	BParameterGroup* fParamGroup = fWeb->MakeGroup("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; i < fEqualizer.BandCount(); i++) {
		char freq[32];
		sprintf(freq,"%gHz",fEqualizer.BandFrequency(i));
		group = fParamGroup->MakeGroup(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);		
}