If you have a problem or need to report a bug please email : support@dsprobotics.com
There are 3 sections to this support area:
DOWNLOADS: access to product manuals, support files and drivers
HELP & INFORMATION: tutorials and example files for learning or finding pre-made modules for your projects
USER FORUMS: meet with other users and exchange ideas, you can also get help and assistance here
NEW REGISTRATIONS - please contact us if you wish to register on the forum
Users are reminded of the forum rules they sign up to which prohibits any activity that violates any laws including posting material covered by copyright
Shared Mem Wavetable Oscillator
9 posts
• Page 1 of 1
Shared Mem Wavetable Oscillator
Hi guys,
I have been trying to build a wavetable oscillator based on Trog's shared mem concept. One problem is how to create and populate the wavetable mem so it can be used in a poly section. Tulamide has provided a nice Ruby solution, where memory is not only allocated for 16 byte aligned access. The same module also actually writes data from a green array into the mem. Only after the writing is completed is a valid address passed to an ASM module for mem access. That way crashes are eliminated (well, less likely ).
Below is a demo which uses linear interpolation and some SSE trickery for speed optimization. It turns out that there is actually room for two waveforms stored in one array. I am posting it eventhough it is half-baked, as there is similar activity in another thread.
I have been trying to build a wavetable oscillator based on Trog's shared mem concept. One problem is how to create and populate the wavetable mem so it can be used in a poly section. Tulamide has provided a nice Ruby solution, where memory is not only allocated for 16 byte aligned access. The same module also actually writes data from a green array into the mem. Only after the writing is completed is a valid address passed to an ASM module for mem access. That way crashes are eliminated (well, less likely ).
Below is a demo which uses linear interpolation and some SSE trickery for speed optimization. It turns out that there is actually room for two waveforms stored in one array. I am posting it eventhough it is half-baked, as there is similar activity in another thread.
- Attachments
-
- SharedMem_Ruby_Final_4_Arrays (base 2)_Modul_.fsm
- (25.77 KiB) Downloaded 1248 times
-
martinvicanek - Posts: 1328
- Joined: Sat Jun 22, 2013 8:28 pm
Re: Shared Mem Wavetable Oscillator
Very, very nice! I do not understand the "lock" hack
Perhaps it is not necessary to put 4 wavetables in one array. with clever bitwise logic it is possible to shuffle the correct value to all channels even when mem is not 4channel. However, the code is rather lengthy and I've not written it down (a lot of shuffling and I don't have the nerves for it right now xD ).
Perhaps it is not necessary to put 4 wavetables in one array. with clever bitwise logic it is possible to shuffle the correct value to all channels even when mem is not 4channel. However, the code is rather lengthy and I've not written it down (a lot of shuffling and I don't have the nerves for it right now xD ).
- KG_is_back
- Posts: 1196
- Joined: Tue Oct 22, 2013 5:43 pm
- Location: Slovakia
Re: Shared Mem Wavetable Oscillator
KG_is_back wrote:I do not understand the "lock" hack
We are dealing with two parts that are independant from each other: Ruby and Assembler. Ruby doesn't know what Assembler does and vice-versa. Now, memory allocated by Ruby (whenever we use something, we allocate memory) will be controlled by Ruby. Therefore it is critical if we pass access to memory created with Ruby to Assembler.
Older languages used a pretty simple mechanism: The programmer allocates memory, uses it, and frees it after use. This can get pretty complicated for the programmer, should he/she forget to free memory. And in fact, it was often the reason for so called memory leaks.
Ruby goes another path. Pretty much every memory access is automated, so that the programmer doesn't have to care about it. For example, you just call "class.new" and Ruby takes care of allocating memory for the class structure. Since you don't have direct access to memory, another system is also needed to free memory. Ruby uses a system called "garbage collection".
Basically, whenever memory is allocated, information about it is put on a queue. Everything on the queue is checked regularly if still valid. If not, the memory is freed and the information removed from the queue. For example, if we did something like a = Array.new(100) and then somewhere later in the code do a = 2, then the array and its associated memory is no longer valid. Ruby then will free that memory. But memory that is freed will be used by the os or other applications, processes, and so on. But Assembler still works with that memory, so it would lead to issues and crashes.
By accessing the frame every once in a while from within Ruby the garbage collector will never get a chance to free the memory associated with the frame - which is what is needed to let Assembler work with that memory. Ruby acts as a memory protector for the asm module, so to say.
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
Re: Shared Mem Wavetable Oscillator
Oh... so because the frame is regularly used by event method (in this case by the "watch" method inside it) it is protected from getting "expired" like a milk on a summer sun.
I thought objects in instance variables are protected from GC. Does this happen, because when rubyedit instance is not active for some time its instance variables may get scrapped as unnecessary?
I thought objects in instance variables are protected from GC. Does this happen, because when rubyedit instance is not active for some time its instance variables may get scrapped as unnecessary?
- KG_is_back
- Posts: 1196
- Joined: Tue Oct 22, 2013 5:43 pm
- Location: Slovakia
Re: Shared Mem Wavetable Oscillator
tulamide wrote:KG_is_back wrote:I do not understand the "lock" hack
We are dealing with two parts that are independant from each other: Ruby and Assembler. Ruby doesn't know what Assembler does and vice-versa. Now, memory allocated by Ruby (whenever we use something, we allocate memory) will be controlled by Ruby. Therefore it is critical if we pass access to memory created with Ruby to Assembler.
Older languages used a pretty simple mechanism: The programmer allocates memory, uses it, and frees it after use. This can get pretty complicated for the programmer, should he/she forget to free memory. And in fact, it was often the reason for so called memory leaks.
Ruby goes another path. Pretty much every memory access is automated, so that the programmer doesn't have to care about it. For example, you just call "class.new" and Ruby takes care of allocating memory for the class structure. Since you don't have direct access to memory, another system is also needed to free memory. Ruby uses a system called "garbage collection".
Basically, whenever memory is allocated, information about it is put on a queue. Everything on the queue is checked regularly if still valid. If not, the memory is freed and the information removed from the queue. For example, if we did something like a = Array.new(100) and then somewhere later in the code do a = 2, then the array and its associated memory is no longer valid. Ruby then will free that memory. But memory that is freed will be used by the os or other applications, processes, and so on. But Assembler still works with that memory, so it would lead to issues and crashes.
By accessing the frame every once in a while from within Ruby the garbage collector will never get a chance to free the memory associated with the frame - which is what is needed to let Assembler work with that memory. Ruby acts as a memory protector for the asm module, so to say.
not all true,
the problem of the crash is mainly due to how Flowstone manages memory and especially the case, here less discussed and considered, which is the relocation after the memory is freed.
to test it just to copy and paste some ruby modules that create frame, and you'll see that the memory is not managed by ruby, but by FlowStone, ruby only uses it.
- Tronic
- Posts: 539
- Joined: Wed Dec 21, 2011 12:59 pm
Re: Shared Mem Wavetable Oscillator
Tronic wrote:not all true,
the problem of the crash is mainly due to how Flowstone manages memory and especially the case, here less discussed and considered, which is the relocation after the memory is freed.
to test it just to copy and paste some ruby modules that create frame, and you'll see that the memory is not managed by ruby, but by FlowStone, ruby only uses it.
I know what you mean, because the behaviour you describe is exactly what I prevent with this hack
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
Re: Shared Mem Wavetable Oscillator
Very nice, thanks Martin and tulamide.
This is a solution when using frames.
I prefer to use the mem create with the mem to address, this is more stable. Check out my free running poly oscs this doesn't have any problem with garbage collection because we can create and delete the memory ourselves.
I do have one crashing issue regarding using the "mem to address" directly with a wavetable create and a wave file. I have a crash on load sometimes but I don't think it is related to this issue because I am not using Ruby. But I think I may have an invalid address while the wavetable or wavefile is loaded.
I like the fact that you are outputting zero while the frame is created, I think I will try that because it could solve my stability problem.
But I think the stability problem is also there when using the float array to mem, because the float array is copied to a mem so every time the float array updates the address changes.
This is a solution when using frames.
I prefer to use the mem create with the mem to address, this is more stable. Check out my free running poly oscs this doesn't have any problem with garbage collection because we can create and delete the memory ourselves.
I do have one crashing issue regarding using the "mem to address" directly with a wavetable create and a wave file. I have a crash on load sometimes but I don't think it is related to this issue because I am not using Ruby. But I think I may have an invalid address while the wavetable or wavefile is loaded.
I like the fact that you are outputting zero while the frame is created, I think I will try that because it could solve my stability problem.
But I think the stability problem is also there when using the float array to mem, because the float array is copied to a mem so every time the float array updates the address changes.
- Exo
- Posts: 426
- Joined: Wed Aug 04, 2010 8:58 pm
- Location: UK
Re: Shared Mem Wavetable Oscillator
This is a good solution for most cases though.
Anywhere I would use a "float array to mem" > "mem address" I could replace with this.
But this solution wouldn't be suitable in the case of the wavetable create primitive, because passing it to the Ruby component as a float array would be way to heavy.
The case I am using is a bandlimited wavetable (4096 sample points) that is 32MB in size, Ruby cannot handle that.
So very large arrays are not suitable. But smaller arrays this is perfect
Anywhere I would use a "float array to mem" > "mem address" I could replace with this.
But this solution wouldn't be suitable in the case of the wavetable create primitive, because passing it to the Ruby component as a float array would be way to heavy.
The case I am using is a bandlimited wavetable (4096 sample points) that is 32MB in size, Ruby cannot handle that.
So very large arrays are not suitable. But smaller arrays this is perfect
- Exo
- Posts: 426
- Joined: Wed Aug 04, 2010 8:58 pm
- Location: UK
Re: Shared Mem Wavetable Oscillator
Exo wrote:The case I am using is a bandlimited wavetable (4096 sample points) that is 32MB in size, Ruby cannot handle that.
Well, actually it can. But the time needed is just inacceptable (and the reason for Flowstone to interfear). You encounter the downside of the garbage collector. Before writing a wall of text, have a look at this blog for an explanation: http://www.platanus.cz/blog/working-wit ... es-in-ruby
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
9 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 51 guests