Shop OBEX P1 Docs P2 Docs Learn Events
Ideas for a MP3 Player for a Person with reduced Eyesight? — Parallax Forums

Ideas for a MP3 Player for a Person with reduced Eyesight?

Christof Eb.Christof Eb. Posts: 1,087
edited 2023-01-18 21:21 in Propeller 2

Hi,
these days I am pondering, if it would be a good idea to try to build an MP3 player for a Person with reduced Eyesight using P2. The person is not used to use computers or mobil phones.
The goals are:

  • A machine, that is very easy to use
  • Hear Stories. Audio books. So it must be possible to pause and restart at the same point. If the audio is split into multiple files, these must be played in the right order.
  • MP3 from SD Card.
  • Perhaps have spoken feedback as user interface.
  • Have a color display? I have got some ST7735?
  • Possibility to use batteries?
    (* Perhaps add a radio receiver ? DAB???)

What is clear, is that the DAC capability of P2 would be sufficient and would be an argument to use P2. I would like to do this in Forth, C or Basic. I want to use hardware and software building blocks. Perhaps spend the time to build some nice wooden enclosure.

What I have found so far:

A similar project with raspi:
https://github.com/NicolasBrondin/book-pie It seems to use java script, which I am not familiar with.

Mini DFPlayer https://picaxe.com/docs/spe033.pdf - has some limitations. Cannot work with directories. P2 should be able to do much itself.

MP3 offline decoding with P1:
https://forums.parallax.com/discussion/148036/mp3-decoder-works
The library is not included and this was done with P1 then.

Perhaps some ideas? Thank you very much!

Comments

  • pik33pik33 Posts: 2,347
    edited 2023-01-18 10:26

    I tried to recompile C-based mp3 decoders for a P2 using Flexprop. The result was way too slow to run in the real time.

    I still think a P2 can decode mp3 in the real time, it only needs a proper decoder written for it, in asm.

  • @pik33 said:
    I tried to recompile C-based mp3 decoders for a P2 using Flexprop. The result was way too slow to run in the real time.

    I still think a P2 can decode mp3 in the real time, it only needs a proper decoder written for it, in asm.

    Thank you for sharing this experience! Perhaps an offline decoder would still be useful to generate WAV files on the same SD card. Perhaps this would make the handling of data more easy.

  • MP3 was created when storage was small and expensive -- that's no longer the case. WAV decoding is easy; I just need a reliable uSD driver for Spin2 and I will be building a P2 audio player the way I did with the P1 (called the EFX-TEK AP-16+ and smaller version AP-8+).

    I did some experimenting with WAV decoding in the P2 and got an assist from Chip to ensure it works. ATM, this code must be passed the hub address of an embedded WAV file. I'll adapt this to the double buffering code I wrote for the P1. It's nice having real DACs in the P2 (the P1 had race condition problems that would inject noise into the audio).

    pub play_wav(p_wav, level) : result | systix, t1, mode, nsmpls, smpltix, ls, rs
    
    '' Play WAV file from RAM
    '' -- p_wav is pointer to embedded file
    ''    * must be canonical WAV with header
    '' -- level is volume control, 0..100 (%)
    
      systix := clkfreq                                             ' fix FlexProp incompatibility
    
      org
                            mov       t1, p_wav
                            add       t1, #20                       ' audio format
                            rdbyte    t1, t1
                            cmp       t1, #1                wcz     ' must be 1 (PCM)
            if_ne           jmp       #.done
    
                            mov       mode, #0                      ' wav configuration mode
    
                            mov       t1, p_wav
                            add       t1, #22                       ' channels
                            rdbyte    t1, t1
                            cmp       t1, #2                wcz     ' max is 2 channels
            if_a            jmp       #.done
    
                            bitz      mode, #1                      ' 0 = mono, 1 = stereo
    
                            mov       t1, p_wav
                            add       t1, #34                       ' bits per sample
                            rdbyte    t1, t1
                            shr       t1, #3                        ' convert to bytes/sample
                            cmp       t1, #2                wcz     ' max is 2 bytes/channel
            if_a            jmp       #.done
    
                            bitz      mode, #0                      ' 0 = 8-bit, 1 = 16-bit
    
                            mov       t1, p_wav
                            add       t1, #40
                            rdlong    nsmpls, t1                    ' read sub chunk 2 bytes
    
                            mov       t1, p_wav
                            add       t1, #32                       ' block align (bytes per sample)
                            rdbyte    t1, t1
                            qdiv      nsmpls, t1
                            getqx     nsmpls
    
                            mov       result, nsmpls                ' return samples in file
    
    '                       rdlong    smpltix, #$44                 ' read clkfreq
                            mov       smpltix, systix               ' fix FlexProp incompatibility
    
                            mov       t1, p_wav
                            add       t1, #24                       ' sample rate
                            rdlong    t1, t1
    
                            qdiv      smpltix, t1                   ' clkfreq/sample rate
                            getqx     smpltix
    
    
    .fix_level              fge       level, #0                     ' keep 0..100
                            fle       level, #100
                            mul       level, #$7FFF/100             ' set scale factor
    
    
    .start                  add       p_wav, #44                    ' point to audio
                            getct     t1                            ' start sample timer
    
    
    .loop                   jmprel    mode
                            jmp       #.mono8
                            jmp       #.mono16
                            jmp       #.stereo8
                            jmp       #.stereo16
    
    
    .mono8                  rdbyte    ls, p_wav                     ' read sample
                            add       p_wav, #1                     ' point to next
                            shl       ls, #8                        ' expand sample, make 1v p-p
                            bitnot    ls, #15
                            mov       rs, ls                        ' copy to right channel
                            jmp       #.set_volume
    
    
    .mono16                 rdword    ls, p_wav                     ' read sample
                            add       p_wav, #2                     ' point to next
                            mov       rs, ls                        ' copy to right
                            jmp       #.set_volume
    
    
    .stereo8                rdbyte    ls, p_wav                     ' read left sample
                            add       p_wav, #1                     ' point to right
                            rdbyte    rs, p_wav                     ' read right sample
                            add       p_wav, #1                     ' point to next left
                            shl       ls, #8                        ' expand sample, make 1v p-p
                            bitnot    ls, #15                       ' fix sign bit
                            shl       rs, #8                        ' expand sample, make 1v p-p
                            bitnot    rs, #15                       ' fix sign bit
                            jmp       #.set_volume
    
    
    .stereo16               rdword    ls, p_wav                     ' read left sample
                            add       p_wav, #2                     ' point to right
                            rdword    rs, p_wav                     ' read right sample
                            add       p_wav, #2                     ' point to next left
    
    
    .set_volume             muls      ls, level                     ' scale volume
                            shr       ls, #16                       ' adjust
                            bitnot    ls, #15                       ' center at 1v
    
                            muls      rs, level
                            shr       rs, #16
                            bitnot    rs, #15
    
    
    .set_dacs               wypin     ls, #L_OUT                    ' output sample
                            wypin     rs, #R_OUT
    
                            addct1    t1, smpltix                   ' update sample timer
                            waitct1                                 ' let it expire
    
                            djnz      nsmpls, #.loop                ' next sample
    
    
    .quiet                  wypin     ##$8000, #L_OUT               ' set to midpoint of DAC
                            wypin     ##$8000, #R_OUT
    
    .done                   ret
      end
    
  • MP3 playback is something I have been looking at. I would love to create a Falcon Player (FFP) workalike with the P2. Sending the E1.31 sequence data would be easy. The audio, not so much.

    RAM may be the bottleneck. It might require something like the P2-EC32MB to be able to pull it off. Read all of the data into the PSRAM and act on it from there.

  • @pik33 said:
    I tried to recompile C-based mp3 decoders for a P2 using Flexprop. The result was way too slow to run in the real time.

    I still think a P2 can decode mp3 in the real time, it only needs a proper decoder written for it, in asm.

    I think you picked the wrong decoder :). Attached is a zip file of the Adafruit_MP3 decoder, which on a P2 running at 250 MHz decodes at ~1824 kbps, which is enough for 48 kHz stereo (16 bits/sample). Mind you that is using a whole cog, and I don't have actual audio output hooked up yet (it's left as an exercise for the reader). You can optionally define WRITE_FILE in examples/standalone/main.c to get the output to the host in a file called "output.bin"; that slows the decode process down to below real-time, but allows verification of the output.

    To compile this you'll need FlexC 5.9.26. The example is in examples/standalone; there's a README_P2.txt at the top level describing how to build and run.

  • pik33pik33 Posts: 2,347
    edited 2023-01-18 18:16

    I think you picked the wrong decoder

    Yes, they are wrong, as they are full blown Linux things (= noone thought about speed optimizing, but rather a lot of things that can be skipped)

    I will try this adafruit thing. As I have a multiformat player already, this may add mp3 playing capability to it. Adafruit is focused on microcontrollers, so their code has to be efficient. The whole cog is not a problem there: SID and MOD both use (more than 1) cogs which I start and stop on demand.

  • Wuerfel_21Wuerfel_21 Posts: 4,371
    edited 2023-01-18 20:00

    @JonnyMac that code is actually busted. The valid DAC values only range from $0000 to §FF00, so at full volume, this will actually clip the top of the waveform off. Could also be significantly condensed, but it's not like this is performance-relevant.

    Speaking of easy decoding, perhaps y'all want to take a look at this codec I wrote: https://github.com/Wuerfel21/Heptafon
    Quarters the size of a WAV file (as the name implies, 7 bits per stereo sample, not counting sector overhead), can decode at 160 kHz on a P1 with PASM decoder and is better quality than many common ADPCM-based codecs. Haven't written a P2 ASM decoder, if someone needs one, just ask. I spent quite a lot of time trying different ideas and tuning everything, it is really quite good.

  • @JonnyMac that code is actually busted.

    Says you. I pulled it from a working program, and only as an example snippet of the simplicity of WAV decoding.

    Could also be significantly condensed, ...

    Maybe, but I tend to go for simple, obvious, working code so that new people can join in.

    When we have a proper uSD driver for Spin2, I will recreate the commercial WAV player I wrote for the P1 that is sold by EFX-TEK.

  • @JonnyMac said:

    @JonnyMac that code is actually busted.

    Says you. I pulled it from a working program, and only as an example snippet of the simplicity of WAV decoding.

    Obviously it works and the clipping is fairly minor at worst, but that doesn't make it less wrong, especially if you intend people to look at it to learn from. The DAC range issue is non-obvious and ignoring it in example code just makes it more so.

    The (presumed) fix would be to simply change mul level, #$7FFF/100 to mul level, #$7F7F/100, the bitnot ls, #15 under .set_volume to add ls,##$7F80 and wypin ##$8000, #L_OUT to to wypin ##$7F80, #L_OUT.

    You may correctly worry that this looses you some bit precision, but so does the code as-is due to bugs with the volume adjustment (which I just noticed looking at it again). First, there's rounding error in the volume calculation ($(7FFF / 100) * 100 is only $7FBC), second, ($8000 * $7FBC) >> 16 results in a max. amplitude of only $3FDE. The shift should be 15 bits to allow full swing. I guess this backflips into the clipping issue never being present because the full DAC range isn't used, anyways.

    Do not question my supernatural ability to decimate questionable code on sight. ;P

  • @ersmith Thanks for posting this! Going to have to check this out! I spent hours last week googling for something exactly like this. Closest I found was MP3PlayLib from mp3-tech.org

  • Christof Eb.Christof Eb. Posts: 1,087
    edited 2023-01-18 21:21

    Thanks for the inputs!
    Thanks Eric! That is certainly something to try out!

  • I used libmad on an ARM920t a long time ago. 90MHz was enough for playback. But know that libmad has ARM assembly for the inverse DCT.

  • Might also be worth looking at Rockbox. While most of the targeted platforms have hardware decoding, not all of them do, and many are slow enough to need a pretty efficient codec.

  • pik33pik33 Posts: 2,347

    When we have a proper uSD driver for Spin2, I will recreate the commercial WAV player I wrote for the P1 that is sold by EFX-TEK.

    You have a working (and now efficient) driver in Flexprop, so simply compile the player with that. My player plays wav files without any problems using this. (there was a problem earlier with the driver being too slow, but now it is history)

  • You have a working (and now efficient) driver in Flexprop,

    I don't use FlexProp.

  • RaymanRayman Posts: 13,797

    I also think wav is much better than mp3. Compression alters the waveforms. Maybe not in a way I can tell, but still…

    uSD cards are huge now.
    I think if Apple still made iPods, it would be uncompressed or at least lossless compressed

  • RaymanRayman Posts: 13,797
    edited 2023-01-20 01:33

    MP3 would be good though if don’t want to rely on uSD and put media on boot flash instead..

  • FLAC might be a suitable compromise between MP3 and uncompressed PCM.

    It’s FOSS, supported by the major OSs, the system is biased toward fast decoding (at the expense of slower encoding), and there’s a reference implementation available in C.

    The iPods that Apple make these days come with a phone built in ;-), and support use of ALAC (Apple Lossless Audio Codec) which is also FOSS but can be more power hungry and result in bigger files.

  • pik33pik33 Posts: 2,347

    @Rayman said:
    MP3 would be good though if don’t want to rely on uSD and put media on boot flash instead..

    I simply have a lot of files in mp3 format, so the simplest solution is to put them on SD as they are. Now, my player plays .wav, so I have to decompress them first. I have no free time now to play with that, but while I find some time, I will add the decoder to my player. All what I need from the mp3 decoder to do this is a "decode frame" function. All the rest of the stuff is actually implemented it he player.

  • @pik33 said:

    @Rayman said:
    MP3 would be good though if don’t want to rely on uSD and put media on boot flash instead..

    I simply have a lot of files in mp3 format, so the simplest solution is to put them on SD as they are. Now, my player plays .wav, so I have to decompress them first. I have no free time now to play with that, but while I find some time, I will add the decoder to my player. All what I need from the mp3 decoder to do this is a "decode frame" function. All the rest of the stuff is actually implemented it he player.

    It is interesting, that people here keep saying that a WAV player would be sufficient. I was kind of locked into thinking, that MP3 is mandatory. There is a WAV player just sitting there on my table built into Taqoz.
    On the other hand files from the internet or CD must somehow come onto this SD-card and it should be possible for a third person to do this relatively easily. I am somewhat scared that Digital Right Management might be the show stopper. Never heard of DAISY format, which is used here: https://katalog.blista.de/hoerbuecher/

    Just downloaded an audio book (a free one from some other source) of 1.5h = 90MB MP3 and converted it on my PC with Audacity to signed 16bit WAV, which took about 2* 30sec and gave 900MB. So I can try, if my Taqoz circular knitting machine can tell me stories, if I hook up an amplifier. :-)

  • Just searched for a ST7735 driver and found this: https://forums.parallax.com/discussion/174147/parallax-font-on-st7735-tft
    So once again something to be thankful for which has been given from Phil!!!

  • pik33pik33 Posts: 2,347
    edited 2023-05-12 17:22

    @ersmith said:

    @pik33 said:
    I tried to recompile C-based mp3 decoders for a P2 using Flexprop. The result was way too slow to run in the real time.

    I still think a P2 can decode mp3 in the real time, it only needs a proper decoder written for it, in asm.

    I think you picked the wrong decoder :). Attached is a zip file of the Adafruit_MP3 decoder, which on a P2 running at 250 MHz decodes at ~1824 kbps, which is enough for 48 kHz stereo (16 bits/sample). Mind you that is using a whole cog, and I don't have actual audio output hooked up yet (it's left as an exercise for the reader). You can optionally define WRITE_FILE in examples/standalone/main.c to get the output to the host in a file called "output.bin"; that slows the decode process down to below real-time, but allows verification of the output.

    To compile this you'll need FlexC 5.9.26. The example is in examples/standalone; there's a README_P2.txt at the top level describing how to build and run.

    This thing is a classic C style code, a lot of small files, a main file can be written in C, but how to make a library out of this that can be used as a class for Basic code and compile without using a long command line, from inside standard Flexprop environment? I am now trying to put all of these c and h into one working file and now it even compiles, but how I can access in Basic something that is a pointer to enum? I think the workaround is to write an additional function in C that will work as an interface to Basic code, keeping, for example, that enum inside C and returning "normal" integer.


    Edit:
    (1) simply assign the enum to int
    (2) I misunderstood the code, this is not a pointer to enum, this is simply void* and I hope it is compatible with Basic's pointer to any.

    There is a hope...


    I only need init and decode frame function to see from Basic, so I will try what I can do with my very limited C skills.


    Edit: ## it works! o:)

  • Yes, you can treat the enum just as an integer. void* should indeed be the same as "pointer to any".

    In theory it should be possible to put all the .c files into a .fpide project file. But mixing BASIC and C isn't something I've tried, so it probably won't work as-is :(.

  • pik33pik33 Posts: 2,347

    I wantedd to try the original decoder from > @ersmith said:

    @pik33 said:
    I tried to recompile C-based mp3 decoders for a P2 using Flexprop. The result was way too slow to run in the real time.

    I still think a P2 can decode mp3 in the real time, it only needs a proper decoder written for it, in asm.

    I think you picked the wrong decoder :). Attached is a zip file of the Adafruit_MP3 decoder, which on a P2 running at 250 MHz decodes at ~1824 kbps, which is enough for 48 kHz stereo (16 bits/sample). Mind you that is using a whole cog, and I don't have actual audio output hooked up yet (it's left as an exercise for the reader). You can optionally define WRITE_FILE in examples/standalone/main.c to get the output to the host in a file called "output.bin"; that slows the decode process down to below real-time, but allows verification of the output.

    To compile this you'll need FlexC 5.9.26. The example is in examples/standalone; there's a README_P2.txt at the top level describing how to build and run.

    Maybe the decoder I tried was not as wrong as it looked. These fixed point decoders have asm part for several operation defined for standard CPUs but not for a P2. Your example has included asm code for a P2 while in what I tried to compile, I set the CPU to other and these function were defined in C instead of asm

    The Adafruit MP3 decoder in my experiments works now, but what it outputs is not a good quality audio. I don't know now if it is decoder itself (too low precision?) or maybe I broke something during the implementation.

Sign In or Register to comment.