-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add MP3 playback #2337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MP3 playback #2337
Conversation
bdbde51
to
04be730
Compare
@tannewt I'm guessing you will want this in a new module |
@jepler neat - can i try testing it with PWMout as well or is only i2sout gonna work? |
This is awesome @ladyada and @jepler, perfect timing :-) For this year's CircuitPython powered badges, we decided to ditch the VS1003B chip and go with a small speaker shaped shitty add-on with a small piezo buzzer: So far, we tested it with the @jepler which DAC have you used? We'll look into include them in our speaker board as well |
@urish in theory this is going to work with whatever kind if circuitpython AudioOut you have, whether that's DAC-based AudioOut of samd chips, I2S AudioOut of samd and nRF chips, PWM AudioOut of nRF chips, etc. in practice, there could always be problems. For now, if you can't try a build that just includes this draft PR, I recommend making sure you can play a 16-bit, 2-channel, 44.1kHz WaveFile or RawSample through what you have; that should be a pretty good test. Let us know if you run into trouble. If it relates to details of the badge that you don't want to leak just yet, you can find my email address on all of my commits. :) (I know that what you show above has a single speaker, but we adapted Adafruit_MP3 and some notes there say to only use stereo MP3s; I didn't try a mono one yet to see why) |
@jepler I built your branch and got it to work on the badge, though, there's a lot of background static noise and the MP3 I'm using for testing is played in a wrong sample rate. I attach it if you are interested in looking into this. Thanks for all your efforts so far! |
@urish can you tell me more about your board? microcontroller, and which type of audio output, would be a good start. I tried your file on a pyportal and it seemed to be playing at the right speed and without any background static. |
Sure, nRF52840 and PWM audio with a piezo buzzer. Should I build again and retry? |
@urish I make a distinction between a piezo buzzer (runs on DC and includes a tiny oscillator) and a piezo speaker. Could you be confusing the two? (I'm new here, sorry for the kibitzing should I be getting in your way.) |
It's a piezo speaker then, though its datasheet definitely calls it a "Buzzer" |
If you play a wave file or rawsample with the same specs (sample rate, channel count, bit depth) is it playing at the right frequency or the wrong frequency? is the static noise the same or different? This will help us determine whether it's mp3s or any type of audio to move things forward. |
@jepler I converted the mp3 file to wav using Audacity, and the Wav file exhibits the same issues |
@urish Ah, yes, the data sheet calls it a Piezoelectric Passive Buzzer. I guess a "passive buzzer" is a speaker. The company seems to make also make those with the circuit inside, what I have been calling buzzer. I guess I will have to accept that some speakers are called buzzers. |
@urish does your board have an RC filter on the PWM output? Do you have a way to try fitting one? When using the CircuitPlayground Bluefruit's built in speaker there is a lot of "hiss" which I think probably comes from the interaction between the ~60-70kHz PWM carrier frequency and whatever the amplifier is doing. I get better audio when I use a PAM8302A amplifier breakout, and really quite good audio when I added an RC filter with R*C = 16kHz, chosen almost at random. |
@jepler not at all, but I can definitely order a new rev and fit in an RC filter. Currently I don't have an amp, so the audio output is directly fed to the speaker, but I'm thinking about adding an LM386 to the next revision. I looked at the Bluefruit's schematic, and it seems like it includes an RC filter at the amplifier's input: Or did I miss something? |
That combination of resistor and capacitor in series looks like it is converting the audio signal to be AC-coupled. The kind of RC filter I'm talking about looks more like this: the resistor in series limits how quickly the capacitor can charge/discharge, smoothing the square wave of the PWM signal into a triangle(ish) wave of lower amplitude. The product R * C tells you the audio frequency in Hz where a certain degree of attenuation is achieved. On the CPB, the PWM carrier frequency is (intended to be) >= 62.5kHz, which is well above audio frequency. However, the amplifier is also operating in a digital mode (200-300kHz) and this can lead to unwanted high frequencies being reproduced as audible frequencies. Whether any of this applies to your buzzer (maybe not, if it is "passive") I don't know. I tried to produce a recording showing the huge quality difference going from the internal CPB speaker to an external class-D amplifier with an RC filter, but my recording equipment is not up to the task. |
(also I'd choose a R*C product more like 20kHz than 200kHz, since 20kHz frequently represents an upper end of human hearing. Something that is above the 62.5kHz PWM frequency of the CPB will not help anything) |
@urish It's I, Dar, butting in again. Have you checked out the speaker/buzzer for sound quality? Perhaps that is the weakest point in your audio chain. The same company makes buzzers with an oscillator inside that look much the same. Buzzers are typically designed to make an irritating BAAZP! sound. The one you have takes an external oscillator and is optimized for 4kHz IIRC. It should work as a speaker, but perhaps it is a very poor speaker. |
@urish BTW, the piezo speaker is a capacitive load. For a low-pass filter, you might be able to just put a resistor in series with the speaker. (Or increase the value of it, if you already have one.) I don't know your circuit, but some drivers have a limit on the capacitance of the load, so again consider the resistor. Also, many assume an inductive load, as with your usual dynamic speaker, and a capacitive load might not work as well. I wish you well in your project and I look forward to hearing how MP3 playback works for you. I hope to be tinkering with that real soon now, well as soon as I figure out github. |
Thanks for all the info @jepler and @Dar-Scott! I guess I'm going back to the drawing board for now :) |
@urish Great, keep in touch! I hope to have a chance to address the playback rate probems next week. |
@jepler Thank you for this. I snagged it and am testing with Feather M4 Express and the Adafruit UDA1334A I2S DAC. Left works, with some faint clicks, perhaps when the audio is loud. Right is very noisy and the sound is attenuated. I tried it with stereo and mono. The files are MP3 64000 bps. I will go on to a different DAC. I can use whatever files you recommend. |
I probably have a DAC compatibility problem. Maybe some bits of L are in the MSB of R, shifting R quieter but adding a lot of seemingly random noise. The DAC might be expecting data to change on the other edge of the clock, or there is an LR clock problem in framing the bits. Or an off-by-one problem. |
@Dar-Scott I've also been using UDA 1334 I2S DAC via Adafruit's breakout board, and I have not noticed an effect like that. That said, I2S is not as standard as it should be, and for instance on the UDA, the SF0/1 pins can select 4 incompatible "I2S-ish" standards for how the bits are aligned. "classic I2S" is what you should be getting if you didn't pull either of those pins high, however. Among the things I am going to do Monday are finish preparing a creative commons licensed set of mp3 files for testing, so we can all have a source file we agree on. (It's almost done, I just over-complicated it with Makefiles and id3tags and such) Let me recommend again trying with a segment of the same audio sample but in .wav format (same sample rate, bits per sample, and channel count) to determine whether problems are related to mp3 decoding or general audio playback issues, too. With nRF PWM this let us quickly zero in on sample rate simply being wrong so hopefully it's a good quick sanity check. |
None of the SF0/1 pins are pulled high. I converted my file to wav (both stereo and mono) and the left sounds very good. The right is noisy. Perhaps I broke my DAC board. Even so, it seems strange that the wav generated from the mp3 sounds better (on the left) than the mp3. I have a couple other DACs to try. |
Here's the selection of mp3 files I've been testing with, some piano music from wikipedia: |
.. a requirement that oofatfs needs to be taught to respect. This problem can be demonstrated with the following snippet, except that the related file ("test.bin") must also be contiguous on the filesystem. You can ensure this by reformatting your device's filesystem before testing, then copying any single file bigger than 4kB to test.bin. f = open("test.bin", "rb") f.seek(2048) b = bytearray(2048) v = memoryview(b) f.readinto(v[909:]) Closes: adafruit#2332
@@ -3382,7 +3382,11 @@ FRESULT f_read ( | |||
if (!sect) ABORT(fs, FR_INT_ERR); | |||
sect += csect; | |||
cc = btr / SS(fs); /* When remaining bytes >= sector size, */ | |||
if (cc) { /* Read maximum contiguous sectors directly */ | |||
if (cc | |||
#if _FS_DISK_READ_ALIGNED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we chatted about. We'll leave #2332 open with ideas for a proper fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay! Super excited for this.
@@ -3382,7 +3382,11 @@ FRESULT f_read ( | |||
if (!sect) ABORT(fs, FR_INT_ERR); | |||
sect += csect; | |||
cc = btr / SS(fs); /* When remaining bytes >= sector size, */ | |||
if (cc) { /* Read maximum contiguous sectors directly */ | |||
if (cc | |||
#if _FS_DISK_READ_ALIGNED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we chatted about. We'll leave #2332 open with ideas for a proper fix.
@jepler I think I am now using adafruit:master. (I'm a github noob, so maybe.) MP3 I2S works there. Well, the signals look good to me. I am now playing both channels on my DAC, but am still getting a bad clipping-like sound, which I don't think is an I2S problem. I'm sorry that I am not able to provide a more complete report. I do get a crazy memory error when I (accidentally) try to play a wav file as mp3, and when I try to play the cbr studio MP3 file from your mp3 set. Let me know if you can use more details. Thank you so much for this. I am very excited about it. |
@Dar-Scott let's see if we can't make sure of the version you're using. The problem reading the cbr studio MP3 file sounds like one I believed I had found and fixed. You could just try the 5.0 beta 1 release from circuitpython.org, it has MP3 in it. Or, you could verify that you built what you intended. Open up the serial connection to your board, hit ctrl-c several times until you are dropped in the REPL. Paste in the lne just above the ">>>" prompt. It should look something like this:
From that and a little more advanced git knowledge I can make sure you're running what we hope you're running. If it says "5.0.0-beta.1" with anything else after it, you're good. If not, you're running something older. |
@jepler Here's what I got: Adafruit CircuitPython 5.0.0-beta.0-122-g67097e0e0-dirty on 2019-12-10; Adafruit Feather M4 Express with samd51j19 |
Whoops. Hit Comment instead of Preview. I'll switch to 5.0 beta 1 release. I missed that it was out. |
@jepler I have acquired the latest 5.0 beta and the latest library bundle. Leveling up.
I can now play cbr studio. I still have the noise, more for louder sounds, and maybe more for higher bit rates. My new DAC boards have not arrived, so I'm using the only one I have. I did notice that more and perhaps different noise when my hand is near the BCLK wire. I have a 6" patch, I'll try to shorten that. And I should look at the signals when attached with a 'scope to see how they shape up. The logic analyzer can hide all kinds of evil. The I2S pins seem a little wimpy. I am powering the UDA 1334 board off of the Feather 3.3V using the regulator input, though at some point I did try 5V. Everything on the far side of the board is left open, including mute and de-emphasis. I monitor using either the headphone output or the line-out to amplifier and speakers. |
@jepler I have moved to audio analog output for now until I get the noise resolved. (I think boards came in yesterday.) That works great, so far. Woohoo! I tried playing from a microSD card (both mounted and not mounted), but I keep getting an error right after play(), but I can't tell what is throwing it:
I don't know if this is a corrupted microSD card or an MP3 concern, so I thought I'd mention it. |
@jepler Oh, and with SD the USB drive & serial are lost to the host right after the error message and the REPL message. The error line number still flashes. The board does not boot in safe-mode after the next reset, so I'm caught in a cycle. So, this is a nasty error, for me. |
Crashing hard after an exception may be #2378, which I think is fixed now at the tip of master. There are uf2 images to help you format the flash storage, that may be necessary if you can't get into python safe mode in this circumstance. |
I was able to nimbly reset and click to squeeze in a new main.py. From an MP3 file on the flash drive, playing analog, I get garbage forever when I use two output channels. After catching the exception (try) from attempting to play from an MP3 file on the microSD and then playing analog from the file on the flash drive, I get garbage forever, even just using one channel. Has anybody played native MP3 from an SD card? |
@jepler By "garbage forever", I mean a radar-like banging sound with noise that goes on until I do something, at a time later than when the play should have ended. |
There was a bug that caused analog stereo mp3 on m4 to have static in the right channel all the time. This is fixed in #2375. |
Working with these crashes is like walking upstream in a river of molasses. Here are some hints: If the difference between the first two is the time to fill your buffer, then filling that takes 12 ms longer with the SD. My files are 64,000 bps, 2-channel, 24000 samples per second according to afinfo on my Mac. So, the exception occurs at very roughly 7700 bytes and 2900 samples. I'm guessing that the sector size for my microSD card is 512 bytes. That would make the failure at about 15 sectors. At 64000 bps, that is 120 ms. For me, it is unclear which might have a bug: file, MP3, SD, audio or Dar. |
@Dar-Scott the info about your setup and program has become scattered over a lot of messages. It sounds like you have something that is 100% crashing and my own test setup is different enough that I'm not seeing it. It would be helpful to me if you can distill it down into a fresh issue hopefully with enough info in one place for me to reproduce it. There are some pretty bad issues with 5.0alpha0 with stereo on samd, and with crashes following exceptions. If you go to circuitpython.org for your board, click on "absolute newest", and choose one that is 20191212-10183d5.uf2 or newer, some of the problems may be improved. Thanks, and thanks for sticking with it! Without reports like yours, we won't have the info we need to get mp3 playback to the point where it's solid enough for everyday users. |
I am having problems in playing MP3 from SD cards. Earlier I talked about a problem with I2S. I will get back to that, but right now I'm using analog audio. I am using analog audio on A0 and A1 connected to a TPA2016 and speakers. The amplifier does not have I2C hooked up yet; I'm using the default settings. This sounds nice. (I am using 20191212-10183d5.uf2.) I am passing no parameters when creating a playable beyond the file handle. Both files from your mp3 corpus and from my set play OK when played from the flash, that is, from the same drive I save main.py to. In my microSD tests I play a short file from the flash, wait a couple seconds and then play a file from the microSD card. I have thrashed in trying things like mounting the SD to "/sd" and using the SD file system unmounted. I can see the files on the SD card either way. I have tried reusing objects and rebuilding them between files. I'm not using buffering in the open. And I am opening mode="rb". The SD card is 4GB. I'm using a recent build, 20191212-10183d5.uf2, and a library bundle from today or yesterday. For the SD card, I'm using the usual SPI pins plus A4 for chip select. I'm using the usual library files to make the file system. As I mentioned above, I have mounted it to "/sd" and I have opened filed directly from the new file system without mounting it. I'm using the SparkFun microSD breakout, just raw pins, no level shifters, running it off of 3.3V, (but, arg, no mounting holes). I get a nasty crash when playing from the microSD. About 120 ms into playing, an exception is thrown, type=<class 'OSError'> args=(5,), and I get a nasty crash. Even my Mac and especially my mouse get confused. Reset starts up the file again, so it is a cycle. My Mac starts rejecting it and it is hard to get in a control-C after a reset or plug-in. I tried disabling autoreload, but that is limited since it doesn't seem to be persistent and the crash half takes down the drive and 3/4 takes down the serial. Eventually, those go completely down. I have to reset, and it seems at times, power off, but that might be my frustration. I had been using Mu, but have moved to PyCharm and CoolTerm. I might have started these tests after the move. My audio files seem to be 64,000 bps mp3 stereo. So, essentially, put a /sd in front of the file path and boom. |
Superficially that setup seems similar to mine that is working -- I am using pygamer, and the SD card reader is integrated, but they are both samd51 chips and both are using DAC. I can think of two things to try to exclude as the cause. First, try with the TPA2016 disconnected/unpowered. This would exclude the amplifier drawing so much power that it is causing some sort of glitch in the rest of the system. I don't think this is likely. Second, write a pure circuitpython program to just read the mp3 file and do nothing with it. Something like (untested):
(edited to fix example) If this completes without errors (prints 1091 a bunch of times, then some other smaller number, then 0) it seems to exclude a general problem reading from the SD. |
Thanks @jepler ! That failed with an i/o error immediately. Good news for me is that it doesn't create a nasty crash. However, it may mean that either audio or mp3 is not handling the error very well in playing and I get a crash. However, it might be the size of read that can make an impact and I have not read an evil number of bytes in new testing, and that problem is in the SD library. Your test seems to work when reading less than 1024 bytes. At 1024, the error comes on the third read. At 1901, the error comes immediately. This might be an off-by-one error some place. I don't know of anything goofy with the microSD card or my wiring. I have moved my CS from the usual place. So, the i/o error is not mp3 related, but not handling it correctly might be. |
@jepler I moved from the 4GB card with no name to an 8GB SanDisk card. Reading 1901 bytes repeatedly works. Average speed is about the same. Yay! At this point I don't know if the original card is broken, flakey, or in an unusual corner of the specs. |
If you're unstuck now, that's good news. If you want to pursue the behavior of the 4GB card further, I recommend taking it to https://github.com/adafruit/Adafruit_CircuitPython_SD and if you have problems with mp3s otherwise you can pursue it with a fresh issue here. |
@jepler Thanks! That just the info I need. I'll report separately on what happens when I attempt to play a .wav as a .mp3. I'm unstuck! I will put aside the SD card problem. I will also put aside the I2S problem, for now, very probably mine. I am also able to play from an unmounted microSD card. |
@jepler behavior with bad mp3 file When I try to play a .wav as a .mp3, I no longer get the weird memory exception, but no exception at all is raised. Also audio.playing is True for about 25 ms. I would hope to get either an exception or a False. I can readily work around. I have not tried a garbage file or an empty file. |
Testing performed: On a nRF52840 with I2S DAC on pins 6/9/10, play the "test.mp3" file using this code.py:
I also tested all the following mp3s (
lame
using CBRs and presets that include VBR/ABR) on a pyportal, with the mp3 file residing on a freshly-formatted SD card: