Checking if a sample is being loaded

I just added this to my API wishlist:

My problem is that I want to explicitly set the looping mode of a sample, once it has been loaded.
But loading a sample in Renoise will set the loop after the sample has been loaded, which means that even with a clever method that probes the sample every few milliseconds will fail if the sample takes a long time to load.

I’m yet to come up with a solution that doesn’t involve super-complex hacks, but maybe someone else has an idea?

I made an attempt with the loop locking mode here:
http://forum.renoise…post__p__312517

Perhaps it might help you brewing another idea what to use instead of monitoring samples being loaded.
It simply boils down to:Monitor the loop mode and reset it if it changes.

Hey vV, thanks! That isn’t a bad idea - in my tool, I’m already adding a bunch of notifiers so why not one more?

The only problem I could imagine was for someone to actually change the loop mode while the sample was loading. But, pfff.

Yesterday, I did try to implement a loop_mode notifier - but it’s not perfect.
Well, it does sort of work but I can’t reliably say if the loop mode is being set by the program or the user.

Some further investigation into the order of notifiers:

Creating a sample via waveform editor (no existing sample)
instr.name_observable fired…

Cutting entire sample via waveform editor
sample.sample_buffer_observable fired…
instr.name_observable fired…

Deleting a sample from the instrument
instr.name_observable fired…
instr.samples_observable fired…

Clear an instrument via instr. list
instruments_observable fired…
instruments_observable fired…
instruments_observable fired…
etc.

Loading a sample on empty slot
instr.name_observable fired…

Loading same sample again (sample notifiers already attached)
sample.loop_mode_observable fired…
sample.beat_sync_enabled_observable fired…
sample.sample_buffer_observable fired…

Loading another sample on top (sample notifiers already attached)
sample.loop_mode_observable fired…
sample.beat_sync_enabled_observable fired…
sample.beat_sync_lines_observable fired…
instr.name_observable fired…

Loading sample into a custom named instr.slot (sample notifiers already attached)
sample.loop_mode_observable fired…
sample.beat_sync_enabled_observable fired…
sample.beat_sync_lines_observable fired…

Loading sample into a custom named instr.slot (no existing sample)
instr.samples_observable fired…

Bottom line: it is very tricky to set up a reliable detection of newly loaded samples. Using a combination of various notifiers, and then checking against these to determine the action to take is possible, but still tricky to implement.

I would have expected the sample_buffer notifier to fire more often, too. This would at least make it possible to exclude harmless actions such as renaming an instrument, setting the loop mode.

The sample_buffer_observable is commonly used to do something with the existing buffer and if the original content is no longer available, it makes no sense to trigger it.

If you want to make it less tricky and more secure, simply create a 3-dimensional array which contains the following fields:

instrument[instrumentno][sampleslot][contains_data] = True/False
instrument[instrumentno][sampleslot][crc-byteoffset] = Random offset location in sample_buffer
instrument[instrumentno][sampleslot][crc-hash] = Calculated hash-code based on a small block of data from that sample buffer

You could base the crc-hash on a block of 256 bytes or 512 bytes (or the maximum bytes if shorter) on any random spot in the sample (usually not the very first beginning or last block!), but at least small enough so that if someone would have large vocal samples, the routine is not continuously crouching all the instruments to check if the current sample data is still matching the current crc code.
Usually no two samples are the same, but in case someone is chopping up a breakbeat, the start and end transient of the sample might match, hence the random byte-offset to pick a block not somewhere at the beginning (but do pick that same block when reference-checking).

I stole some crc-32 code from here in case you don’t want to bother whipping up a crc hash generation algorithm.

  
do  
 local bit_band, bit_bxor, bit_rshift, str_byte, str_len = bit.band, bit.bxor, bit.rshift, string.byte, string.len  
 local consts = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }  
  
 function MyAddon:CRC32(s)  
 local crc, l, i = 0xFFFFFFFF, str_len(s)  
 for i = 1, l, 1 do  
 crc = bit_bxor(bit_rshift(crc, 8), consts[bit_band(bit_bxor(crc, str_byte(s, i)), 0xFF) + 1])  
 end  
 return bit_bxor(crc, -1)  
 end  
end  
  

And if the [contains_data] field is the opposite of what you stored (or nil), you ofcourse don’t need to use CRC verification, but simply generate a new one (or clear it).

Hm, I rechecked all my above examples and the sample_buffer notifier is fired in more than just a single case. I think overlooked this, as there were a lot of notifiers being fired first (loop_mode, for example, gets fired multiple times during the load of a sample).
But it still isn’t a sure-fire way to detect a new sample being loaded - for example, it doesn’t happen when a sample has been named something else than the sample name. It would at least be nice if samples could behave the same, no matter the name they have. Obviously, loading a sample ‘into’ a named instrument would not trigger the instr.name observable, but otherwise this difference seems a bit strange.

But for now, my tool sets a flag when any conditions arise that could be owed to a sample being loaded. Then, with a little delay, it will in fact attach to the sample. It works, but obviously comes with a lot of false positives. And so I guess calculating the checksum could become useful, so thanks for digging up that code!