DasEtwas / enginesound

Procedural engine sound generator controlled via GUI or CLI
MIT License
309 stars 15 forks source link

Understanding the algorithm #8

Closed ashwinb-git closed 3 years ago

ashwinb-git commented 3 years ago

Hey!

Your implementation is really impressive! I was reading the paper and I am trying to implement my own version in Matlab/c++. I was going through your initial commit and had trouble understanding how you established the relation between the variable x and the rpm. So I wanted to ask if you used any other sources besides the paper to write the code ? I am new to rust so its difficult to understand the flow.

DasEtwas commented 3 years ago

No clue what you are referring to. A code link would be nice.

DasEtwas commented 3 years ago

I didn't use any other sources. I have to admit the code quality is trash, but this started as a small project which I decided was too interesting to not share.

ashwinb-git commented 3 years ago

Well, I was looking at your very first commit ('add placeholder files' )so that I can understand the algorithm much better and it helped. The part that I couldn't understand how you wrote your initial loop in' gen.rs'.

``for i in (0..get_length(&spec, duration)).map(|x| x as f64 / onesec) { let rpm = (i / 8.0).powf(0.9) * (rpmhi - rpmlo) + rpmlo;

    let x = (get_phasor_freq(rpm) * i) % 1.0;
    let mut sample = (exhaust_valve(x) * 1.0 + intake_valve(x) * 1.0 + piston_motion(x) * 0.5 + fuel_ignition(x) * 1.0) as f32;

    sample = filter.filter(sample);

    let (res, _) = resonance_chamber.step(sample, 0.0);
    sample += res;

    writer.write_sample((sample * amplitude) as i16).unwrap();
}

}

I understood that the phasor is the sum of all four functions, intake & exhaust valves, piston and fuel. But how did u figure out the number of iterations for the loop ? how to relate the rpm with this phasor equation? I think you rename this variable 'x' as 'piston_location' or something in your latest version. I would also like know how the variable 't' works in the fuel ignition equation. In your first commit you have set a constant called 'ignition scale = 0.1' which is the 't'. Is there a particular reason for selecting this value?

DasEtwas commented 3 years ago

Seems like x in this case is the crankshaft position with the unit "revolutions". Especially in the first commit you can assume I used many fudge values like 0.1 which were just for testing.

ashwinb-git commented 3 years ago

yeah, I understand. So for each rpm we just figure out where the crankshaft is and get the corresponding sample right? but i was wondering, for a 4 stroke engine, the intake is active during the first revolution and the exhaust valve during the next. Did u take this into consideration while programming?

DasEtwas commented 3 years ago

The exhaust_valve and intake_valve functions reflect this.

ashwinb-git commented 3 years ago

yeah, got it! And now the piston and ignition sound is sent through the cylinder and lp filtered right? cylinder is represented by the waveguide. are waveguides modelled as stack/queues with samples delayed as they are popped out?

DasEtwas commented 3 years ago

A cylinder is an instantaneous emitter of sample amplitudes. Only its inputs and outputs have waveguides. The piston's inptu and output from/to those is modulated by the valve functions. LP (Low-pass) filtering is applied to the crankshaft noise and intake noise. Waveguides are modelled by bidirectional delay lines. The output of one delay line is split into 1. the input of the delay line going the opposite way and 2. the output of the waveguide. This splitting factor is sometimes called alpha.

ashwinb-git commented 3 years ago

The paper says "Sound generation starts in the waveguides representing the cylinders". So, shouldn't the cylinder be a waveguide as well? I understand that the piston and fuel sound is the input to the cylinder. And when the intake valve is open, that sound is modulated by the intake valve function sent to the corresponding waveguide. Same goes for the exhaust side as well. According to the paper, when the valves are closed, the energy is fed back to the delay lines. So when both valves are closed, what happens to the sound sample generated in the cylinder?

DasEtwas commented 3 years ago

So when both valves are closed, what happens to the sound sample generated in the cylinder?

It is discarded.

ashwinb-git commented 3 years ago

Thanks! Interesting. arent the piston and fuel sound themselves one of the three sound components/outputs? And with the waveguides, I understand that you are using vector arrays to delay each sample. Or how do you delay them?

DasEtwas commented 3 years ago

arent the piston and fuel sound themselves one of the three sound components/outputs?

No, exhaust and intake outpus come from waveguides connected to pistons. Only the engine vibrations output is directly LP-filtered piston noise.

I understand that you are using vector arrays to delay each sample.

Ring buffers, yes (I called them loop buffers at the time)

ashwinb-git commented 3 years ago

Thanks! It helped me a lot in designing the intake side. Now for the exhaust side, the samples modulated by the exhaust valve function, go to the straight pipe which is a wave guide right? and then the muffler elements which are also waveguides I think. But I couldn't understand how they are structured.

DasEtwas commented 3 years ago

I couldn't explain it better than Figure 5 on page 4 of the linked paper in the readme.

ashwinb-git commented 3 years ago

I understand that the exhaust samples modulated by the ex_valve are sent as inputs to the straight pipe (which is a waveguide). The output from the pipe is sent to all 4 muffler elements and the muffler elements are also 4 waveguides each with a fixed delay right? how did you fix the delay of each muffler element?

DasEtwas commented 3 years ago

Your rhetoric questions are correct. The muffler delays are user settings.

ashwinb-git commented 3 years ago

Great! Then all these muffler outputs are added to produce the exhaust sound right? And also how do u set the length of the loop buffers i.e delays for the waveguides in general?

DasEtwas commented 3 years ago

I assume constant speed of sound in all wave guides regardless of pressure and use the speed of sound of air at sea level to mimic the time it takes for a wave to travel a certain distance.

ashwinb-git commented 3 years ago

That's a great idea! And I got the way you calculate the number of samples to delay from the code. I have designed the intake side with only one cylinder as of now. Do you play the sound by each sample or do you get all the samples and play it together? (I am using matlab's sound function). I also wanted to know how you control the loop for sound generation?

DasEtwas commented 3 years ago

As with every typical DSP program, sound is processed in batches. The "loop" runs at a constant sample rate.

ashwinb-git commented 3 years ago

Alright. I designed one cylinder. And for more cylinders how did you calculate the phase shift of each one?

edit- I designed the entire thing. Thank you so much for your help!

DasEtwas commented 3 years ago

Please understand I am not keen on providing a walkthrough on how to program a generator. Looking at a real engine's crank will give you a good visual answer.