ok, @thetechnobear, i have one other question, if you don't mind. as i wrote i was successful in mallocing some sdram space. my object works great with this. since i though about loading multiple instances that would use the same data, can i also do the [parent -> polyIndex][0]
trick with the array i allocated dynamically to access the data from more than one object? or do i have to initialise a pointer via [attr_poly][LENGTH] attribute ((section (".sdram")))
and point that to the location of the array that i malloced in sdram? either way, i cannot get it to work but maybe this is also not possible. i could not see from your code how/if you do that in the elements code. what happens if you open more then one instance of those?
Two questions...(arrays and where to put them) [SOLVED]
ok, the thing to realise is these 'tricks' are only really necessary because each instance of an object is created as a separate class, and so access is restricted, because that class name is the instance name of the object... so you cant reference it easily.
so for complex objects, i think its much easier to just define a new class in a header file, which you then include (object editor supports include)
that way you know what the class name is called, so you can easily implement a singleton pattern. (which is basically just a static pointer wrapped in a class)
it perhaps sounds complex, but in reality its a couple of lines of code... and also provides a perfect place to put 'common' code e.g. loading your data from the file.
(its a pity i didn't structure the Push object like this, not quite sure why i did it using C functions... was a long time ago )
tip... what i do when looking at this kind of thing, is look at the xpatch.cpp (run it thru a pretty print), it then becomes fairly clear the C++ 'syntax issues' your seeing... I guess that's why I also don't think exclusively in terms of axo object, and more just in how it looks in C++.
lmnts, nah, it (stupidly) loads the data per instance... because i kind of knew you could only have one instance of it... partly due to cpu, and iirc, the MI code has some statics which cause issues with multiple instances.
thanks again. hmm again over my head for the moment. have to read up on that singleton pattern. yeah it sounds complex because i am not a programmer really so i would put all my loading data code and other common stuff into the header file. and create a class where i put in all the static pointers i need and it would only be called once when i create multiple instances? sounds great. phuuh.
i looked at the patch.cpp and i can see all the classes there. so basically something like this should work for my header file?
;class lpc_common{
public:
static uint8_t *lpc_data;
static uint16_t *onset_data;
static uint32_t *word_idx;
static uint8_t wordcount = 0;
void dataInit () {
FIL lpcfile, onsetfile;
uint32_t lpcfile_len, onsetfile_len;
FRESULT err;
UINT bytes_read;
err = f_open(&lpcfile, "0:/lpcdata.sns", FA_READ | FA_OPEN_EXISTING);
if(err != FR_OK) {
LogTextMessage("Unable to open lpcdata, is the file on the sdcard?");
return;
}
lpcfile_len = f_size(&lpcfile);
lpc_data = (uint8_t *)sdram_malloc(lpcfile_len);
// Load lpcdata into sdram
int remaining_bytes = lpcfile_len;
int offset = 0;
while (remaining_bytes > 0) {
if (remaining_bytes > sizeof(fbuff)) {
err = f_read(&lpcfile, fbuff, sizeof(fbuff), &bytes_read);
if (bytes_read == 0)
break;
memcpy((char*)(&lpc_data[0]) + offset,(char *)fbuff,bytes_read);
remaining_bytes -= bytes_read;
offset += bytes_read;
} else {
err = f_read(&lpcfile, fbuff, remaining_bytes, &bytes_read);
memcpy((char*)(&lpc_data[0]) + offset,(char *)fbuff,bytes_read);
remaining_bytes = 0;
}
}
if (err != FR_OK) {
LogTextMessage("Failed reading lpcdata file, aborting...\n");
return;
}
err = f_close(&lpcfile);
if (err != FR_OK) {
LogTextMessage("Failed closing lpcdata file, aborting...\n");
return;
}
//load onsets into sdram
err = f_open(&onsetfile, "0:/onsets.sns", FA_READ | FA_OPEN_EXISTING);
if(err != FR_OK) {
LogTextMessage("Unable to open onsets, is the file on the sdcard?");
return;
}
onsetfile_len = f_size(&onsetfile);
//16bit only needs half the size because f_size reports 8bits(bytes) not 16bit
onset_data = (uint16_t *)sdram_malloc(onsetfile_len/2);
//16bit uses two slots, equals length reported by f_size, since we are writing one byte at a time
remaining_bytes = onsetfile_len;
offset = 0;
while (remaining_bytes > 0) {
if (remaining_bytes > sizeof(fbuff)) {
err = f_read(&onsetfile, fbuff, sizeof(fbuff), &bytes_read);
if (bytes_read == 0)
break;
memcpy((char*)(&onset_data[0]) + offset,(char *)fbuff,bytes_read);
remaining_bytes -= bytes_read;
offset += bytes_read;
} else {
err = f_read(&onsetfile, fbuff, remaining_bytes, &bytes_read);
memcpy((char*)(&onset_data[0]) + offset,(char *)fbuff,bytes_read);
remaining_bytes = 0;
}
}
if (err != FR_OK) {
LogTextMessage("Failed reading onset file, aborting...\n");
return;
}
err = f_close(&onsetfile);
if (err != FR_OK) {
LogTextMessage("Failed closing onset file, aborting...\n");
return;
}
//calculate word indexes from wordlengths and put into sdram
word_idx = (uint32_t*)sdram_malloc(onsetfile_len/2);
int i;
for(i=0;i<sizeof(word_idx);i++){
if (i == 0) word_idx[0] = 0;
else {
word_idx[i] = onset_data[i-1] + word_idx[i - 1];
}
}
wordcount = sizeof(word_idx);
}
}
and i can then access the pointers via lpc_common.lpc_data and the file loading via lpc_comon.dataInit()
?
probably not, but maybe something in that direction?
thanks for your time and support, really appreciated.
funny how in the preview the code tags work, but in the posted example they don't really...
ok not exactly.
more like:
lpc_common a;
a.lpc_data
etc...
and file loading
lpc_common:dataInit()
and, would the function definition not have to be in a .c file?
ok i had a go at this. i tried to make two really simple objects, one with a param dial, one with an outlet. i then tried to send the value from the param of one object to the outlet of the other object by passing the value to a variable defined in a class in a header file.
but, i get always this error:
/Users/lokki/Documents/axoloti/build/xpatch.o: In function `PatchProcess(long*, long*)':
xpatch.cpp:(.text+0x110): undefined reference to `Fly::datafly'
collect2: error: ld returned 1 exit status
the contents of my common.h file are only:
class Fly{
public:
static uint32_t datafly;
};
i'll attach the two objects. if you @thetechnobear or someone else can point me in the right direction with this simple example, that would be great. i looked at: https://en.wikipedia.org/wiki/C%2B%2B_classes and thought i did it right...
thanks.
classreceive.axo (508 Bytes)
classsend.axo (524 Bytes)
your just declaring the static, you need to instantiate it as well
you header file should look like
#pragma once
class Fly{
public:
static uint32_t datafly;
};
uint32_t Fly::datafly=0;
note: normally this would not be done in the header file, but in the compilable module (C file) ... but thats not used in axo.
however, what I do, is still break up my C code into headers and C files, and then just include the C file in the header file.
this means that when axoloti supports compilable modules, you can easily move your code into proper modules, as its already split correctly.
for 'trivial examples' though not necessary as you'd probably never want to go to the effort of having a separate module.
just as a follow up, i did it with all the data loading functions and stuff as well, all in a class now, and if you load multiple instances, the data will only be loaded the first time, but reused by the other modul-instances. this is nice, small memory footprint. thanks heaps @thetechnobear, i really really dig your way of helping me out here, i learn a lot and i am having fun!
Hi All!
Coming late to this thread, but I'm just wondering if the above offers a solution to a problem I had when working on my LPC objects.
One of my objects requires a number of pre-defined multi-dimensional arrays. I'd like put them directly into SDRAM somehow, and ideally, also make them available to other objects. I don't want to load the data from a binary file on the SD card.
Is this possible using any of the methods outlined above? Sorry if this has been covered already. It's been a while since I worked on any Axoloti coding, so it's taking me a while to get back into the swing of things.
a|x
Thanks @lokki for answering in a pvt. I can roll the data into a binary file in the object directory, and load that automatically into SDRAM at patch init.
a|x
Yeah the point of this is the user doesn’t know the data is held in a file, it’s automatic to them. (Like lmnts)
The only thing to becareful of is they have to use an sdcard, which theoretically is not a requirement , but in practice most users have.
Again, just to repeat (as important) this is designed for initializing /allocating data at startup , if you keep allocating/freeing you are likely to have issues.
Also remember a patch can only take a certain amount of time, otherwise initialisation will timeout.
(Can’t remember off hand what that timeout is)
Also remember to check return value from the Malloc , the more object developers use this, the more likely users are to run out of sdram during initialisation... and if you don’t log the failure, they’ll just start complaining of boards ‘crashing’
Load Struct from binary file into SDRAM (C)
Hi @thetechnobear Are there any relatively simple examples of the use of C header files and C++ objects in external files in the factory library?
I've been avoiding C++, because I've always been a bit scared of OOP, but it's probably about time I gave it a try.
a|x
the push object... or the really simple one i sent you. (to answer a question i was not asked)
I can't seem to work out how to hard-code the path for the binary file to be loaded from (same directory as the .axo itself), the destination path on the SD Card, and the reference to the file when loading it.
I've tried adding this block to the XML definition of the object:
<file-depends>
<file-depend localFilename="lpc_tables.raw" targetPath="/shared/lpc/lpc_tables.raw"/>
</file-depends>
Then I tried this line, to attempt to open the file:
file_error = f_open(&bin_file, "/shared/lpc/lpc_tables.raw", FA_READ | FA_OPEN_EXISTING);
a|x
Solved that one (hadn't actually put the raw file at the specified location- d'oh!).
Is it OK for my object to save data into /shared, or is this reserved for factory objects like lmnts?
If not, how do I specify the file should be copied to a directory named after the patch?
a|x
the question is, what happens when the directory is not on the sdcard? does it autocreate? or can we except every user to have shared on their sdcard? (did the lpc folder in shared get created automatically or did you have to create it first?) might consider to move my data to shared as well in that case. (at least the coefficients, not sure about the word file and the onsets)
It automatically creates it. I don't know what happens if it's already there, but I presume it just adds the sub-directory and data file to the existing top-level directory.
a|x