Skip to content
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

Feature Text To Speech #191

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ddea7bf
Merge pull request #1 from biologist79/master
Joe91 Dec 18, 2022
c669a67
Merge pull request #2 from biologist79/master
Joe91 Jan 6, 2023
6fee1a6
feature tts as playmode and button-action
Joe91 Jan 8, 2023
db08969
fix English translation
Joe91 Jan 8, 2023
7077626
some cleanup on the website
Joe91 Jan 8, 2023
5ac8e09
english translation review
Joe91 Jan 8, 2023
d97ac88
Merge pull request #3 from biologist79/master
Joe91 Feb 6, 2023
33c09ce
Merge pull request #4 from biologist79/master
Joe91 Feb 7, 2023
48605b3
Merge branch 'master' into feature_tts
Joe91 Feb 9, 2023
72856e3
removed web-stuff and start to add them again.
Joe91 Feb 9, 2023
fb64bb9
try to add command-selection
Joe91 Feb 9, 2023
fef0288
Bugfix for automatic file generation
laszloh Feb 10, 2023
9e46a7f
Merge pull request #5 from laszloh/i18n_bugfix
Joe91 Feb 11, 2023
ca36576
Merge pull request #6 from biologist79/master
Joe91 Feb 11, 2023
f9189a7
Merge remote-tracking branch 'origin/master' into feature_tts
Joe91 Feb 11, 2023
2d72990
add custom-text-command again
Joe91 Feb 9, 2023
80af63d
bugfix: UTF8 encoding & missing translation
laszloh Feb 13, 2023
8c9e94b
Merge pull request #7 from biologist79/master
Joe91 Feb 14, 2023
31bc052
Merge branch 'master' into feature_tts
Joe91 Feb 14, 2023
cede224
execute modification works again
Joe91 Feb 14, 2023
3136802
prepare for custom-text
Joe91 Feb 14, 2023
0fcf902
Merge pull request #8 from laszloh/i18n_bugfix
Joe91 Feb 14, 2023
857fe25
add custom-text again
Joe91 Feb 14, 2023
abd804e
don't require text anymore
Joe91 Feb 14, 2023
4e2f34b
Merge pull request #9 from biologist79/master
Joe91 Feb 14, 2023
11497bf
fix assignment in webserver
Joe91 Feb 14, 2023
9f2e300
Merge pull request #10 from biologist79/master
Joe91 Feb 23, 2023
bddd713
Merge remote-tracking branch 'origin/master' into feature_tts
Joe91 Feb 23, 2023
5fdeb92
fix merge issues
Joe91 Feb 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion html/management_DE.html
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,39 @@
<legend>Aktueller Titel</legend>
<div id="track"></div>
</div>
<div class="form-group col-md-12">
<legend>Command Control</legend>
<select class="form-control" id="commandId" name="commandId">
<option class="placeholder" disabled selected value="">Modifikation auswählen</option>
<option value="100">Tastensperre</option>
<option value="179">Schlafe sofort</option>
<option value="101">Schlafen nach 15 Minuten</option>
<option value="102">Schlafen nach 30 Minuten</option>
<option value="103">Schlafen nach 1 Stunde</option>
<option value="104">Schlafen nach 2 Stunden</option>
<option value="105">Schlafen nach Ende des Titels</option>
<option value="106">Schlafen nach Ende der Playlist</option>
<option value="107">Schlafen nach fünf Titeln</option>
<option value="110">Wiederhole Playlist (endlos)</option>
<option value="111">Wiederhole Titel (endlos)</option>
<option value="120">Dimme LEDs (Nachtmodus)</option>
<option value="130">Aktiviere/deaktive WLAN</option>
<option value="140">Bluetooth Lautsprecher aktivieren/deaktivieren</option>
<option value="141">Bluetooth Kopfhörer aktivieren/deaktivieren</option>
<option value="150">Aktiviere FTP</option>
<option value="152">Spreche Text</option>
<option value="0">Lösche Zuordnung</option>
<option value="170">Play/Pause</option>
<option value="171">Vorheriger Titel</option>
<option value="172">Nächster Titel</option>
<option value="173">Erster Titel</option>
<option value="174">Letzter Titel</option>
<option value="180">Springe vorwärts (n Sekunden)</option>
<option value="181">Springe rückwärts (n Sekunden)</option>
</select>
<button type="button" onclick="executeCommand()" class="btn btn-primary" >Ausführen</button>
</div>

</div>
</div>
<div class="tab-pane fade show active" id="nav-rfid" role="tabpanel" aria-labelledby="nav-rfid-tab">
Expand Down Expand Up @@ -295,6 +328,7 @@
<option class="option-folder" value="9">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>
<option class="option-folder" value="13">Alle Titel aus einem zufälligen Unterverzeichnis (sortiert)</option>
<option class="option-stream" value="8">Webradio</option>
<option class="option-stream" value="14">Text To Speech</option>
<option class="option-stream" value="11">Liste (Dateien von SD und/oder Webstreams) aus lokaler .m3u-Datei</option>
</select>
</div>
Expand All @@ -318,6 +352,7 @@
<option value="140">Bluetooth Lautsprecher aktivieren/deaktivieren</option>
<option value="141">Bluetooth Kopfhörer aktivieren/deaktivieren</option>
<option value="150">Aktiviere FTP</option>
<option value="152">Spreche Text</option>
<option value="0">Lösche Zuordnung</option>
<option value="170">Play/Pause</option>
<option value="171">Vorheriger Titel</option>
Expand Down Expand Up @@ -464,6 +499,18 @@
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset>
<legend>Custom Text</legend>

<label for="customText">Custom Text:</label>
<input type="text" class="form-control" id="customText" placeholder="CUSTOM TEXT" name="customText" required>
<div class="invalid-feedback">
Bitte Custom-Text eintragen.
</div>
</fieldset>
</div>
<br>

<div class="form-group col-md-12">
<fieldset>
Expand Down Expand Up @@ -1209,7 +1256,8 @@
vWarning: document.getElementById('warningLowVoltage').value,
vIndLow: document.getElementById('voltageIndicatorLow').value,
vIndHi: document.getElementById('voltageIndicatorHigh').value,
vInt: document.getElementById('voltageCheckInterval').value
vInt: document.getElementById('voltageCheckInterval').value,
vCustText: document.getElementById('customText').value
}
};
var myJSON = JSON.stringify(myObj);
Expand Down Expand Up @@ -1310,6 +1358,17 @@
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}

function executeCommand() {
var myObj = {
"controls": {
action: document.getElementById('commandId').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}

function sendVolume(vol) {
var myObj = {
"controls": {
Expand Down
62 changes: 61 additions & 1 deletion html/management_EN.html
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,39 @@
<legend>Current track</legend>
<div id="track"></div>
</div>
<div class="form-group col-md-12">
<legend>Command Control</legend>
<select class="form-control" id="modId" name="modId">
<option class="placeholder" disabled selected value="">Select modification</option>
<option value="100">Keylock</option>
<option value="179">Schlafe immediately</option>
Joe91 marked this conversation as resolved.
Show resolved Hide resolved
<option value="101">Sleep after 15 minutes</option>
<option value="102">Sleep after 30 minutes</option>
<option value="103">Sleep after 1 hour</option>
<option value="104">Sleep after 2 hours</option>
<option value="105">Sleep after end of track</option>
<option value="106">Sleep after end of playlist</option>
<option value="107">Sleep after end of five tracks</option>
<option value="110">Loop playlist</option>
<option value="111">Loop track</option>
<option value="120">Dimm LEDs (nightmode)</option>
Joe91 marked this conversation as resolved.
Show resolved Hide resolved
<option value="130">Toggle WiFi</option>
<option value="140">Toggle Bluetooth</option>
<option value="141">Bluetooth Headphoe aktivation / deaktivation </option>
Joe91 marked this conversation as resolved.
Show resolved Hide resolved
<option value="150">Enable FTP</option>
<option value="152">Speek Text</option>
<option value="0">Remove assignment</option>
<option value="170">Toggle Play/Pause</option>
<option value="171">Previous track</option>
<option value="172">Next track</option>
<option value="173">First track</option>
<option value="174">Last track</option>
<option value="180">Seek forwards (n seconds)</option>
<option value="181">Seek backwards (n seconds)</option>
</select>
<button type="button" onclick="executeCommand()" class="btn btn-primary" >Ausführen</button>
</div>

</div>
</div>
<div class="tab-pane fade show active" id="nav-rfid" role="tabpanel" aria-labelledby="nav-rfid-tab">
Expand Down Expand Up @@ -295,6 +328,7 @@
<option class="option-folder" value="9">All tracks of a directory (random, loop)</option>
<option class="option-folder" value="13">All tracks of a random subdirectory (sorted alph.)</option>
<option class="option-stream" value="8">Webradio</option>
<option class="option-stream" value="14">Text To Speech</option>
<option class="option-stream" value="11">List (files from SD and/or webstreams) from local .m3u-File</option>
</select>
</div>
Expand All @@ -316,7 +350,9 @@
<option value="120">Dimm LEDs (nightmode)</option>
<option value="130">Toggle WiFi</option>
<option value="140">Toggle Bluetooth</option>
<option value="141">Bluetooth Headphoe aktivation / deaktivation </option>
Joe91 marked this conversation as resolved.
Show resolved Hide resolved
<option value="150">Enable FTP</option>
<option value="152">Speek Text</option>
<option value="0">Remove assignment</option>
<option value="170">Toggle Play/Pause</option>
<option value="171">Previous track</option>
Expand Down Expand Up @@ -463,6 +499,18 @@
</fieldset>
</div>
<br>
<div class="form-group col-md-12">
<fieldset>
<legend>Custom Text</legend>

<label for="customText">Custom Text:</label>
<input type="text" class="form-control" id="customText" placeholder="CUSTOM TEXT" name="customText" required>
<div class="invalid-feedback">
Please enter custom-text.
</div>
</fieldset>
</div>
<br>

<div class="form-group col-md-12">
<fieldset>
Expand Down Expand Up @@ -1208,7 +1256,8 @@
vWarning: document.getElementById('warningLowVoltage').value,
vIndLow: document.getElementById('voltageIndicatorLow').value,
vIndHi: document.getElementById('voltageIndicatorHigh').value,
vInt: document.getElementById('voltageCheckInterval').value
vInt: document.getElementById('voltageCheckInterval').value,
vCustText: document.getElementById('customText').value
}
};
var myJSON = JSON.stringify(myObj);
Expand Down Expand Up @@ -1309,6 +1358,17 @@
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}

function executeCommand() {
var myObj = {
"controls": {
action: document.getElementById('commandId').value
}
};
var myJSON = JSON.stringify(myObj);
socket.send(myJSON);
}

function sendVolume(vol) {
var myObj = {
"controls": {
Expand Down
66 changes: 62 additions & 4 deletions src/AudioPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ static uint8_t AudioPlayer_InitVolume = AUDIOPLAYER_VOLUME_INIT;
static void AudioPlayer_Task(void *parameter);
static void AudioPlayer_HeadphoneVolumeManager(void);
static char **AudioPlayer_ReturnPlaylistFromWebstream(const char *_webUrl);
static char **AutioPlayer_ReturnPlaylistFromTextToSpeech(const char *_textToInsert);
static int AudioPlayer_ArrSortHelper(const void *a, const void *b);
static void AudioPlayer_SortPlaylist(const char **arr, int n);
static void AudioPlayer_SortPlaylist(char *str[], const uint32_t count);
Expand Down Expand Up @@ -624,6 +625,12 @@ void AudioPlayer_Task(void *parameter) {
if (gPlayProperties.playMode == WEBSTREAM || (gPlayProperties.playMode == LOCAL_M3U && gPlayProperties.isWebstream)) { // Webstream
audioReturnCode = audio->connecttohost(*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
gPlayProperties.playlistFinished = false;
} else if (gPlayProperties.playMode == TEXT_TO_SPEECH) {
audioReturnCode = true; // is handled later
gPlayProperties.tellCustomText = true;
gPlayProperties.currentSpeechActive = true;
gPlayProperties.lastSpeechActive = true;
gPlayProperties.playlistFinished = false;
} else if (gPlayProperties.playMode != WEBSTREAM && !gPlayProperties.isWebstream) {
// Files from SD
if (!gFSystem.exists(*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber))) { // Check first if file/folder exists
Expand Down Expand Up @@ -657,6 +664,8 @@ void AudioPlayer_Task(void *parameter) {
} else {
Audio_setTitle("Webradio");
}
} else if (gPlayProperties.tellCustomText) {
Audio_setTitle("Text To Speech");
} else {
if (gPlayProperties.numberOfTracks > 1) {
Audio_setTitle("(%u/%u): %s", gPlayProperties.currentTrackNumber+1, gPlayProperties.numberOfTracks, *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
Expand Down Expand Up @@ -719,10 +728,32 @@ void AudioPlayer_Task(void *parameter) {
}
}

// Handle custom text
if (gPlayProperties.tellCustomText) {
gPlayProperties.tellCustomText = false;
bool speechOk;
String customText = "";
if (gPlayProperties.playMode == TEXT_TO_SPEECH){
customText = *(gPlayProperties.playlist);
} else {
customText = gPrefsSettings.getString("customText", "");
}
#if (LANGUAGE == DE)
speechOk = audio->connecttospeech(customText.c_str(), "de");
#else
speechOk = audio->connecttospeech(customText.c_str(), "en");
#endif
if (!speechOk) {
System_IndicateError();
}
}

// If speech is over, go back to predefined state
if (!gPlayProperties.currentSpeechActive && gPlayProperties.lastSpeechActive) {
gPlayProperties.lastSpeechActive = false;
if (gPlayProperties.playMode != NO_PLAYLIST) {
if (gPlayProperties.playMode == TEXT_TO_SPEECH) {
gPlayProperties.playMode = NO_PLAYLIST;
} else if (gPlayProperties.playMode != NO_PLAYLIST) {
xQueueSend(gRfidCardQueue, gPlayProperties.playRfidTag, 0); // Re-inject previous RFID-ID in order to continue playback
}
}
Expand Down Expand Up @@ -835,7 +866,11 @@ void AudioPlayer_TrackQueueDispatcher(const char *_itemToPlay, const uint32_t _l
gPlayProperties.currentTrackNumber = _trackLastPlayed;
char **musicFiles;

if (_playMode != WEBSTREAM) {
if (_playMode == WEBSTREAM) {
musicFiles = AudioPlayer_ReturnPlaylistFromWebstream(filename);
} else if (_playMode == TEXT_TO_SPEECH) {
musicFiles = AutioPlayer_ReturnPlaylistFromTextToSpeech(filename); // no modification needed
} else {
if (_playMode == RANDOM_SUBDIRECTORY_OF_DIRECTORY) {
filename = SdCard_pickRandomSubdirectory(filename); // *filename (input): target-directory // *filename (output): random subdirectory
if (filename == NULL) { // If error occured while extracting random subdirectory
Expand All @@ -846,8 +881,6 @@ void AudioPlayer_TrackQueueDispatcher(const char *_itemToPlay, const uint32_t _l
} else {
musicFiles = SdCard_ReturnPlaylist(filename, _playMode);
}
} else {
musicFiles = AudioPlayer_ReturnPlaylistFromWebstream(filename);
}

// Catch if error occured (e.g. file not found)
Expand All @@ -857,6 +890,7 @@ void AudioPlayer_TrackQueueDispatcher(const char *_itemToPlay, const uint32_t _l
if (gPlayProperties.playMode != NO_PLAYLIST) {
AudioPlayer_TrackControlToQueueSender(STOP);
}
free(filename);
return;
}

Expand Down Expand Up @@ -978,6 +1012,18 @@ void AudioPlayer_TrackQueueDispatcher(const char *_itemToPlay, const uint32_t _l
break;
}

case TEXT_TO_SPEECH: { // This is also always just one "track"
Log_Println((char *) FPSTR(modeWebstream), LOGLEVEL_NOTICE);
if (Wlan_IsConnected()) {
xQueueSend(gTrackQueue, &(musicFiles), 0);
} else {
Log_Println((char *) FPSTR(webstreamNotAvailable), LOGLEVEL_ERROR);
System_IndicateError();
gPlayProperties.playMode = NO_PLAYLIST;
}
break;
}

case LOCAL_M3U: { // Can be one or multiple SD-files or webradio-stations; or a mix of both
Log_Println((char *) FPSTR(modeWebstreamM3u), LOGLEVEL_NOTICE);
xQueueSend(gTrackQueue, &(musicFiles), 0);
Expand Down Expand Up @@ -1053,6 +1099,18 @@ char **AudioPlayer_ReturnPlaylistFromWebstream(const char *_webUrl) {
return ++url;
}

// Adds Text To Speech to playlist; same like SdCard_ReturnPlaylist() but always only one entry
char **AutioPlayer_ReturnPlaylistFromTextToSpeech(const char *_textToInsert) {
char *textToInsert = x_strdup(_textToInsert);
static char **playlist;
playlist = (char **)x_malloc(sizeof(char *) * 2);
playlist[0] = x_strdup("1"); // Number of files is always 1 in url-mode
playlist[1] = x_strdup(textToInsert);

free(textToInsert);
return ++playlist;
}

// Adds new control-command to control-queue
void AudioPlayer_TrackControlToQueueSender(const uint8_t trackCommand) {
xQueueSend(gTrackControlQueue, &trackCommand, 0);
Expand Down
1 change: 1 addition & 0 deletions src/AudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ typedef struct { // Bit field
bool currentPlayMono: 1; // true if mono; false if stereo
bool isWebstream: 1; // Indicates if track currenty played is a webstream
bool tellIpAddress: 1; // If true current IP-address is spoken
bool tellCustomText: 1; // If true current IP-address is spoken
bool currentSpeechActive: 1; // If speech-play is active
bool lastSpeechActive: 1; // If speech-play was active
size_t coverFilePos; // current cover file position
Expand Down
16 changes: 16 additions & 0 deletions src/Cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,22 @@ void Cmd_Action(const uint16_t mod) {
break;
}

case CMD_TELL_CUSTOM_TEXT: {
if (Wlan_IsConnected()) {
if (!gPlayProperties.pausePlay) {
AudioPlayer_TrackControlToQueueSender(PAUSEPLAY);
}
gPlayProperties.tellCustomText = true;
gPlayProperties.currentSpeechActive = true;
gPlayProperties.lastSpeechActive = true;
System_IndicateOk();
} else {
Log_Println(unableToTellIpAddress, LOGLEVEL_ERROR);
System_IndicateError();
}
break;
}

case CMD_PLAYPAUSE: {
if (OPMODE_NORMAL == System_GetOperationMode()) {
AudioPlayer_TrackControlToQueueSender(PAUSEPLAY);
Expand Down
2 changes: 2 additions & 0 deletions src/Web.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ bool processJsonRequest(char *_serialJson) {
float vIndLow = doc["general"]["vIndLow"].as<float>();
float vIndHi = doc["general"]["vIndHi"].as<float>();
uint8_t vInt = doc["general"]["vInt"].as<uint8_t>();
const char *_customText = object["general"]["vCustText"];

gPrefsSettings.putUInt("initVolume", iVol);
gPrefsSettings.putUInt("maxVolumeSp", mVolSpeaker);
Expand All @@ -459,6 +460,7 @@ bool processJsonRequest(char *_serialJson) {
gPrefsSettings.putFloat("vIndicatorLow", vIndLow);
gPrefsSettings.putFloat("vIndicatorHigh", vIndHi);
gPrefsSettings.putUInt("vCheckIntv", vInt);
gPrefsSettings.putString("customText", _customText);

// Check if settings were written successfully
if (gPrefsSettings.getUInt("initVolume", 0) != iVol ||
Expand Down
Loading