Problem using F() macro

I have an Arduino Uno and the Adafruit Music Maker with 1.6.8 software. My program has many calls to musicPlayer.playFullFile("filename.wav") and I need to put those file names in Flash.

:) musicPlayer.playFullFile("filename.wav") works fine.

:( musicPlayer.playFullFile((const char *) F("filename.wav")) compiles, but there is no sound.

The Adafruit VS1053 library on Github has musicPlayer.playFullFile() and startPlayingFile() as follows:

boolean Adafruit_VS1053_FilePlayer::playFullFile(const char *trackname) {
  if (! startPlayingFile(trackname)) return false;

  while (playingMusic) {
    // twiddle thumbs
    feedBuffer();
  }
  // music file finished!
  return true;
}

boolean Adafruit_VS1053_FilePlayer::startPlayingFile(const char *trackname) {
  // reset playback
  sciWrite(VS1053_REG_MODE, VS1053_MODE_SM_LINE1 | VS1053_MODE_SM_SDINEW);
  // resync
  sciWrite(VS1053_REG_WRAMADDR, 0x1e29);
  sciWrite(VS1053_REG_WRAM, 0);

  currentTrack = SD.open(trackname);
  if (!currentTrack) {
    return false;
  }

  // As explained in datasheet, set twice 0 in REG_DECODETIME to set time back to 0
  sciWrite(VS1053_REG_DECODETIME, 0x00);
  sciWrite(VS1053_REG_DECODETIME, 0x00);

  playingMusic = true;

  // wait till its ready for data
  while (! readyForData() );

  // fill it up!
  while (playingMusic && readyForData())
    feedBuffer();

  // Serial.println("Ready");

  return true;
}

I've also chased this issue into the Adafruit SD Library on GitHub to no avail.

Any help is most appreciated.

musicPlayer.playFullFile((const char *) F("filename.wav")) compiles, but there is no sound.

What do you think the F() macro does? It produces a different kind of object. What sense does it make to cast that to a const char * pointer?

Suppose you have a class called Dog and one called Building. If you create an instance of the Dog class, called spot, and cast that instance to a Building *, can you ride spot's elevator to the 12th floor?

Do not cast things that you do not understand!

You could use something like this

  char fnBuffer[13];

  strcpy_P(fnBuffer, PSTR("filename.wav"));

  musicPlayer.playFullFile(fnBuffer);

Thanks Whandall, I had your method previously, but tried this alternative way.

PaulS, understanding is a temporal thing and I guess you don't subscribe to "there are no stupid questions"... only stupid answers. Thanks also.

You could use something like this

How does that reduce SRAM? You still need to have the string in flash AND in SRAM. That's what the strcpy_P() does - it copies the data from flash to SRAM.

I guess you don't subscribe to "there are no stupid questions"

Sometimes I do. Not in this case. You had to explicitly add the cast to make the code compile. That should have been a big clue. The compiler told you then what the type produced by the F() macro was. It should have been obvious that casting that type to a const char * was not going to perform a legitimate cast.

Now, if the question had been "How can I make this work?", my answer might well have been quite different.

Many functions are overloaded and support const char* and const __FlashStringHelper* so one could think both were interchangeable.

Too bad, but it does not work.

PaulS: How does that reduce SRAM? You still need to have the string in flash AND in SRAM.

Because you can have one SRAM buffer and many song names in PROGMEM.

MorganS: Because you can have one SRAM buffer and many song names in PROGMEM.

Doh. I hate when I don't think about something long enough before engaging the fingers.

To further complicate things, there are compilers where you can cast a ROM string to a RAM string and it works just fine (for reading, obviously not for writing). I'm using one for an ARM Cortex-M3 that works that way, although the ARM address space is part of why it's easy for the compiler to do that.

There are ARM based Arduinos, right? Do they allow ROM strings and RAM strings to be treated interchangeably when reading?

BigBobby: To further complicate things, there are compilers where you can cast a ROM string to a RAM string and it works just fine (for reading, obviously not for writing). I'm using one for an ARM Cortex-M3 that works that way, although the ARM address space is part of why it's easy for the compiler to do that.

There are ARM based Arduinos, right? Do they allow ROM strings and RAM strings to be treated interchangeably when reading?

This is really an issue of the AVR and its use of multiple address spaces. YES the AVR sucks when it comes to using const data and C since C does not have a concept of multiple address spaces. As such, it is not possible to use pointers the way C expects them to work when using const data on an AVR.

While I believe there are some other compilers that have proprietary extensions in the compiler to help hide multiple address spaces, this is not an option in avr-gcc.

The avr-gcc compiler works around this by copying const data to RAM so pointers to the data can work. The issue is that there is very limited RAM. The progmem stuff is an AVR hack to try to work around this with the avr-gcc compiler. It tells the linker not copy the const data to ram and to use the raw flash address as the pointer to it. Unfortunately because of the multiple address spaces, the pointer is pointer into flash and since flash can't be accessed directly through pointers, anything that expects pointers to work correctly will fail.

The progmem stuff is a s/w solution to what is really a h/w problem. When using progmem (const data) pointers, you have to use access routines which really wonks up your code and is one of the things I really ditest on the AVR.

Other processors like the ARM and the PIC32/Mips don't have this issue and const "just works" as C expects it to work.

--- bill

bperrybap: Other processors like the ARM and the PIC32/Mips don't have this issue and const "just works" as C expects it to work.

So the compiler the Arduino IDE uses for Arduinos using ARM processors behave this way?

I don't have an ARM Arduino here, so building test code would require me to read assembly rather than just running it. Can anyone else verify that the ARM Arduinos are flexible when reading strings from ROM?

BigBobby: So the compiler the Arduino IDE uses for Arduinos using ARM processors behave this way?

I don't have an ARM Arduino here, so building test code would require me to read assembly rather than just running it. Can anyone else verify that the ARM Arduinos are flexible when reading strings from ROM?

Yes it should work. But you could try just compiling some code for an ARM based board and look the code & data usage and verify it for yourself. You don't need any h/w to do that.

--- bill