SuperDirt icon indicating copy to clipboard operation
SuperDirt copied to clipboard

sample repitching using smpl chunk metadata and tune argument

Open ahihi opened this issue 2 years ago • 3 comments

implements #273, now with the simplified approach suggested by @telephon.

ahihi avatar Jul 05 '22 16:07 ahihi

thank you for the thoughtful review, i am learning a lot! :D

ahihi avatar Jul 08 '22 19:07 ahihi

let me know what you think about this version!

ahihi avatar Jul 08 '22 21:07 ahihi

thank you for the thoughtful review, i am learning a lot! :D

great :) The code is already very good, so it is just some tweaking.

telephon avatar Jul 16 '22 17:07 telephon

sorry for the long delay! ive now addressed your previous comments.

still TBD is the name of the argument for enabling this feature :) do you think metatune is acceptable? (ref. https://github.com/musikinformatik/SuperDirt/pull/274#discussion_r917135727)

ahihi avatar Dec 04 '22 23:12 ahihi

while using this in my own livecoding, i noticed incorrect durations for some tuned notes.. i fixed this before but i think https://github.com/musikinformatik/SuperDirt/pull/274/commits/b53a29f89174cda02429a82e77a7c6a92e84473c broke it again. will take a look at it soon

ahihi avatar Dec 18 '22 02:12 ahihi

note durations are now fixed. in the absence of further comments on the argument name i also took the liberty of renaming it to metatune. unless you would like some other changes i believe this should be ready to merge now!

ahihi avatar Apr 15 '23 22:04 ahihi

i agree it would be better, but i had some trouble making it work that way (the commit was sitting on my computer for a while so i dont remember the details anymore). i will give it another shot

ahihi avatar Apr 16 '23 09:04 ahihi

right, the problem is that by the time the module function is invoked, DirtEvent.calcTimeSpan has already happened and it is too late to modify unitDuration.

i wrote down this pseudocode summary of the event construction control flow, in case it is helpful:

DirtOrbit.init {
    makeDefaultParentEvent {
        defaultParentEvent = Event.make {
            ...
            ~midinote = #{ ~note ? ~n + (~octave * 12) }
            ~freq = #{ ~midinote.value.midicps }
            ...
        }
    }
}

DirtSoundLibrary.loadSoundFile {
    readSoundFile
    readMetaData
    addBuffer {
        bufferEvents[name] = makeEventForBuffer {
            builds event object, includes:
            - baseFreqToMetaFreqRatio (constant)
            - unitDuration (lambda, calls ~freq.value)
        }
    }
}

SuperDirt.connect {
    defines playFunc { |msg, ...|
        event = (latency: ...)
        event.putPairs(msg[1..])
        receiveAction.(event) // receiveAction is nil by default -> no-op
        DirtEvent(..., event).play {
            event.parent = orbit.defaultParentEvent
            event.use {
                // ignoring ~diversion != nil case
                mergeSoundEvent {
                    currentEnvironment.proto = orbit.dirt.soundLibrary.getEvent
                }
                orderTimeSpan // swaps ~begin and ~end if ~speed < 0
                calcTimeSpan {
                    applies ~unitDuration
                }
                playSynths {
                    modules.do(_.(this)) {
                        sound module called
                    }
                }
            }
        }
    }
    registers playFunc as OSC handler for /play2
}

ahihi avatar Apr 16 '23 12:04 ahihi

Ah yes, I see.

Maybe there is a better way to do this.

Can you try this:

~dirt.set(\freq, { ~midinote.value.midicps * ~metaTuneRatio.value }, \metaTuneRatio, 1.0);

and then you can just set the metaTuneRatio in the SoundLibrary, I think.

If that works, we can extend the frequency calculation model of superdirt in DirtOrbit accordingly.

COMMENT: actually, thinking about it, it is better as it is in this pull request. Saves some energy and keeps things together. I add some minor comments.

telephon avatar Apr 18 '23 10:04 telephon

here is an example that perhaps helps clarify how this works in practice.

samples used: https://share.pulusound.fi/metatune-samples.zip

contains a subdirectory foo with four samples. this is their smpl metadata:

$ find samples -iname '*.wav' -exec pitcheon '{}' ';'

samples/foo/00 hypersaw.wav
  1 smpl chunk(s)
    [0] note: 50.000 / freq: 146.832Hz / smpl: (50, 0)

samples/foo/01 bell.wav
  1 smpl chunk(s)
    [0] note: 71.410 / freq: 505.719Hz / smpl: (71, 1760936591)

samples/foo/02 piano.wav
  0 smpl chunk(s)

samples/foo/03 pluck.wav
  1 smpl chunk(s)
    [0] note: 68.000 / freq: 415.305Hz / smpl: (68, 0)

note that 02 piano.wav has none, and all the others are have different pitches.

superdirt startup code:

(
s = Server.local;
s.options.sampleRate_(44100);
s.options.memSize = 1024 * 1024;
s.options.numBuffers = 1024 * 256;
s.options.numWireBufs = 128;
s.options.maxNodes = 1024 * 32;
s.latency_(0.205);
s.newBusAllocators;

s.waitForBoot {
	~dirt = SuperDirt(2, s);
	~dirt.loadSoundFiles(thisProcess.nowExecutingPath.dirname +/+ "samples/*");
	s.sync;
	~dirt.start(outBusses: 0 ! 12);
};
)

after this, ~dirt.soundLibrary.metaDataEvents looks like:

IdentityDictionary[
  (foo -> Dictionary[
    (0 -> ( 'baseFreqToMetaFreqRatio': 1.7817974362766, 'midinote': 50.0 )),
    (1 -> ( 'baseFreqToMetaFreqRatio': 0.51733355006402, 'midinote': 71.4100000656 )),
    (3 -> ( 'baseFreqToMetaFreqRatio': 0.6299605249486, 'midinote': 68.0 ))
  ])
]

tidal pattern:

d1
  $ n "[0 1 2 3]*2"
  # s "foo"
  # legato 1
  # pF "metatune" 0

compare values 0 and 1 for metatune. the piano sample is unaffected due to the aforementioned lack of metadata.

ahihi avatar Sep 18 '23 19:09 ahihi

good question! i forgot about the synth case. it might be best to factor out the dictionary-manipulating code into a function that is used by both addBuffer and addSynth.

i also recently noticed metadata not getting correctly updated when reloading samples from a folder which has been loaded before, will look into that too.

ahihi avatar Oct 01 '23 13:10 ahihi

how about this?

i also recently noticed metadata not getting correctly updated when reloading samples from a folder which has been loaded before, will look into that too.

i could not reproduce this anymore, maybe i just did something wrong last time.

ahihi avatar Oct 03 '23 04:10 ahihi

Thank you! This is good.

telephon avatar Oct 11 '23 07:10 telephon