Author Topic: Scaling samples  (Read 10549 times)

Ingolf

  • Posts: 81
Scaling samples
« on: 11 Feb '03 - 22:11 »
Hi there,

I have made this peace of code (Delphi) to mix a stream with a master stream, and apply scaling of the data (pitch shifting). To do so, I do the following:

The stream callback asks lets say 1764 bytes and I want to do 150% pitch shifting. I would request 2646 bytes of data from the decoding stream and pass it to this function (don't mind reverse) as MixPtr:

Code: [Select]
procedure MixChannels(BasePtr, MixPtr: Pointer; BaseLength, MixLength: Integer; ReverseMix: Boolean);
var
 B, M: ^DWORD;
 Len: Integer;
 I: Integer;
 C, S: Extended;
 L, R, X, Y: SmallInt;
begin
 B := BasePtr;
 S := MixLength / BaseLength;
 C := S;
 M := MixPtr;
 for I := 0 to (BaseLength div 4)-1 do begin
   X := LoWord(B^);
   Y := LoWord(M^);
   L := Clip(X + Y);
   X := HiWord(B^);
   Y := HiWord(M^);
   R := Clip(X + Y);
   B^ := MakeLong(L, R);
   Inc(B);
   M := MixPtr;
   if ReverseMix then Inc(M, Round((MixLength div 4) - C))
   else Inc(M, Round(C));
   C := C + S;
 end;
end;


Now when I apply this scale factor of 1.5, I hear all sorts of clicks and blops, even worse when going below 1. Is this really because of the samples being added or cut off, causing a weird waveform? Or is it just me!?!?!

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #1 on: 12 Feb '03 - 21:42 »
anyone?!

Ingolf2

  • Guest
Re: Scaling samples
« Reply #2 on: 13 Feb '03 - 10:38 »
boehoehoe snif

Angelus

  • Posts: 2
Re: Scaling samples
« Reply #3 on: 13 Feb '03 - 11:37 »
/me huggles ingolf  :-*

Ian @ un4seen

  • Administrator
  • Posts: 21211
Re: Scaling samples
« Reply #4 on: 13 Feb '03 - 15:36 »
I don't know how Delphi adds 16-bit values, but if it doesn't extend them to 32-bit, then your "X + Y" can overflow before it even reaches clipping.

Also, how are you defining the "Clip" function? It should be taking a 32-bit integer parameter. Make sure it is, and try changing "X" and "Y" to 32-bit integers...

Code: [Select]
var
L, R: SmallInt;
X, Y: Integer;


Ingolf2

  • Guest
Re: Scaling samples
« Reply #5 on: 13 Feb '03 - 16:00 »
If I am not mistaking, it first adds it and then passes it to the clipping function as Longint, because Clip expects Longint.

It works, because when I don't apply pitch shifting, it sounds great, only when using pitch shifting, the sound produces clicks and blops.

So I really don't know. Do you think this function should work?

Ian @ un4seen

  • Administrator
  • Posts: 21211
Re: Scaling samples
« Reply #6 on: 13 Feb '03 - 16:43 »
Quote

It works, because when I don't apply pitch shifting, it sounds great, only when using pitch shifting, the sound produces clicks and blops.

Do you mean it's fine when "BaseLength" and "MixLength" are the same?

What's happening to "MixPtr" between calls? If it's not increasing by "MixLength", then there's the cause of the clicks.

Quote

So I really don't know. Do you think this function should work?

It depends on what you're expecting it to do. The code is doing basic resampling (playing the sound at an altered rate)... as the rate increases, the pitch increases, but the length also decreases. And vice versa. Like playing a record at the wrong speed :)

Ingolf2

  • Guest
Re: Scaling samples
« Reply #7 on: 13 Feb '03 - 18:04 »
Quote
Do you mean it's fine when "BaseLength" and "MixLength" are the same?


It's fine when they are the same indeed.

Quote
What's happening to "MixPtr" between calls? If it's not increasing by "MixLength", then there's the cause of the clicks.


I don't know what you mean exactly, but this is what I do:
- GetMem(MixPtr, MixLength);
- Read Length data into Buffer.
- Read MixLength data into MixPtr. (I calculate MixLength by MixLength = BaseLength * 1.5 when scaling 150%)
- Then pass the pointers: MixChannels(Buffer, MixPtr, Length, MixLength, FALSE);

Quote
It depends on what you're expecting it to do. The code is doing basic resampling (playing the sound at an altered rate)... as the rate increases, the pitch increases, but the length also decreases. And vice versa. Like playing a record at the wrong speed :)


That is what it has to do, stuff the data from MixPtr into BasePtr by adding or cutting samples, only without the clicks, hehe.

You say "... the pitch increases, but the length also decreases ...", don't you mean increases? I read more(not less) data from the channel into the MixPtr in order to scale them over the Buffer.

I can't see the light.

Ian @ un4seen

  • Administrator
  • Posts: 21211
Re: Scaling samples
« Reply #8 on: 14 Feb '03 - 12:29 »
Quote

That is what it has to do, stuff the data from MixPtr into BasePtr by adding or cutting samples, only without the clicks, hehe.

You say "... the pitch increases, but the length also decreases ...", don't you mean increases?

When you play something faster, it reaches the end quicker - the playback length/duration decreases. Think of a speeded-up record :)

So, if you're expecting the pitch to be changed without affecting the length, then I'm afraid the code above will not do that.

Ingolf2

  • Guest
Re: Scaling samples
« Reply #9 on: 14 Feb '03 - 15:35 »
I know what you mean now. Off course the length decreases (duration). If you had said duration, I would have understoud.  When reading more data from a channel and then stuffing it into a shorter buffer, the duration decreases.

When you say Length, I think of the data that is requested every time a buffer must be filled. So I thought you sayed that I have to read less data into MixPtr while shifting up, and read more data when shifting down. Just a misunderstanding I guess.

Is it helpful if I send an original wav file and a pitch shifted one?

Ingolf2

  • Guest
Re: Scaling samples
« Reply #10 on: 15 Feb '03 - 11:06 »
I have changed it to this now, but I still have the same problem. I noticed that the sound had some clicks even when not scaling, but C := S was the cause of that.

Code: [Select]
procedure MixChannels(BasePtr, MixPtr: Pointer; BaseLength, MixLength: Integer; ReverseMix: Boolean);
var
 B, M: ^DWORD;
 I, C: Integer;
 S: Single;
 L, R: SmallInt;
 lB, rB, lM, rM: Integer;
begin
 B := BasePtr;
 M := MixPtr;
 S := MixLength / BaseLength;
 for I := 0 to (BaseLength div 4)-1 do begin
   lM := SmallInt(LoWord(M^));
   rM := SmallInt(HiWord(M^));
   lB := SmallInt(LoWord(B^));
   rB := SmallInt(HiWord(B^));
   L := ClipSampleInt(lB + lM);
   R := ClipSampleInt(rB + rM);
   B^ := MakeLong(L, R);
   Inc(B);
   M := MixPtr;
   if ReverseMix then Inc(M, (MixLength div 4) - Round(I*S))
   else Inc(M, Round(I*S));
 end;
end;

Irrational86

  • Posts: 960
Re: Scaling samples
« Reply #11 on: 15 Feb '03 - 18:51 »
I think its, BaseLength / MixLength, because you want to get to BaseLength, not to MixLength ;)
Dont just ignore me, give it a try

Ingolf2

  • Guest
Re: Scaling samples
« Reply #12 on: 15 Feb '03 - 19:47 »
How is that possible?

1. (base / mix) = (100 / 150) = 0,666
2. (mix / base) = (150 / 100) = 1,5

The method requests 150 samples, and since 150 * 0,666 is 100, it will only read 100 samples from MixPtr, which is not correct.

By using my method, it will read samples like this:

B[0] = M[0]
B[1] = M[1,5]
B[2] = M[3]
...
B[98] = M[147]
B[99] = M[148,5]

I would say this is correct, because the 150 samples need to fit into an array of 100 samples. Don't you think?

When using method 2, it will speed up because reading more data, but pitch shifted down.

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #13 on: 15 Feb '03 - 19:56 »
I recorded the sound, and this is what it showed. It looks like the proc reads beyond MixPtr or something.

« Last Edit: 16 Feb '03 - 13:05 by Ingolf »

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #14 on: 16 Feb '03 - 13:10 »
...

Ian @ un4seen

  • Administrator
  • Posts: 21211
Re: Scaling samples
« Reply #15 on: 17 Feb '03 - 12:30 »
"Round" should really be "Trunc"...
Code: [Select]
   if ReverseMix then Inc(M, (MixLength div 4) - 1 - Trunc(I*S))
   else Inc(M, Trunc(I*S));

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #16 on: 17 Feb '03 - 13:30 »
You sure? Why?

Irrational86

  • Posts: 960
Re: Scaling samples
« Reply #17 on: 17 Feb '03 - 13:32 »
Because trunc gets the whole number exact, without a probability of jumping to the number higher because of the decimals, Ian has probably been programming sound stuff for a while now (at least since ~'98 ). My guess is he knows his stuff, if i were you i would listen to him ;D

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #18 on: 17 Feb '03 - 13:36 »
I am, so don't start. I know he's great, I just asked why.

Thanks man, it works! As I already said in my previous post: It looks like the proc reads beyond MixPtr or something. But I hadn't thought of that, thanks!

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #19 on: 17 Feb '03 - 13:40 »
I just tested quickly, but now when playing sound with a low amplitude or with a lot of high frequencies, the sound is "gappy" or something.

The clicks are gone! now this...

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #20 on: 18 Feb '03 - 21:29 »
I'm sorry for acting stupid, but could you guys help me out one more time? Everything is still great, no worries! But...

When I change the pitch a couple of times, and then reset it to normal speed (1), it sounds crappy (or "gappy") like I already mentioned. Everything is fine when there has been no changes to the pitch.

The method that calls MixChannels gets a new pointer to a memory block everytime it is called, so I think it cannot be data beeing mixed up, I don't know.

Can anyone help me out?

Thanks for your help Ian and XMinioNX!

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #21 on: 19 Feb '03 - 16:22 »
I have something different now. When playing mp3's etc. everything is fine, but when I open a wave file, see what happens! I totally don't understand...

Please, any help would be appreciated...


Ian @ un4seen

  • Administrator
  • Posts: 21211
Re: Scaling samples
« Reply #22 on: 20 Feb '03 - 16:12 »
Quote
I have something different now. When playing mp3's etc. everything is fine, but when I open a wave file, see what happens! I totally don't understand...

Make sure the WAV file is stereo. Your mixing routine is expecting stereo 16-bit sample data.

Ingolf

  • Posts: 81
Re: Scaling samples
« Reply #23 on: 20 Feb '03 - 19:09 »
The file is stereo. I does not happen when shifting up 200%

?!?!?!?!?!

vertex

  • Posts: 38
Re: Scaling samples
« Reply #24 on: 20 Feb '03 - 19:28 »
beware of the stereo/pcm-format: left/right/left/right/left/right/left... the data you write! try scaling the left and right channel seperately and then mix it together inīthe l/r/l/r/l... format... just a small idea...