I'm writing some program for a stm32l0 microcontroller. I define some arrays as global variables:
volatile uint32_t synth_counter [N_CHANNELS];
volatile uint32_t synth_pitch [N_CHANNELS];
volatile uint32_t synth_envelope [N_CHANNELS];
volatile int_fast8_t synth_key [N_CHANNELS];
volatile uint32_t synth_active [N_CHANNELS];
volatile uint32_t synth_state [N_CHANNELS];
volatile uint32_t synth_envl_add [N_CHANNELS];
volatile uint32_t synth_envl_sub [N_CHANNELS];
volatile uint32_t synth_envl_goal[N_CHANNELS];
volatile int_fast8_t synth_prev [N_CHANNELS];
volatile int_fast8_t synth_next [N_CHANNELS];
...
uint8_t CH_ACTIVE [N_NOTES];
int_fast8_t CH_INDEX [N_NOTES];
volatile uint32_t PITCH [N_NOTES];
volatile int8_t SAMPLE [N_SAMPLE] = DEFAULT_SAMPLE;
As you can see, the size of the array is a constant defined in a header file:
#define N_NOTES 97 // = (9 - 1) * 12 + 1
#define N_CHANNELS 12 //EVEN NUMBER
#define N_SAMPLE 256
I want those arrays have an initial value which can be overwritten later. For one of them I could do:
volatile int8_t SAMPLE [N_SAMPLE] = DEFAULT_SAMPLE;
because I know that N_SAMPLE will always be 256.
So I could define something like this:
#define DEFAULT_SAMPLE \
{ \
0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, \
0x7D, 0x7C, 0x7B, 0x7A, 0x7A, 0x79, 0x78, 0x76, \
0x75, 0x74, 0x73, 0x71, 0x70, 0x6E, 0x6D, 0x6B, \
0x6A, 0x68, 0x66, 0x64, 0x62, 0x60, 0x5E, 0x5C, \
0x5A, 0x57, 0x55, 0x53, 0x50, 0x4E, 0x4B, 0x49, \
0x46, 0x44, 0x41, 0x3E, 0x3C, 0x39, 0x36, 0x33, \
0x30, 0x2D, 0x2A, 0x27, 0x25, 0x22, 0x1E, 0x1B, \
0x18, 0x15, 0x12, 0x0F, 0x0C, 0x09, 0x06, 0x03, \
0x00, 0xFC, 0xF9, 0xF6, 0xF3, 0xF0, 0xED, 0xEA, \
0xE7, 0xE4, 0xE1, 0xDD, 0xDA, 0xD8, 0xD5, 0xD2, \
0xCF, 0xCC, 0xC9, 0xC6, 0xC3, 0xC1, 0xBE, 0xBB, \
0xB9, 0xB6, 0xB4, 0xB1, 0xAF, 0xAC, 0xAA, 0xA8, \
0xA5, 0xA3, 0xA1, 0x9F, 0x9D, 0x9B, 0x99, 0x97, \
0x95, 0x94, 0x92, 0x91, 0x8F, 0x8E, 0x8C, 0x8B, \
0x8A, 0x89, 0x87, 0x86, 0x85, 0x85, 0x84, 0x83, \
0x82, 0x82, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, \
0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, \
0x82, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x89, \
0x8A, 0x8B, 0x8C, 0x8E, 0x8F, 0x91, 0x92, 0x94, \
0x95, 0x97, 0x99, 0x9B, 0x9D, 0x9F, 0xA1, 0xA3, \
0xA5, 0xA8, 0xAA, 0xAC, 0xAF, 0xB1, 0xB4, 0xB6, \
0xB9, 0xBB, 0xBE, 0xC1, 0xC3, 0xC6, 0xC9, 0xCC, \
0xCF, 0xD2, 0xD5, 0xD8, 0xDA, 0xDD, 0xE1, 0xE4, \
0xE7, 0xEA, 0xED, 0xF0, 0xF3, 0xF6, 0xF9, 0xFC, \
0xFF, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15, \
0x18, 0x1B, 0x1E, 0x22, 0x25, 0x27, 0x2A, 0x2D, \
0x30, 0x33, 0x36, 0x39, 0x3C, 0x3E, 0x41, 0x44, \
0x46, 0x49, 0x4B, 0x4E, 0x50, 0x53, 0x55, 0x57, \
0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x68, \
0x6A, 0x6B, 0x6D, 0x6E, 0x70, 0x71, 0x73, 0x74, \
0x75, 0x76, 0x78, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, \
0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F \
}
I can not know this for other arrays.
I would want to create for example a #define DEFAULT_SYNTH_NEXT which will become:
{ 1, 2, 3, 4, 5, 6, 7, -1} when N_CHANNELS equals 8,
{ 1, 2, 3, 4, 5, -1} when N_CHANNELS equal 6,
and so on.
Create a #define DEFAULT_CH_ACTIVE which is an array filled with N_NOTES values of -1.
and so on and so on.
Is it possible, using the C preprocessor to achieve a #define which depends on previously defined number in a way described above?
Somehow (recursively?) (iterably?) produce the expected result?
I found an article http://jhnet.co.uk/articles/cpp_magic but this does not look very readable and understandable and I didn't manage to figure it out yet. I'm hoping for something more clear to read.
I see 2 alternatives to what I want, but they are not ideal:
Alternative 1 is statically defining like:
#define DEFAULT_SYNTH_PREV {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
#define DEFAULT_SYNTH_NEXT {1, 2, 3, 4 ,5, 6, 7, 8, 9, 10, 11, -1}
and so on, but then I have to manually edit it every time I update one of those values.
Alternative 2 is initialising the arrays in the main() function, in for() loops before entering the main loop.
This is what I'm currently doing,
set_tuning(DEFAULT_TUNING);
for(i=0; i<N_CHANNELS; ++i)
{
synth_counter[i] = 0;
synth_pitch[i] = 0;
synth_envelope[i] = 0;
synth_envl_add[i] = 0;
synth_envl_sub[i] = 0;
synth_envl_goal[i] = 0;
synth_active[i] = 0;
synth_state[i] = ENVL_0;
if (i==0)
synth_prev[i] = -1;
else
synth_prev[i] = i-1;
if (i==(N_CHANNELS - 1))
synth_next[i] = -1;
else
synth_next[i] = i+1;
synth_key[i] = -1;
}
for (i=0; i<N_NOTES; ++i)
{
CH_ACTIVE[i] = 0;
CH_INDEX[i] = -1;
}
This does prefill the array with required values but it is less optimal code than the code which prefills predefined variables by copying from FLASH to RAM in the reset handler in the startup code which is included to the project by default:
Reset_Handler:
ldr r0, =_estack
mov sp, r0 /* set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2]
adds r2, r2, #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
This is why I'm still hoping to be able to "dynamically" #define the array initialisers
#define N_NOTES 97 // = (9 - 1) * 12 + 1- do not do this. If a number makes sense as a result of a math operation, put the math operation into number definition. I have seen some errors with unpleasant consequences where there was a math error like that.