ArduboyPlaytune FX

ArduboyRecording(6)

I have hacked a copy of the ArduboyPlaytunes to work with the FX chip. You can now stream music and graphics from the FX chip simultaneously. I have included a sample program that shows how and includes streaming from the FX and from PROGMEM as a comparison.

It can be found here.

The approach is slightly different to the other playScore() functions as it uses a circular buffer to hold the data to play. The buffer is replenished as part of the standard loop() processing, as shown below. This is primarily due to the fact that the FX and ArduboyPlaytunes would fight over interrupts.

The buffer size is configurable but my testing has shown that even the smallest buffer of 8 unit8_ts is enough. I guess if you have some really, really fast music and a really, really slow frame rate you could up it to a lavish length of 16!

#include <Arduboy2.h>
#include "src/ArduboyPlaytuneFX.h"
#include <ArduboyFX.h>
#include "fxdata/fxdata.h"

Arduboy2 arduboy;
uint16_t buffer[8]; 
ArduboyPlaytuneFX tunes(arduboy.audio.enabled, buffer);

void loop() {

  if (!arduboy.nextFrame()) return; 
  arduboy.pollButtons();
  tunes.fillBufferFromFX();

  ...

  tunes.playScoreFromFX(SmellsLikeTeenSpirit, SmellsLikeTeenSpirit_Len);
2 Likes

3 Likes

Isn’t tunesMemFX_Len actually the end of the stream rather than the length?
It also seems that tunesMemFX_Index is more of an absolute pointer than a relative index.


It would be nice to know how the address for the data is actually generated. I can’t help but feel with a bit of template voodoo SmellsLikeTeenSpirit could have a type like FXArray<uint8_t, 1234> that could turn tunes.playScoreFromFX(SmellsLikeTeenSpirit, SmellsLikeTeenSpirit_Len) into tunes.playScoreFromFX(SmellsLikeTeenSpirit) through more inference. (Though I appreciate that would probably require @Mr.Blinky changing how fxdata.txt is handled.)

It’s the length of the array rather the end address of the stream. It is used to detect when the last byte is read and if the theme is to be repeated (denoted by an 0xE0) then the current memory location being read is reset to the beginning of the memory range.

Its the location of the memory being read. Are you trying to make a point about my variable names? :slight_smile:

The addresses of the data generated by the fxdata-build.py script are just that - addresses. There are no data types implied and you could start reading bytes or ints willy nilly. There is nothing stopping you totally misinterpreting whats in the FX data.

There is no reason whey I couldn’t drop the length parameter and stream the data until I find an 0xE0 (repeat) or 0xF0 (end). To do so would introduce more logic as the ArduboyPlaytunes does not have a regular structure and you would need to track where you are in the stream. Values of 0xE0 or 0xF0 can turn up in the stream as a wait length, so you must know that you are about to read a command as opposed to a wait period. And this needs to be done when filling the buffer not when consuming it.

That doesn’t make sense then.

Why compare the index to the length if the length is the length (not the end) but the index is the address of the data?

It would make sense if index were an actual index going from 0 to length and length were the actual length, or if the ‘index’ and ‘length’ were both addresses (the ‘current pointer’ and ‘end pointer’), but you seem to be saying neither is the case?

I’m just trying to make sense of what’s going on because you seem to be mixing an absolute addressing approach (akin to using bare pointers) with an offset-based approach (akin to array indexing).

For example, say tunesMemFX_Start were 0x100000, tunesMemFX_Index were 0x100000 and then tunesMemFX_Len (the length of the array) were 16. That would imply that tunesMemFX_Index == tunesMemFX_Len isn’t going to be true for a very long time.

If tunesMemFX_Len were the end - 0x10000F - then it would work as intended.

Likewise if tunesMemFX_Len were still the length but tunesMemFX_Index was an offset from tunesMemFX_Start it would also make sense.

But like I say, from what I’m looking at you seem to be mixing the two and arriving at something that logically shouldn’t work?

The only way it would make sense with the code as it currently is would be if tunesMemFX_Index were the address of the next byte to be read and if tunesMemFX_Len was actually the address of the end of the data rather than the length of the data.

I know that, the same is more or less true of pointers if you strip away the safety features, but there are absolute addresses (equivalent to pointers) and relative indexes (equivalent to offsets to be added to base addresses).

Fixed. It worked in its current state because the starting memory location happened to be 0. Of course, it would not work if the memory location was non-zero.

1 Like

For a moment there I thought I was going crazy.

That would definitely explain why you hadn’t noticed.


By the way, I had actually considered creating a pointer-like interface for FX memory quite a while back, but the caveats about how reading works better when using readEnd and the like means it would be sub-optimal (unlike a progmem or EEPROM interface, which would be spot-on).

1 Like