LenShustek / Playtune_poll

A background polyphonic music generator for Arduino, using fast polling
MIT License
16 stars 2 forks source link

Playtune_poll + SPI Display #2

Closed vsdyachkov closed 1 year ago

vsdyachkov commented 1 year ago

Hey Len

I want to use in my project Arduino Nano + ST7920 LCD12864 connected via SPI (for example HW SPI pin 10, 11, 13) + Playtune_poll (pin 2, 3, 4, 5, 6, 7)

It looks like Playtune_poll and SPI share the same timer, which is causing a conflict.

Is it possible for them to use different timers or stop playing music while sending a command to the display via SPI? Perhaps there are other ways to resolve the situation?

LenShustek commented 1 year ago

Here are a couple of ideas:

vsdyachkov commented 1 year ago

Thanks for the quick response!

Calling void tune_stop_timer(void) - stops the timer and playback, tune_start_timer() does not by itself continue playback, however calling tune_playscore() restarts playback. But I would like just a pause and a continuation. It seems that the original code does not provide for this.

I explored the possibility of using timers 0 and 2. Judging by the description, timer0 is used to operate the system time functions delay(), delayMicroseconds() and millis() and micros() - I don't want to break it. Reconfigured Playtune_poll to Timer2, but the connected display is not updated. I even tried to abandon timers altogether - anyway, something in the tune_playscore() function breaks the logic of the SPI bus...

I'll try to study the problem again, if nothing happens, I'll try on Micro or Mega 2560 Pro Mini

LenShustek commented 1 year ago

It's easy to add a new function to Playtune_poll.ino that will resume a score instead of starting it from the beginning. Something like this:

void tune_resumescore(void) {
  if (score_cursor) {
    if (!timer_running)
        tune_start_timer(0);
    tune_stepscore();  // start commands again
    tune_playing = true; } }

For safety, it's a good idea to zero score_cursor when it gets to the end of the score in tune_stepscore():

else if (opcode == CMD_STOP) { /* stop score */
         score_cursor = 0;
         tune_playing = false;
          ...

But I'm starting to question whether you have really diagnosed the problem correctly. As far as I know the SPI routines don't use any timers, which is supported by this discussion: https://electronics.stackexchange.com/questions/90778/does-arduino-spi-use-up-a-timer The problem might instead be a conflict with I/O pins. SPI on a Nano uses pins 11, 12, and 13 for sure, and usually 10 for CS. The last three pins that Playtune_poll uses are 10, 11 and 12. One thing to try, if you're willing to only have five channels, is to set MAX_CHANS in Playtune_poll.h to 5 instead of 8. Then it will only use pins 5, 6, 7, 8, and 9, and leave 10, 11, and 12 for SPI.

vsdyachkov commented 1 year ago

I had an idea that the timer interrupts the SPI transfer session and incorrect or out of sync information comes to the display... But the problem was simpler, I configured several ports on one channel. Once the bug was fixed, everything started working correctly.

tune_resumescore - also works, useful feature


I have two questions:

1) miditones can create a .bin file -b generate a binary file output instead of C source code How to play it then?

2) Is it possible to reduce SRAM consumption? If all settings (DO_VOLUME, DO_PERCUSSION and ASSUME_VOLUME) are enabled - this takes 1386 bytes (67% of Nano ), if these settings are disabled - 567 bytes (27% Nano)

LenShustek commented 1 year ago

I'm glad you solved your problem. I just posted an update that has a slightly better version of tune_resumescore, and also has suggestions for alternate Nano pins that can be used.

Compiling the C source code output by miditones generates the same binary file that -b generates. It still needs to be played with Playtune, or some program like it. I provided -b mostly for people in non-Arduino environments who want to directly import the binary file without having to compile it.

You can reduce the SRAM usage easily only by decreasing MAX_CHANS, because there are several arrays of that size. And by disabling DO_VOLUME and DO_PERCUSSION, as you discovered.

The only other opportunity I can see for a significant decrease is this: If "polltime" is a constant known at compile-time, then you could create constant versions of the arrays decrement_table and ticks_per_period, and put them in ROM instead of RAM. That would save (MAX_NOTE+1) * (4+2) = 744 bytes of RAM -- which is a lot for processor with only 2K! It's definitely doable, but it's a non-trivial change, especially if you want to still be able to generate the version where polltime can be changed. Don't forget that the code that accesses those arrays when they are in ROM would have to use pgm_read_dword() and pgm_read_word().

LenShustek commented 1 year ago

It's definitely doable, but it's a non-trivial change, especially if you want to still be able to generate the version where polltime can be changed.

Well, I needed a break from another project, and this was intriguing, so I gave it a shot. It wasn't as hard as I thought. It reduces the RAM used for global variables from 1387 bytes to 128 bytes, and at the same time reduces the Flash program memory from 19836 to 18640, because the code to dynamically generate the tables isn't needed.

It needs more testing, so I'm not going to push it to the repository yet. But if you want to try it and help me with testing, I'll attach it here. This version has DO_CONSTANT_POLLTIME already set to 1, so it should be ready to go. (I had to add a .txt extension to be able to attach it to this message; just remove that.) Playtune_poll.ino.txt

vsdyachkov commented 1 year ago

Great! Now with all settings it takes about 75 bytes of SRAM and 3736 Flash with an empty test_score

1) Line 894 is missing ";"

2) As far as I understand ASSUME_VOLUME - sets a certain global volume level?

3) Question about DO_VOLUME

I took midi track and converted like this: miditones.exe -pt -v -d -i -t8 and If I set DO_VOLUME 1 - the sound is more "robotic", abrupt and muffled If I set DO_VOLUME to 0 - the overall sound is more interesting, but there is an unpleasant high-frequency squeak (I think it's drums)

Example link https://dropmefiles.com/vOyEv

Is that how it should be?

LenShustek commented 1 year ago

ASSUME_VOLUME is only there in case the file has no Pt header (created by Miditones with the -d option), so Playtune knows whether the bytestream has volume information at all. With no volume information, it always generates a symmetric square wave.

How it sounds depends on Playtune, but also on what speaker you're using, and whether there is any frequency-band equalization going on. Since we're generating square waves, which have a lot of high frequency overtones, a little low-pass filtering is helpful.

I just put several files up at https://shustek.com/playtune/.

This has been a fun diversion from what I'm really supposed to be doing...

vsdyachkov commented 1 year ago

vol1_perc1_voladj.mp3 - I think this is the best sound on a "clean" ATmega without external hardware piping

LenShustek commented 1 year ago

You were curious about my connection scheme. On the Nano, it's just eight 470-ohm resistors connected to pins D5 through D12, the other ends of which are connected together to the center pin of a phono jack. Then the Nano ground is connected to the shield. Those recordings I made were with an old Goal Zero Rock Out powered speaker plugged into the jack and sitting in front of my computer's microphone. I didn't take my own advice about using a low-pass filter!

vsdyachkov commented 1 year ago

At first I connected all the audio pins together and connected it to a 4 ohm 3W speaker through a potentiometer, it didn’t work out very well. Then I decided to connect each pin through its own resistor to the speaker. It turned out that I got a fairly loud and even sound if I used 100 ohm resistors.

Using a speaker with an amplifier or some additional circuits seems to me already redundant for such a simple chip. It was interesting to get such a good result on limited hardware.

After studying examples like this https://youtu.be/-c_ovdKLtGc, I wanted to write my own midi parser, but it turned out it was already written and it took years of experience :)

LenShustek commented 1 year ago

It doesn't necessarily confer wisdom, but I do have some experience. Last year I passed the 60th anniversary of the first computer program I ever wrote, which was for a vacuum-tube computer. I am pleased that programming is as much fun now as it was then.

vsdyachkov commented 1 year ago

Congratulations on such a significant date! Lots of good and interesting experience