2

I'm implementing a voice synthesizer chip. To build a phrase, I create a list of phonemes like this:

static const uint8_t PROGMEM heybuddy[] = {
  pPA5, pHH1, pEY, pPA5,
  pBB2, pAX, pDD2, pIY, pPA5,pPA5,pPA5,
};

And to play them I do this:

 for (size_t i=0; i<sizeof(heybuddy); i++){
    say(pgm_read_byte(heybuddy+i));
 }

So what I'd like to do is create a function where I can just pass the array to a function that can take care of the loop and do some other maintenance stuff.

//what I've tried: 
void sayPhrase(const uint8_t *phrase){
  int open = 0;
  for (size_t i=0; i<sizeof(*phrase); i++){

      if(pgm_read_byte(*phrase) != 4 && open == 0){
          Serial.println("mouth open");
          open = 1;
      } else if(pgm_read_byte(*phrase) == 4 && open == 1){
         Serial.println("mouth closed");
         open = 0;
      }
      say(pgm_read_byte(*phrase+i));
    } 
}

But when it comes to passing a static const I'm lost I've tried passing it as a reference with * but nothing I do seems to work - I just don't know the proper way to do this in C.

What I have compiles, but it seems to only get one phoneme and repeat it over and over. The rest are lost.

Pharap
  • 149
  • 8

4 Answers4

1

The problem is, when you call sizeof(*phrase), what is returned is 1, the size of the uint8_t array element, the result of your pointer de-reference. On the other hand, if you call sizeof(phrase) inside the function, you get the size of the pointer itself and not the size of the array, as you might expect. When you pass the array to the function via a pointer, the function has no idea if you passed the start of an array to it or merely a pointer to some uint8_t variable, so it can't know what the size is; context has been lost.

Instead, you should call sizeof(phrase) in the same scope where the array was declared and initialized and pass the result to the function, which can then use this value in the for loop.

static const uint8_t PROGMEM heybuddy[] = {
  pPA5, pHH1, pEY, pPA5,
  pBB2, pAX, pDD2, pIY, pPA5,pPA5,pPA5,
};

size_t SIZE = sizeof(heybuddy);  // this is ok since each element = 1 byte

sayPhrase(phrase, SIZE);

The function definition should be:

void sayPhrase(const uint8_t *phrase, size_t size){
  int open = 0;
  for (size_t i=0; i < size; i++){

      if(pgm_read_byte(phrase) != 4 && open == 0){
          Serial.println("mouth open");
          open = 1;
      } else if(pgm_read_byte(phrase) == 4 && open == 1){
         Serial.println("mouth closed");
         open = 0;
      }
      say(pgm_read_byte(phrase+i));
    } 
}
SoreDakeNoKoto
  • 2,422
  • 2
  • 14
  • 23
1

I have some extensive examples of accessing PROGMEM here.

I've adapted one of the examples to show how you might do something similar to what you want:

const int NUMBER_OF_ELEMENTS = 10;

const char Message0000 [] PROGMEM = "Twas bryllyg, and ye slythy toves"; 
const char Message0001 [] PROGMEM = "Did gyre and gymble"; 
const char Message0002 [] PROGMEM = "in ye wabe:"; 
const char Message0003 [] PROGMEM = "All mimsy were ye borogoves; And ye mome raths outgrabe."; 
const char Message0004 [] PROGMEM = "\"Beware the Jabberwock, my son! \n The jaws that bite, the claws that catch!"; 
const char Message0005 [] PROGMEM = "Beware the Jubjub bird, and shun\n The frumious Bandersnatch!\""; 
const char Message0006 [] PROGMEM = "He took his "; 
const char Message0007 [] PROGMEM = "vorpal sword in hand:"; 
const char Message0008 [] PROGMEM = "Long time the manxome foe he sought - "; 
const char Message0009 [] PROGMEM = "So rested he by the Tumtum tree, \n And stood awhile in thought."; 

const char * const messages[NUMBER_OF_ELEMENTS] PROGMEM = 
   { 
   Message0000, 
   Message0001, 
   Message0002, 
   Message0003, 
   Message0004, 
   Message0005, 
   Message0006, 
   Message0007, 
   Message0008, 
   Message0009, 
   };

void sayPhrase (const char * const * message)
  {
  const char * ptr = reinterpret_cast<const char *>(pgm_read_ptr (message));  // pointer to message
  Serial.println(reinterpret_cast<const __FlashStringHelper *>(ptr));    // and print it
  } // end of sayPhrase

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  for (int i = 0; i < NUMBER_OF_ELEMENTS; i++)
    sayPhrase (&messages [i]);

  }  // end of setup

void loop () { } 
Pharap
  • 149
  • 8
Nick Gammon
  • 38,901
  • 13
  • 69
  • 125
1

If you're not averse to a bit of C++:

void sayPhrase(const uint8_t * phrase, size_t size)
{
    bool open = false;
    for (size_t i = 0; i < size; ++i)
    {
        if(pgm_read_byte(phrase) != 4 && !open)
        {
            Serial.println("mouth open");
            open = true;
        }
        else if(pgm_read_byte(phrase) == 4 && open)
        {
            Serial.println("mouth closed");
            open = false;
        }
        say(pgm_read_byte(&phrase[i]));
    } 
}

template< size_t size >
void sayPhrase(const uint8_t (&phrase)[size])
{
    sayPhrase(phrase, size);
}

Which can then be called directly on the array:

sayPhrase(heybuddy);

And the template will infer the array size and call the non-template function.

Pharap
  • 149
  • 8
0

As others have pointed out, your problem is sizeof, which is calculated at compile time, not when the code is run.

You need to pass the size of the array to the function. This can be done in many ways, depending on what else you are doing.

In c the most common method is to use a null-terminated string, but I suspect this may not work.

You can store the size of each phrase in a separate array, and pass as a separate parameter or Pascal style as a count byte in the first element of the phrase.

Milliways
  • 1,655
  • 2
  • 18
  • 29