Project - Audio Player

Let your projects be heard - give them a voice, respond to different events with different pre-recorded audio announcements (in different voices, and even different languages), or respond to events by playing different sounds, or perhaps just play music.

The JQ6500 Serial Audio Player module allows storing MP3 or WAV audio files in 16M Flash memory for selective playback controlled by the ESP.
The on-board audio amplifier can directly drive a small loudspeaker, which makes the module rather 'juicy', so I suggest powering via a USB charger.
The JQ6500-28p version (above) has on-board Micro-SD card reader offering up to 32Gb of additional storage.
The 28-pin version with on-board SD card reader costs about a fiver from ebay, and the 16-pin non-SD version is available for less than 2 quid.
USB can be used to copy files directly from computer to the modules on-board Flash memory, or to an inserted SD card on the 28-pin version.

This script sends control codes to select file and folder and change volume etc, but does not bother to read back any 'query' codes from the player.
Therefore only the 3 solid connections are required, the dotted pink connection is optional if you wish to experiment with receiving query codes.
Here is a good link for more information about how to use the JQ6500 family of audio modules:

The script has a 'stretchy' FIFO queue which expands to fill with sequences of audio file names, then uses the 'Busy' pin to control the FIFO output to create in effect a 'sentence' of chained 'words' which are announced one after the other, thus allowing sentences to be made up from individual words and numbers.

I've tried various other audio player modules, and although this is so far the best, they typically squeeze a cut-down version of Filing System into a limited space which is primarily aimed at selecting files by number (typically in the order the files were written to the FAT (file allocation table).
In fairness I suspect the JQ6500 players may actually select by the filename representation of number rather than by file copy order, but no matter if not, because there are free sort utilities available if needed that can physically re-order the file entries in the FAT.
The JQ6500 has various different control methods allowing for button and even IR operation, but we are interested in using Serial Control codes from Annex.
Serial mode uses 1 byte for sending folder information, but I think I've seen somewhere that the limit is 99 folders... which is ample.
It also uses 1 byte for sending the file number, therefore limiting to 255 files even though apparently able to store up to a thousand per folder.
In fairness, up to 99 folders each containing up to 255 files should suffice for any realistic applications requirements.

My choice is to adopt a file convention which uses 1 folder for all required files.
Therefore I reserve the first numbers 1 to 24 for the corresponding 'channel' number alert message, and the next 24 are reserved for a matching complimentary message, ie: x = ChannelxON, x+24=ChannelxOFF (even if not needed for momentary triggers). Numbers 50 to 99 are available for specific 'system' messages such as 75=MuteON, 81=Volume, 82=UP, 83=DOWN etc. Numbers above 100 are for a chained-word vocabulary.

Such a convention makes it fairly easy to use other folders to hold other 'sets' of alternative voice 'messages if wished, eg: in different languages.
Or, as in my case, to add 'custom' sets of messages tailored to location and requirements - so instead of announcing default "Channel 6 Alert" and "Channel 6 Clear" messages, my system announces "Shed Door Open" and "Shed Door Closed" for the appropriate channel 6 sensor.
Changing between English Defaults, English Custom, French Defaults, French Custom etc etc is just a simple matter of changing folders.

It is easy to create your own voice announcement recordings (in different languages and various TTS voices) using a variety of free online 'Text to Speech' services, or by recording 'speaking' google translate, or by using a free Text2Speech utility on computer, or by simply recording your voice.

If you wish to 'chain' together individual recorded words and numbers into spoken sentences you will need to trim off any intro and exit silences.
I used the free Audacity Portable to record and edit audio files:

The script below is just the stripped out audio part of a sensor monitoring system I am doing, so the audio examples I have included below are some of my systems recordings.
The numbered file names need to be in a folder called "01" on the SD card, so unzip the supplied audio files from the zip into a folder called "01" where the script can find them.
File 10.wav is an intro message, followed by the first 5 files in the folder, which will be played via the voice queue at startup.
Then pressing the gpio0 button simply speaks the message number "001.wav"... other event triggers (eg: from a port expander) could of course speak other messages.
It is just intended as an example to 'open the door' and help you give voice to your own systems..

My system is using a pcf8574 port expander to read various sensors and speak the appropriate messages when triggered - I expect I will eventually publish the system when it is perfect, but not till then, because I don't want to be spending much of my time ironing out any wrinkles that people would find..
So this script is merely to demonstrate how to use Annex to play selected audio files in response to events.

If you want to try out your own audio recordings you can copy them into a different folder eg: "02" and select the appropriate folder and file from the Output page.
Note that if you change either the folder or file in the textbox windows you need to PRESS ENTER to register the changed contents back to the variable.and then click the "Play Selected File" button. else it will ignore your typed in changes.

title$ = "Audio Player - Version JQ6500 - by Electroguard"
'Requires JQ6500-28P module, using serial control mode to play selected file.
q$ = "" 'FIFO queue, add files to be played to end of queue, eg: q$ = q$ + " " + filename number (ie: "5"), then GOSUB TALK
serial2.mode 9600, 5, 4    'tx, rx - Only needs TX to send control codes to JQ6500 RX
onserial2 serial2in           'only needed if expecting responses from jq6500
response = 0                  'var to receive serial2 response byte
busypin = 13
busy = 1
pin.mode busypin, input, pullup
interrupt busypin, talk
buttonpin = 0: pin.mode buttonpin, input, pullup                           'using active low gpio0 button
interrupt buttonpin, pressed
vdir = &h01  ' Default audio folder
vfile = &h01  ' Default audio file
vol = 19  'Default to mid-volume (max=30)
'JQ6500 serial codes are defined below for convenience.
vstart = 126 'Serial command start byte
vend = 239 'Serial command end byte
vplay = 13
vpause = 14
vnext = 1
vprev = 2
volup = 4
voldown = 5
vset = 6
vreset = &h0C
vdirfile = &h12
vsource = &h09
vloopmode = &h11
vdchange =  &h0F
vdnext =  &h01
vdprev =  &h00
gosub setvol
gosub paint
onhtmlchange DOREFRESH
pause 500
q$ = "10"
wlog q$
gosub talk
for c = 1 to 5
 q$ = trim$(q$ + " " + str$(c))     'adding demo recordings to the queue, should be played out in order without loss
next c
wlog q$

if pin(busypin) = busy then
  pause 200
 loop until pin(busypin) <> busy
 Q$ = trim$(Q$)
 if Q$ > "" then
  bite$ = word$(Q$,1)
  vfile = val(bite$)
  print2 chr$(vstart);chr$(04);chr$(vdirfile);chr$(vdir);chr$(vfile);chr$(vend)
  Q$ = trim$(word.delete$(Q$,1))

a$ = title$ +  "<BR><BR>"
a$ = a$ + "Folder " + textbox$(vdir)
a$ = a$ + "  File " + textbox$(vfile)
a$ = a$ + "<BR>"
a$ = a$ + button$("Play selected file", PLAYFILE)
a$ = a$ + "<BR><BR>"
a$ = a$ + button$( "<", PREV)
a$ = a$ + button$( "Play", PLAY)
a$ = a$ + button$( ">", NEXTf)
a$ = a$ + "<BR><BR>"
a$ = a$ + button$( "< Folder", DPREV)
a$ = a$ + button$( "Folder >", DNEXT)
a$ = a$ + "<BR><BR>"
a$ = a$ + button$( "< Vol", VOLd)
a$ = a$ + button$( "Vol >", VOLu)
a$ = a$ + slider$( vol, 0, 30)
a$ = a$ + textbox$(vol)
a$ = a$ + button$( "Set Volume", SETVOL)
a$ = a$ + "<BR><BR>"
a$ = a$ + "<BR><BR>"
html a$
a$ = ""


'Play current (last played) file
print2 chr$(vstart);chr$(02);chr$(vplay);chr$(vend)

'Set absolute volume value
print2 chr$(vstart);chr$(03);chr$(vset);chr$(vol);chr$(vend)

'Relative volume decrease
print2 chr$(vstart);chr$(02);chr$(voldown);chr$(vend)

'Relative volume increase
print2 chr$(vstart);chr$(02);chr$(volup);chr$(vend)

'Play specified 'vfile' audio file from 'vdir' folder
print2 chr$(vstart);chr$(04);chr$(vdirfile);chr$(vdir);chr$(vfile);chr$(vend)

'Play next file
print2 chr$(vstart);chr$(02);chr$(vnext);chr$(vend)

'Play previous file
print2 chr$(vstart);chr$(02);chr$(vprev);chr$(vend)

'Select next folder
print2 chr$(vstart);chr$(03);chr$(vdchange);chr$(vdnext);chr$(vend)

'Select previous folder
print2 chr$(vstart);chr$(03);chr$(vdchange);chr$(vdprev);chr$(vend)

'Reset JQ6500
print2 chr$(vstart);chr$(02);chr$(vreset);chr$(vend)
pause 400
print2 chr$(vstart);chr$(03);chr$(vsource);chr$(1);chr$(vend)
temp$ = serial2.input$
wlog "Response=" + hex$(asc(temp$))

wlog "button"
if pin(buttonpin) = 0 then
 q$ = q$ + " " + "1"
 gosub talk

end '------------- end ---------------

Apparently at least one person has been using the JQ6500 audio player module, so I will report a problem I have discovered with mine.
You can easily test if yours is similarly affected by repeatedly using the scripts Play button in the Output page.

If the same sensor alert message is replayed several times, it starts missing the first part of the message, and can eventually miss out all of the message entirely.
Eg: "Gate 1 visitor" becomes "1 visitor" then "visitor" then "or" then "".
I have tried resetting the JQ6500 before playing the message, but makes no difference.
I'm guessing it is a problem with its stripped-down filing system confusing locations of the various file names to locations of the pause and play memory position within files.
When a file is first accessed it references to the beginning of the recording ok.
But after that, it assumes it should be referencing within the same file for Pause and Play position, and if it hasn't already been paused, it seems it just creates a reference point for itself.
That is obviously a bug that would be easily correctable, but asia does not have a good reputation for fixing obvious faults, nor aftercare responsibility.

So it seems the only way for the file system to get the start of the file position correct is when playing it for the first time after playing a different file, else it switches to referencing within the same file and gets it wrong.

Therefore perhaps the problem can be skirted by playing something like a "Ding dong" attention-grabber message first, then playing the required recording.

Or perhaps just use the serial Text to Speech module that featured in the next project.
Robin Baker,
Apr 6, 2019, 3:23 PM