COCKOS
CONFEDERATED FORUMS
Cockos : REAPER : NINJAM : Forums
Forum Home : Register : FAQ : Members List : Search :
Old 05-30-2016, 06:37 AM   #1
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default IMidiQueue - How to clone it?

Hi there,

I'm creating a IMidiQueue with N notes. Once created, I process it (peak and remove) inside PDR during the time.
Once its empty, I'd like to just reuse it, without "recreating" it every time: so, without creating and add the N notes again (which for my purpose will be identical).

How can I basically "clone" it?

Last edited by Nowhk; 05-30-2016 at 06:46 AM.
Nowhk is offline   Reply With Quote
Old 05-30-2016, 11:28 PM   #2
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

I'm not sure if I understand... Do you want to get to the notes in the queue without removing them?
Tale is offline   Reply With Quote
Old 05-31-2016, 12:04 AM   #3
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
I'm not sure if I understand... Do you want to get to the notes in the queue without removing them?
Hi Tale Thanks for the reply.

Well, basically I've made a sequencer that play notes and loop at some points.
So I create notes and I add them to a IMidiQueue, which will sort due to the offset. Within the PDR, I check the first "incoming" notes with a while (basic MIDI processing), or I break the process:

Code:
	while (!queuedNotes.Empty()) {
		IMidiMsg* note = queuedNotes.Peek();
		if (note->mOffset > myOffset) break;
		
		SendMidiMsg(note);
		queuedNotes.Remove();
	}
The problem is that once I finish the note and I loop the pattern, I need to re-process the same midi notes (which I've removed).

I'd like to avoid re-creating them at PDR (since could take lots of resources), and just wrap or clone the IMidiQueue.

Is it more clear now?
Nowhk is offline   Reply With Quote
Old 05-31-2016, 12:50 AM   #4
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Yeah, got it, thanks. It never occurred to me to use IMidiQueue for a sequencer, that's smart.

Anyway, I guess you could do something like:

Code:
if (!queuedNotes.Empty())
{
	for (;;)
	{
		IMidiMsg* note = queuedNotes.Peek(mSeqIndex);
		if (note->mOffset - mSeqOffset > myOffset) break;

		SendMidiMsg(note);
		if (++mSeqIndex >= queuedNotes.ToDo())
		{
			mSeqIndex = mSeqOffset = 0;
		}
	}
	mSeqOffset += myOffset;
}
You would have to init mSeqIndex(0) and mSeqOffset(0), and you need to add Peek(int) to IMidiQueue:

Code:
inline IMidiMsg* Peek(int index) const
{
	return &mBuf[mFront + index];
}
Tale is offline   Reply With Quote
Old 05-31-2016, 01:04 AM   #5
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
Yeah, got it, thanks. It never occurred to me to use IMidiQueue for a sequencer, that's smart.

Anyway, I guess you could do something like:

Code:
if (!queuedNotes.Empty())
{
	for (;;)
	{
		IMidiMsg* note = queuedNotes.Peek(mSeqIndex);
		if (note->mOffset - mSeqOffset > myOffset) break;

		SendMidiMsg(note);
		if (++mSeqIndex >= queuedNotes.ToDo())
		{
			mSeqIndex = mSeqOffset = 0;
		}
	}
	mSeqOffset += myOffset;
}
You would have to init mSeqIndex(0) and mSeqOffset(0), and you need to add Peek(int) to IMidiQueue:

Code:
inline IMidiMsg* Peek(int index) const
{
	return &mBuf[mFront + index];
}
I see. Thanks. But for my code this will mess a bit the whole mechanism. What about this instead:

I send midi
I change offset adding the lenght of the pattern
I add the note to the queue (thus, at the end of the queue)
I call remove

In this way, I wrap the notes (adding them) at the end, with the right offset. Would this so expensive? I call "Flush" (so Compact) when looping. This will free the old and no more used memory...
Nowhk is offline   Reply With Quote
Old 05-31-2016, 06:54 AM   #6
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by Nowhk View Post
In this way, I wrap the notes (adding them) at the end, with the right offset. Would this so expensive? I call "Flush" (so Compact) when looping. This will free the old and no more used memory...
IMidiQueue is really light-weight (e.g. remove just updated an index), so I would not expect any problems, even if you do fill it again every process call. I would make sure you use it as a member variable, that way it only initially allocates some memory, and never frees it again until your plug-in is destructed.
Tale is offline   Reply With Quote
Old 05-31-2016, 10:09 AM   #7
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
IMidiQueue is really light-weight (e.g. remove just updated an index), so I would not expect any problems, even if you do fill it again every process call.
I know. I only free (flush) the memory one time per loop. But I add a note every time I process another one. Add "copy" the whole element and put it at the end, but it also seems to be a light task.

If I use a std::vector for example, and I create new One (deleting the previous) it glitch the whole process. It seems to be a lot heavy than your queue.

Do you agree that those kind of containers should be avoid working with audio?
Nowhk is offline   Reply With Quote
Old 06-01-2016, 03:09 AM   #8
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by Nowhk View Post
I know. I only free (flush) the memory one time per loop. But I add a note every time I process another one. Add "copy" the whole element and put it at the end, but it also seems to be a light task.
FYI: Flush doesn't actually free memory, but it does move the MIDI messages still in the queue.

Quote:
Originally Posted by Nowhk View Post
If I use a std::vector for example, and I create new One (deleting the previous) it glitch the whole process. It seems to be a lot heavy than your queue.

Do you agree that those kind of containers should be avoid working with audio?
Yeah, I agree. Then again, I am not very fond of STL in general anyway, so I try to avoid them anyway...
Tale is offline   Reply With Quote
Old 06-01-2016, 03:13 AM   #9
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
FYI: Flush doesn't actually free memory, but it does move the MIDI messages still in the queue.
Really? I see this:

Code:
  // Removes a MIDI message from the front of the queue (but does *not*
  // free up its space until Compact() is called).
  inline void Remove() { ++mFront; }
and inside Flush there's Compact(). So in fact I'm filling memory (uselessly) at each "Add" (since Remove doesn't free it up)? Wouldn't it better (working with audio) free up memory once I remove a note?
Nowhk is offline   Reply With Quote
Old 06-01-2016, 03:21 AM   #10
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Well, it does move MIDI messages to the front of the queue (thus freeing up space at the end of the queue), but it doesn't call free().

Last edited by Tale; 06-02-2016 at 04:32 AM.
Tale is offline   Reply With Quote
Old 06-02-2016, 12:27 AM   #11
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
Well, it does move MIDI messages to the front of the queue (thus freeing up space at the end of the queue), but it does call free().
Does or doesnt? Typo?
Nowhk is offline   Reply With Quote
Old 06-02-2016, 04:32 AM   #12
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by Nowhk View Post
Does or doesnt? Typo?
Doesn't, oops! I will edit my post...
Tale is offline   Reply With Quote
Old 06-02-2016, 10:29 AM   #13
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
Doesn't, oops! I will edit my post...
So, every time I add a note it makes a copy of the IMidiMsg, but never destroy it ? After 1000 note Add and 900 remove I still have allocated 1000 objects without removing any of them :O Is it good for an audio application?
Nowhk is offline   Reply With Quote
Old 06-02-2016, 11:34 AM   #14
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Usually you allocate memory during construction, or in Reset() by calling mMidiQueue.Resize(GetBlockSize()). This memory will indeed not be freed again until your plug-in is closed. This is good for audio, because (re)allocating during processing could results in pops/clicks.
Tale is offline   Reply With Quote
Old 06-03-2016, 06:32 AM   #15
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
Usually you allocate memory during construction, or in Reset() by calling mMidiQueue.Resize(GetBlockSize()). This memory will indeed not be freed again until your plug-in is closed. This is good for audio, because (re)allocating during processing could results in pops/clicks.
I see. Thanks for all infos

Where do you allocate memory in ctor for IMidiQueue? I see the Expand() method, which should serve the cause (using realloc method). But before call it, you set mSize to 0, so size is 0/something = 0, which seems to allocate nothing :O I miss a point...

Last edited by Nowhk; 06-03-2016 at 06:45 AM.
Nowhk is offline   Reply With Quote
Old 06-03-2016, 02:35 PM   #16
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

The idea is that you either call mMidiQueue.Resize(expected_max) in your constructor, and/or in Reset() if you want expected_max to be block size dependent. That way malloc() is only called before processing any audio, assuming that the queue will never grow beyond expected_max. If it does grow beyond that at some point, then Expand() will realloc() during audio processing.
Tale is offline   Reply With Quote
Old 06-05-2016, 10:35 AM   #17
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
The idea is that you either call mMidiQueue.Resize(expected_max) in your constructor, and/or in Reset() if you want expected_max to be block size dependent. That way malloc() is only called before processing any audio, assuming that the queue will never grow beyond expected_max. If it does grow beyond that at some point, then Expand() will realloc() during audio processing.
Never call resize on my class. So basically what Ive do till now is to create a IMidiQueue of 1 and expand it every time I add a new note? I thought it is already allocated with DEFAUL_BLOCK_SIZE, but in fact default (init) size is 1.

Edit: oh no wait, its already allocated with DEFAULT_BLOCK_SIZE: theres * mGrow within Expand, didnt see it.

Last edited by Nowhk; 06-05-2016 at 10:41 AM.
Nowhk is offline   Reply With Quote
Old 06-05-2016, 10:48 AM   #18
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Yeah... Note that if you don't explicitly call Resize(), then the very first call Add() will allocate memory. I guess you are calling Add() from within the audio thread, and during processing? Then it probably would be better to call Resize() during plug-in construction, or in your plug-in's Reset().
Tale is offline   Reply With Quote
Old 06-05-2016, 11:20 AM   #19
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
Yeah... Note that if you don't explicitly call Resize(), then the very first call Add() will allocate memory.
Really? I see Expand (called in the CTOR) use realloc, so it should already allocate memory there... no?
Nowhk is offline   Reply With Quote
Old 06-05-2016, 01:09 PM   #20
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by Nowhk View Post
Really? I see Expand (called in the CTOR) use realloc, so it should already allocate memory there... no?
Yes, you are right of course (doh!). Hey, I just write this stuff, I don't really know how it works...
Tale is offline   Reply With Quote
Old 06-06-2016, 06:39 AM   #21
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by Tale View Post
Yes, you are right of course (doh!). Hey, I just write this stuff, I don't really know how it works...
No problem. So I'm ok, it already allocate 1024 IMidiMsg when I declare it. I think I'm good this way, since I'll use/wrap max 10 notes. Even if I do 8000 Add() (followed by parallel Remove) it will always use this memory without grows more. Great! Thanks dude!
Nowhk is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 04:56 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.