Author Topic: ReplayGain / BASS_FX_VOLUME  (Read 367 times)

lboregard

  • Posts: 12
ReplayGain / BASS_FX_VOLUME
« on: 29 Dec '18 - 18:28 »
Hi,

I'm developing a mostly toy golang player using bass (in macOS).

Every API I've tried has worked beatifully, except seemingly BASS_ChannelSetFX and companion, BASS_FXSetParameters

So (roughly) the code is this

Invocation
Code: [Select]
p.rgFx, err = bass.ChannelSetFX(p.channel, bass.BASS_FX_VOLUME, 0)
if err != nil {
mlog.Error(err)
return
}

gain := math.Pow(10, (job.Gain / 20))
mlog.Info("rgfx(%d)-TrackGain(%f)-Gain (%f)", p.rgFx, job.Gain, gain)

if err := bass.ChannelGain(p.rgFx, float32(gain)); err != nil {
mlog.Error(err)
return
}

if err := bass.ChannelPlay(p.channel); err != nil {
mlog.Warning("Unable to resume play: %s", err)
}

Definitions
Code: [Select]
func ChannelSetFX(ch, kind, priority int) (int, error) {
fx := C.BASS_ChannelSetFX(C.DWORD(ch), C.DWORD(kind), C.int(priority))
if fx == 0 {
return 0, errMsg(int(C.BASS_ErrorGetCode()))
}

return int(fx), nil
}

func ChannelGain(handle int, gain float32) (error) {
var params C.BASS_FX_VOLUME_PARAM

params.fTarget = C.float(gain)
params.fCurrent = C.float(-1)
params.fTime = C.float(0)
params.lCurve = C.DWORD(0)

log.Printf("params(%+v)", params)

if C.BASS_FXSetParameters(C.HFX(handle), pointer.Save(&params)) != 1  {
return errMsg(int(C.BASS_ErrorGetCode()))
}

return nil
}

Don't mind all the castings, as that is Go converting to C, nor the pointer.Save workaround, also Go to C gymnastics (I've used pointer.Save successfully for BASS_ChannelSetSync/BASS_SYNC_END).

Upon execution, I get debug lines such as the following
Code: [Select]
rgfx(2147483666)-TrackGain(-2.210000)-Gain (0.775354)
params({fTarget:0.77535397 fCurrent:-1 fTime:0 lCurve:0})

and so on, which seem acceptable fTarget values.

But when BASS_ChannelPlay, it's completely silent. Zero Sound.

As soon as I stop applying the effect, playback is normal (regular sound).

I've thought about BASS_FXGetParameters, too check what's the current level, but the docs mention that fCurrent is not necessarily the level that was last set with BASS_FXSetParameters.

Any thoughts ?

lboregard

  • Posts: 12
Re: ReplayGain / BASS_FX_VOLUME
« Reply #1 on: 30 Dec '18 - 15:15 »
So I'm leaning towards setting a DSP on the channel and using straight C to apply the gain.

That worked with a Pascal app I coded

Code: [Select]
procedure ReplayGain(handle: HDSP; channel: DWORD; buffer: Pointer; length: DWORD; user: DWORD); stdcall;
var
  p: PSingle;
  i: integer;
  gain: Single;
begin
  gain := PGain(user)^.Gain;
  if gain = 0.00 then exit;

  p := buffer;
  i := 0;

  while (i < (length div 4)) do
  begin
p^ := p^ * Power(10, (gain/20));
Inc(p);
Inc(i);
  end;

If anybody has the equivalent C code, I'd appreciate you sharing.

Code: [Select]
void apply_gain(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user) {
???
}

assuming 32-bit floating-point samples.

In the meantime, I'll work on it.
« Last Edit: 30 Dec '18 - 15:19 by lboregard »

lboregard

  • Posts: 12
Re: ReplayGain / BASS_FX_VOLUME
« Reply #2 on: 30 Dec '18 - 18:21 »
Great Success !!!

But not via the DSP ;D

For reference, the C function looks like this

Code: [Select]
void applygain(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user) {
float *gain = user;
if (!*gain) return;

// printf("gain(%f)\n", *gain);

float *samples = buffer;
for (; length; length-=4, samples+=4) {
float tgain = (float) pow(10., *gain/20.);
*samples = MIN(*samples * tgain, FLT_MAX);
// printf("sample(%f)-length(%d)-tgain(%f)\n", *samples, length, tgain);
}
}

It worked sometimes.

The debug lines showed very high values a couple of times, so I clipped it at max float, but that didn't help.

I suspect it has to do with Go/C interaction.

At the end of the day, the initial code I had is what worked, with a minor change

Code: [Select]
func ChannelGain(handle int, gain float32) (error) {
var params C.BASS_FX_VOLUME_PARAM

params.fTarget = C.float(gain)
params.fCurrent = C.float(-1)
params.fTime = C.float(0)
params.lCurve = C.DWORD(0)

log.Printf("params(%+v)", params)

if C.BASS_FXSetParameters(C.HFX(handle), unsafe.Pointer(&params)) != 1  {
return errMsg(int(C.BASS_ErrorGetCode()))
}

return nil
}

Turns out pointer.Save didn't apply in this context, I needed to pass the params themselves.

Thanks for the library !