Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More PPQN? #1530

Closed
softrabbit opened this issue Dec 30, 2014 · 14 comments
Closed

More PPQN? #1530

softrabbit opened this issue Dec 30, 2014 · 14 comments
Milestone

Comments

@softrabbit
Copy link
Member

Right now LMMS is running 192 ticks/whole note = 48 PPQN (parts per quarter note). It might be a bit on the low side for recorded MIDI and things like that, and it looks like something like 960 PPQN is a pretty common number in other software.

Now, what would changing this mean? Some speculation:

  • Project files wouldn't be backwards compatible unless some "save for LMMS 1.1" feature is implemented. Older projects would of course be converted on load.
  • Non-integer tempo could become easier to implement, still counting in integers behind the scenes (960 PPQN should get close enough to 0.01 BPM resolution)
  • Swing should as well be simpler in integer ticks.
  • @diizy, any deep issues you can think of right away, or other reasons to shoot this down?
@diizy
Copy link
Contributor

diizy commented Dec 30, 2014

On 12/30/2014 03:38 PM, Raine M. Ekman wrote:

Right now LMMS is running 192 ticks/whole note = 48 PPQN (parts per
quarter note). It might be a bit on the low side for recorded MIDI and
things like that, and it looks like something like 960 PPQN is a
pretty common number in other software.

When it comes to recorded MIDI, tick count isn't actually the biggest
bottleneck - period size plays a bigger part, since we record incoming
MIDI once per period... so just increasing the tick count probably won't
do any good when it comes to recording accuracy - we'd need to actually
read some time stamps from the incoming MIDI, I don't think we're doing
that currently.

Now, what would changing this mean? Some speculation:

  • Project files wouldn't be backwards compatible unless some "save
    for LMMS 1.1" feature is implemented. Older projects would of
    course be converted on load.

Backwards compat will break in 2.0 anyway, so if we want to reconsider
the tick count, it's doable - we just have to do it in 2.0.

  • Non-integer tempo could become easier to implement, still counting
    in integers behind the scenes (960 PPQN should get close enough to
    0.01 BPM resolution)

That's not actually related. Non-integer tempo can be implemented just
as easily, no matter what tick count we use - we use floating point
values for the "frames per tick" value so any tempo value can already be
implemented accurately.

Non-integer tempo is actually just a matter of coding a widget that
supports decimals - that's it, that's really all it takes.

  • Swing should as well be simpler in integer ticks.
  • @diizy https://github.com/diizy, any deep issues you can think
    of right away, or other reasons to shoot this down?

Changing the number of ticks is not a small endeavour and involves lots
of fiddling with the core engine... plus, there may be performance
considerations? If we have to process 2x as many ticks in the same
timeframe, that could mean some increased overhead? So before we go nuts
and make the tick count something outrageous, maybe we should run some
tests to see how increased ticks affects performance...

@softrabbit
Copy link
Member Author

When it comes to recorded MIDI, tick count isn't actually the biggest bottleneck - period size plays a bigger part, since we record incoming MIDI once per period...

120 BPM, 48 ticks = 10.4 ms/tick = 500 frames/tick at 48 kHz. More if the tempo is slower or sampling rate is higher, so a tick can easily be several times the period size. JACK MIDI has sample-exact events, so this could be even more relevant when (if?) that gets implemented.

Changing the number of ticks is not a small endeavour and involves lots of fiddling with the core engine

I did grep a bit for DefaultTicksPerTact and hard coded 192s and 48s, it didn't seem that bad. I might've missed some of the finer details.

@diizy
Copy link
Contributor

diizy commented Dec 30, 2014

On 12/30/2014 07:40 PM, Raine M. Ekman wrote:

When it comes to recorded MIDI, tick count isn't actually the
biggest bottleneck - period size plays a bigger part, since we
record incoming MIDI once per period...

120 BPM, 48 ticks = 10.4 ms/tick = 500 frames/tick at 48 kHz. More if
the tempo is slower or sampling rate is higher, so a tick can easily
be several times the period size.

It can, but still increasing the tick count won't increase the accuracy
of recorded MIDI beyond the bottleneck of the period size. Say you have
10 ticks during a period, there will still be only one event recorded
during that period - or, all events recorded during that period will be
recorded as occuring during the same tick. The same will be true no
matter if you have 100 ticks during that period.

JACK MIDI has sample-exact events, so this could be even more relevant
when (if?) that gets implemented.

Yes, but we'd still first need the code to read the timecode from those
events.

Changing the number of ticks is not a small endeavour and involves
lots of fiddling with the core engine

I did grep a bit for DefaultTicksPerTact and hard coded 192s and 48s,
it didn't seem that bad. I might've missed some of the finer details.

Could be. We should still determine the performance implications of
different tick counts. I'm thinking a good choice would be something
that allows the implementation of both triplets and quintuplets (ie.
divisible with 3 and 5). If we start from the existing 48 and multiply
that with 2.5 we'd get 120 per beat, or 480 per whole note.

@SecondFlight
Copy link
Member

As part of a pruning effort, this enhancement request is archived into a dedicated "Better Workflow" checklist here #4877.

@enp2s0
Copy link
Contributor

enp2s0 commented Jul 11, 2019

IMO this should be a setting in Preferences, defaulting to 192 to preserve backwards compatibility.

@SecondFlight
Copy link
Member

FL Studio saves this on a per-project basis, and defaults to 96 if I remember correctly. Raising this number has a fairly significant effect on performance.

@Spekular
Copy link
Member

Spekular commented Aug 18, 2019

As a note to anyone who decides to tackle this, this line is based on the current PPQN:

val = max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing.

A doubled PPQN value should give max(val, -7) instead.

<guesswork> If PPQN is configurable, this needs to be updated whenever PPQN is, and that change needs to propagate down to whoever has called the function. Otherwise the user could cause a crash by lowering the PPQN while snapping to 1/128th bars </guesswork>.

@softrabbit
Copy link
Member Author

Just changing the DefaultTicksPerTact value in MidiTime.h works surprisingly well, with no glaringly obvious problems in playing or rendering, and MIDI import seems fine, too.

This allowed me to do a little unscientific render benchmark, using a MIDI file of close to 7 minutes with 10 SF2 player tracks. I toyed around with the ticks and rendered using default settings, 3 runs each, average user time:

1x      53 seconds
5x      65 seconds
20x     91 seconds

So, there is a bit of a performance hit, but it might be acceptable?

Spots that would need further attention before this being ready for prime time are loading (and saving) of projects, and the song position display, which is way off. Probably a few other places as well.

@teknopaul
Copy link

I have an alternative to increasing PPQ that seems to resolve "a bit on the low side for recorded MIDI and things like that"

In Note.cpp add f_cnt_t m_noteOffset; which is a sample frames offset/delay for starting the note.

When creating a NotePlayHandle add the note's offset.

PlayHandle( PlayHandle::Type::NotePlayHandle, _offset + n.getNoteOffset()),

This essentially means a note's start point is sample precise, we can play a note at any position in the track not limited in any way.

Once this simple change is made setting a note's offset is a lot fun.

Naturally, you can record with close to this precision, CPU delay impacts the recorded offset, but if you know it, you can compensate. On my PC this seems fast enough to record piano being played with no quantizing at all. roughly milisecond precise at 127bpm

code for recording is somethign like this

double offsetSeconds = ((double)Engine::getSong()->getMilliseconds() - n1.pos().getTimeInMilliseconds(Engine::getSong()->getTempo())) / 1000.0;
n1.setNoteOffset(Engine::audioEngine()->processingSampleRate() * offsetSeconds);

You can also nudge notes around, so you have fine grained control of start position if that helps.

You can also apply fine grained groove quantizing to make "funky" (swing) rhythms without having to program notes at the ppq level.

Humanizing not timings is easy when offset is there, its just adding some random frames to the offset

This branch
allmystuff

has it working if anyone want to try it out

@teknopaul
Copy link

quantize-screenshot

You have to turn off completly quantizing for it to record offsets. Any of the existing quantizing functions will naturally set all the offsets to zero

@teknopaul
Copy link

groove-quanitized.zip

example of groove quantizing on and off

@DomClark
Copy link
Member

The trouble with storing note positions as a sample offset from a tick is that samples and ticks can change in relative size. There are three main units of time we have to deal with in LMMS: ticks, samples, and seconds. Ticks and seconds are related to each other by the tempo, and samples and seconds by the sample rate. Thus ticks and samples are related both by the tempo (which can vary within a single project) and the sample rate (which can vary across machines, and even across sessions on the same machine). I think for consistency and simplicity it would be best not to mix units like this.

@teknopaul
Copy link

teknopaul commented Nov 30, 2023 via email

@teknopaul
Copy link

teknopaul commented Nov 30, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants