progmem pgm_read_word usage within loop

Software: IDE 1.6.5
Hardware: Uno

I have a requirement to store two dimensional arrays of numbers in progmem. I started by following the Array of strings example at https://www.arduino.cc/en/Reference/PROGMEM and after attempting to change this for integer use I have ran into a problem that has me stumped. I believe I have narrowed the issue down to the way I’m using pgm_read_word, I am unable to use the first square brackets with the For-loop variable (thisTune). Below is the code that loops twice and displays garbage data, I have commented out a line that only loops once – this shows the correct data but obviously only for a single array. Could someone kindly advise me of my error?

////one 31
const int one_melody[] PROGMEM = { 1047,932,1047,784,622,784,523,1047,932,1047,784,622,784,523,1047,1175,1245,1047,1245,1047,1245,1175,932,1175,932,1175,1047,932,784,932,1047 };
const int one_noteDurations[] PROGMEM = { 187,187,187,187,187,187,375,187,187,187,187,187,187,375,187,187,187,93,187,93,187,187,93,187,93,187,187,187,187,187,375 };

////two 44
const int two_melody[] PROGMEM = { 1480,1568,1568,1568,740,784,784,784,370,392,392,392,0,1568,1568,1568,1568,0,1319,0,1568,1319,0,1568,1760,1319,1319,1319,0,1175,1047,0,1109,0,784,0,1109,784,0,1109,0,1175,1175,1175 };
const int two_noteDurations[] PROGMEM =  { 150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 };

//tune tables
//melody
const int * const table_melody[] PROGMEM = {one_melody, two_melody};
//duration
const int * const table_duration[] PROGMEM = {one_noteDurations, two_noteDurations};
//length
const int table_length[] PROGMEM = {31, 44};


void setup() {
  Serial.begin(9600);
}

void loop() {
  int arraySize = NULL;
  for (int thisTune = 0; thisTune < 2 ; thisTune++) {
  //for (int thisTune = 1; thisTune < 2 ; thisTune++) {  
    Serial.print("length:");
    arraySize = pgm_read_word(&table_length[thisTune]);
    Serial.println(arraySize);
      
    for (int thisNote = 0; thisNote < arraySize; thisNote++) {
      int noteDuration = pgm_read_word(&table_duration[thisTune][thisNote]);
      int thisTone = pgm_read_word(&table_melody[thisTune][thisNote]);
      Serial.print("[thisTune]:");
      Serial.print(thisTune);
      Serial.print("[thisNote]:");
      Serial.print(thisNote);      
      Serial.print("[thisTone]:");
      Serial.print(thisTone);
      Serial.print("[duration]:");
      Serial.println(noteDuration);
    }
  }
  while(1); // Stop
}

const int * const table_melody PROGMEM = {one_melody, two_melody};

How much memory are you saving putting two pointers in PROGMEM?

How difficult is it to read the pointer from the appropriate table_melody entry, and then use that in the for loop?

PaulS:
How much memory are you saving putting two pointers in PROGMEM?

Thank you for replying. I had hopes to expand this to the point where it would be beneficial.

PaulS:
How difficult is it to read the pointer from the appropriate table_melody entry, and then use that in the for loop?

How would one do that?

These lines seem sure to cause trouble:

      int noteDuration = pgm_read_word(&table_duration[thisTune][thisNote]);
      int thisTone = pgm_read_word(&table_melody[thisTune][thisNote]);

table_melody and table_duration are one-dimensional arrays. The notation in thes two lines is indistinguishable from what we’d use to index into a two-dimensional array. I don’t see how the compiler could get this right.

If it did guess correctly, and thought that you wantedpgm_read_word(&(table_duration[thisTune])[thisNote])the results would be wrong, because table_duration is a PROGMEM array, and its values would be accessed as if it were in RAM. I think that the compiler would take the value of the array’s base pointer, and dutifully use it to index in to RAM, and fetch the wrong thing.

This, though, seems to work:

////one 31
const int one_melody[] PROGMEM = { 1047,932,1047,784,622,784,523,1047,932,1047,784,622,784,523,1047,1175,1245,1047,1245,1047,1245,1175,932,1175,932,1175,1047,932,784,932,1047 };
const int one_noteDurations[] PROGMEM = { 187,187,187,187,187,187,375,187,187,187,187,187,187,375,187,187,187,93,187,93,187,187,93,187,93,187,187,187,187,187,375 };

////two 44
const int two_melody[] PROGMEM = { 1480,1568,1568,1568,740,784,784,784,370,392,392,392,0,1568,1568,1568,1568,0,1319,0,1568,1319,0,1568,1760,1319,1319,1319,0,1175,1047,0,1109,0,784,0,1109,784,0,1109,0,1175,1175,1175 };
const int two_noteDurations[] PROGMEM =  { 150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150 };

//tune tables
//melody
const int * const table_melody[] PROGMEM = {one_melody, two_melody};
//duration
const int * const table_duration[] PROGMEM = {one_noteDurations, two_noteDurations};
//length
const int table_length[] PROGMEM = {31, 44};


void setup() {
  Serial.begin(9600);
}

void loop() {
  int arraySize = NULL;
  for (int thisTune = 0; thisTune < 2 ; thisTune++) {
  //for (int thisTune = 1; thisTune < 2 ; thisTune++) {  
    Serial.print("length:");
    arraySize = pgm_read_word(&table_length[thisTune]);
    Serial.println(arraySize);

    int * tbl_mel_ptr = (int*)pgm_read_word(table_melody + thisTune);
    int * tbl_dur_ptr = (int*)pgm_read_word(table_duration + thisTune);
    for (int thisNote = 0; thisNote < arraySize; thisNote++) {
      int noteDuration = pgm_read_word(tbl_dur_ptr + thisNote);
      int thisTone = pgm_read_word(tbl_mel_ptr + thisNote);
      Serial.print("[thisTune]:");
      Serial.print(thisTune);
      Serial.print("[thisNote]:");
      Serial.print(thisNote);      
      Serial.print("[thisTone]:");
      Serial.print(thisTone);
      Serial.print("[duration]:");
      Serial.println(noteDuration);
    }
  }
  while(1); // Stop
}

Edit: punctuation; fix errant word

tmd3,

Thank you. Yes that works very well, exactly the type of solution I was looking for.