DBraun / DawDreamer

Digital Audio Workstation with Python; VST instruments/effects, parameter automation, FAUST, JAX, Warp Markers, and JUCE processors
GNU General Public License v3.0
850 stars 67 forks source link

Get MemoryError when rendering multiple file #117

Open Centauria opened 1 year ago

Centauria commented 1 year ago

My code structure is:

class Player:
    ......
    def render(self, midi):
        engine = dawdreamer.RenderEngine(44100, 128)
        for track in midi:
            plugin = engine.make_plugin_processor(get_name(track), get_vst(track))
            for note in track.notes:
                plugin.add_note(*get_note_info(note))
        mix = engine.make_add_processor('mix', [1] * len(midi))
        engine.load_graph(get_graph(midi))
        engine.render(midi.get_end_time())
        return engine.get_audio()

for midi in midi_list:
    wav = player.render(midi)
    save(wav, wav_file)

When I run this code, it will run normally first. After an hour or two, It will get a MemoryError: bad allocation.

I wonder how can I avoid this.

My computer have 24GB memory, and usually have 14GB available for my code.

DBraun commented 1 year ago

Darn, that could be a tough one. It could be an issue with any of the VSTs, or it could be a DawDreamer issue. I'll revisit the relevant code when I get a chance. In the meantime, I hope you aren't blocked by this. You could possibly split the midi list into multiple BIG parts (each one taking ~30 min) and execute each part with python's subprocess.

Centauria commented 1 year ago

Further test shown as follows:

After rendering total 4226 seconds of audio, program exited with error 0xC0000409 when (loading preset of) Ample Guitar L. Then I removed all usage of Ample guitar & bass plugins, and run it again. After rendering 2507 sec, program exited with error 0xC0000005 when (loading preset of) Roland JUNO-106. After rendering 3828 sec, raised MemoryError when making plugin of JUNO-106. After rendering 2853 sec, raised MemoryError when making plugin of TR808. After rendering 3093 sec, program exited with error 0xC0000005 when (loading preset of) Serum.

I will update latest test result here. Hope it's helpful.

DBraun commented 1 year ago

Thanks, then it seems like a problem with DawDreamer.

DBraun commented 1 year ago

I don't have a full solution yet. Here's a script I was using. I was looking at the Task Manager in Windows and watching the memory usage for a Windows Command Prompt slowly rise. I was noticing the peak value go up by 0.1 MB every 15 seconds or so. It varies per plugin.

import dawdreamer as daw

plugin_path = "C:/VSTPlugins/Serum_x64.dll"

class Player:

    def render(self):

        engine = daw.RenderEngine(44100, 128)

        plugin = engine.make_plugin_processor("plugin", plugin_path)

        # # eventually want to make sure these don't leak either
        # plugin.add_midi_note(60, 60, 0.0, .25)
        # plugin.add_midi_note(64, 80, 0.5, .5)
        # plugin.add_midi_note(67, 127, 0.75, .5)

        # engine.load_graph([(plugin, []),])

        # engine.render(.01)
        # return engine.get_audio()

player = Player()
i = 0
while True:

    player.render()
    if i % 100 == 0:
        print('i', i//100)
    i += 1

The issue must be related to the Plugin Processor specifically because replacing the plugin creation line with

plugin = engine.make_add_processor("plugin", [0., 1.])

results in no leak.

The Sampler Processor has a lot in common with the Plugin Processor such as midi buffers, and yet it doesn't leak in the setup above.

import numpy as np
plugin = engine.make_sampler_processor("plugin", np.zeros((2,128)))

I haven't figured out what's wrong with the plugin processor class... I tried myPlugin.get()->releaseResources(); before each call to myPlugin.reset(); in the C++ source but that didn't fix it either.

If anyone is running a large render batch with DawDreamer, I'd recommend writing your code so that plugins aren't repeatedly created and deleted. Create an engine, create the plugins you need, and reload the graphs as necessary however many times you want. I don't think that style of code is leaking.