Skip to content

Commit

Permalink
Optimization: Startup, Runtime
Browse files Browse the repository at this point in the history
Lazy initialize Frontend parts to enable faster startup time. This will
delay generation of first synthesis, though.
Cache creation of compiled regex patterns when the UserDict entries change.
This makes execution of User Dictionary replacements as good as
unnoticable.

Additionally:

- Fix replacement pattern for UserDictionary entry terms.
- Replace NormDict => UserDict
- Disable strict mode policies
- Add somoe TODO comments


Signed-off-by: Daniel Schnell <[email protected]>
  • Loading branch information
lumpidu committed Mar 22, 2024
1 parent f9b1673 commit 131289f
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 67 deletions.
70 changes: 55 additions & 15 deletions app/src/main/java/com/grammatek/simaromur/AppRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
* Abstracted application repository as promoted by the Architecture Guide.
Expand All @@ -66,8 +68,10 @@ public class AppRepository {
private final NormDictEntryDao mNormDictDao;
private LiveData<AppData> mAppData;
private LiveData<List<com.grammatek.simaromur.db.Voice>> mAllVoices;
private LiveData<List<com.grammatek.simaromur.db.NormDictEntry>> mAllUserDictEntries;
private AppData mCachedAppData;
private List<com.grammatek.simaromur.db.Voice> mAllCachedVoices;
private HashMap<NormDictEntry, Pattern> mAllCachedUserDictEntries;
private final VoiceController mNetworkVoiceController;
private final SpeakController mNetworkSpeakController;
private final ApiDbUtil mApiDbUtil;
Expand Down Expand Up @@ -232,6 +236,9 @@ public AppRepository(Application application) throws IOException {
});
mAllVoices = mVoiceDao.getAllVoices();
mAllVoices.observeForever(voices -> {
if (voices == null) {
return;
}
Log.v(LOG_TAG, "mAllVoices update: " + voices);
// Update cached voices
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Expand All @@ -240,11 +247,39 @@ public AppRepository(Application application) throws IOException {
mAllCachedVoices = Objects.requireNonNullElse(voices, new ArrayList<>());
}
});

mAllUserDictEntries = mNormDictDao.getSortedEntries();
mAllUserDictEntries.observeForever(entries -> {
if (entries == null) {
return;
}
Log.v(LOG_TAG, "mAllUserDictEntries update: " + entries.size() + " entries");
mAllCachedUserDictEntries = createNormDictRegexMap(entries);
});
mMediaPlayer = new MediaPlayObserver();
mScheduler = Executors.newSingleThreadScheduledExecutor();
// only do this once at the beginning
mScheduler.schedule(assetVoiceRunnable, 0, TimeUnit.SECONDS);
Log.v(LOG_TAG, "AppRepository() done");
}

/**
* Create a map of regex patterns for all normalization dictionary entries for fast
* lookup during normalization.
*
* @param entries list of normalization dictionary entries
* @return map of regex patterns for all normalization dictionary entries
*/
private HashMap<NormDictEntry, Pattern> createNormDictRegexMap(List<NormDictEntry> entries) {
HashMap<NormDictEntry, Pattern> regexMap = new HashMap<>();
for (NormDictEntry entry : entries) {
// make for every NormDictEntry.term a regular expression matching on word boundaries and
// case insensitive. Note: we cannot use \b for the word boundary's term end,
// because of possible trailing punctuation
Pattern regex = Pattern.compile("\\b(?i)" +
Pattern.quote(entry.term.strip().toLowerCase()) + "(?!\\S)");
regexMap.put(entry, regex);
}
return regexMap;
}

/**
Expand Down Expand Up @@ -345,33 +380,39 @@ public LiveData<List<com.grammatek.simaromur.db.Voice>> getAllVoices() {
*
* @return List of all current normalization dictionary entries as LiveData
*/
public LiveData<List<NormDictEntry>> getNormDictEntries() {
return mNormDictDao.getSortedEntries();
}

/**
* Get a LiveData list of all current normalization dictionary entries.
*
* @return List of all current normalization dictionary entries as LiveData
*/
public List<NormDictEntry> getNormDictEntriesDirect() {
return mNormDictDao.getEntries();
public LiveData<List<NormDictEntry>> getUserDictEntries() {
Log.v(LOG_TAG, "getUserDictEntries");
return mAllUserDictEntries;
}

/**
* Creates or updates the given entry inside the Db.
*/
public void createOrUpdateNormDictEntry(NormDictEntry entry) {
public void createOrUpdateUserDictEntry(NormDictEntry entry) {
mNormDictDao.insert(entry);
mUtteranceCacheManager.clearCache();
}


/**
* Deletes the given entry from the Db.
* @param mEntry the entry to be deleted
*/
public void deleteNormDictEntry(NormDictEntry mEntry) {
public void deleteUserDictEntry(NormDictEntry mEntry) {
mNormDictDao.delete(mEntry);
mUtteranceCacheManager.clearCache();
}

/**
* Returns map of all observed/cached user normalization dictionary entries and the corresponding
* compiled term regular expression pattern. If there are any changes in the model, this map
* is updated automatically.
*
* @return map of all cached voices
*/
public final HashMap<NormDictEntry, Pattern> getCachedUserDictEntries() {
Log.v(LOG_TAG, "getCachedUserDictEntries");
return mAllCachedUserDictEntries;
}

/**
Expand Down Expand Up @@ -1160,5 +1201,4 @@ protected Void doInBackground(Void... voids) {
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public NormDictViewModel(Application application) {
// Return all entries
public LiveData<List<NormDictEntry>> getEntries() {
if (mAllEntries == null) {
mAllEntries = mRepository.getNormDictEntries();
mAllEntries = mRepository.getUserDictEntries();
}
return mAllEntries;
}
Expand Down Expand Up @@ -82,11 +82,11 @@ public void stopSpeaking(Voice voice) {

public void createOrUpdate(NormDictEntry mEntry) {
Log.v(LOG_TAG, "update: " + mEntry.term + " -> " + mEntry.replacement);
mRepository.createOrUpdateNormDictEntry(mEntry);
mRepository.createOrUpdateUserDictEntry(mEntry);
}

public void delete(NormDictEntry mEntry) {
Log.v(LOG_TAG, "delete: " + mEntry.term + " -> " + mEntry.replacement);
mRepository.deleteNormDictEntry(mEntry);
mRepository.deleteUserDictEntry(mEntry);
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/com/grammatek/simaromur/Simaromur.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@
public class Simaromur extends Activity {

public Simaromur() {
/**
if(BuildConfig.DEBUG)
StrictMode.enableDefaults();
*/
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy())
.detectLeakedClosableObjects()
.build());
*/
finish();
}
}
9 changes: 5 additions & 4 deletions app/src/main/java/com/grammatek/simaromur/TTSService.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ public class TTSService extends TextToSpeechService {
@Override
public void onCreate() {
Log.i(LOG_TAG, "onCreate()");
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy())
.detectLeakedClosableObjects()
.build());

/**
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy())
.detectLeakedClosableObjects()
.build());
*/
mRepository = App.getAppRepository();
// This calls onIsLanguageAvailable() and must run after Initialization
super.onCreate();
Expand Down
12 changes: 3 additions & 9 deletions app/src/main/java/com/grammatek/simaromur/VoiceViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public class VoiceViewModel extends AndroidViewModel {

// these variables are for data caching
private AppData mAppData; // application data
private LiveData<List<Voice>> mAllVoices; // our current voices model

public VoiceViewModel(Application application) {
super(application);
Expand All @@ -43,18 +42,13 @@ public AppData getAppData() {

// Return all voices
public LiveData<List<Voice>> getAllVoices() {
if (mAllVoices == null) {
mAllVoices = mRepository.getAllVoices();
}
return mAllVoices;
return mRepository.getAllVoices();
}

// Return specific voice
public Voice getVoiceWithId(long voiceId) {
if (mAllVoices == null) {
return null;
}
for(Voice voice: Objects.requireNonNull(mAllVoices.getValue())) {
List<Voice> voices = getAllVoices().getValue();
for(Voice voice: Objects.requireNonNull(voices)) {
if (voice.voiceId == voiceId)
return voice;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,6 @@ public static String buildVoiceKey(String voiceName, String voiceVersion) {
assert(!voiceName.isEmpty());
assert(!voiceVersion.isEmpty());
final String key = voiceName + ":" + voiceVersion;
Log.v(LOG_TAG, "buildVoiceKey(): " + key);
return key;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public Set<String> getNonEndingAbbr() {
}

private Set<String> readAbbrFromFile(int resID) {
// TODO: move to FileUtils
Set<String> abbrSet = new HashSet<>();
Resources res = context.getResources();
String line;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class NormalizationManager {
private static final String POS_MODEL = "pos/is-pos-reduced-maxent.bin";

private final Context mContext;
private final POSTaggerME mPosTagger;
private POSTaggerME mPosTagger;
private final TTSUnicodeNormalizer mUnicodeNormalizer;
private final Tokenizer mTokenizer;
private final TTSNormalizer mTTSNormalizer;
Expand All @@ -39,11 +39,6 @@ public NormalizationManager(Context context, Map<String, PronDictEntry> pronDict
mUnicodeNormalizer = new TTSUnicodeNormalizer(context, pronDict);
mTokenizer = new Tokenizer(context);
mTTSNormalizer = new TTSNormalizer();
mPosTagger = initPOSTagger();
if (mPosTagger == null) {
Log.e(LOG_TAG, "Failed to initialize POS tagger");
throw new RuntimeException("Failed to initialize POS tagger");
}
}

/**
Expand Down Expand Up @@ -113,6 +108,15 @@ private String[] tagText(final String text) {

return tags;
}

if (mPosTagger == null) {
// this takes ~2 seconds
mPosTagger = initPOSTagger();
if (mPosTagger == null) {
Log.e(LOG_TAG, "Failed to initialize POS tagger");
throw new RuntimeException("Failed to initialize POS tagger");
}
}
tags = mPosTagger.tag(tokens);
if (DEBUG) {
printProbabilities(tags, mPosTagger, tokens);
Expand All @@ -127,7 +131,6 @@ private POSTaggerME initPOSTagger() {
InputStream iStream = mContext.getAssets().open(POS_MODEL);
POSModel posModel = new POSModel(iStream);
posTagger = new POSTaggerME(posModel);

} catch(IOException e) {
e.printStackTrace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,13 @@ public class Pronunciation {
}

public Pronunciation(Context context) {
Log.v(LOG_TAG, "Pronunciation() called");
mContext = context;
mPronDict = readPronDict();
mIpaPronDict = readIpaPronDict();
mAlphabets = initializeAlphabets();
// the following members are lazily initialized:
// - mG2P
// - mPronDict
// - mIpaPronDict
}

public String transcribe(String text) {
Expand Down Expand Up @@ -133,14 +136,15 @@ private String transcribeString(String text) {
StringBuilder sb = new StringBuilder();
for (String tok : tokens) {
String transcr = "";
if (mPronDict.containsKey(tok)) {
transcr = mPronDict.get(tok).getTranscript().trim();
Map<String, PronDictEntry> pronDict = GetPronDict();
if (pronDict.containsKey(tok)) {
transcr = pronDict.get(tok).getTranscript().trim();
}
else if (tok.equals(SymbolsLvLIs.TagPause)){
transcr = SymbolsLvLIs.SymbolShortPause;
}
else {
transcr = mG2P.process(tok).trim();
transcr = GetG2p().process(tok).trim();
}

// bug in Thrax grammar, catch the error here: insert space before C if missing
Expand Down Expand Up @@ -182,10 +186,25 @@ private List<String> getValidAlphabets() {
return List.copyOf(mAlphabets.keySet());
}

public void initializeG2P() {
private NativeG2P initializeG2P() {
if (mG2P == null) {
mG2P = new NativeG2P(this.mContext);
}
return mG2P;
}

private Map<String, PronDictEntry> initializePronDict() {
if (mPronDict == null) {
mPronDict = readPronDict();
}
return mPronDict;
}

private Map<String, PronDictEntry> initializeIpaPronDict() {
if (mIpaPronDict == null) {
mIpaPronDict = readIpaPronDict();
}
return mIpaPronDict;
}


Expand Down Expand Up @@ -229,6 +248,7 @@ private Map<String, Map<String, Map<String, String>>> initializeAlphabets() {
}

private Map<String, PronDictEntry> readPronDict() {
Log.v(LOG_TAG, "readPronDict() called");
Map<String, PronDictEntry> pronDict = new HashMap<>();
final List<String> fileContent = FileUtils.readLinesFromResourceFile(this.mContext,
R.raw.ice_pron_dict_standard_clear_2201_extended);
Expand All @@ -242,6 +262,7 @@ private Map<String, PronDictEntry> readPronDict() {
}

private Map<String, PronDictEntry> readIpaPronDict() {
Log.v(LOG_TAG, "readIpaPronDict() called");
Map<String, PronDictEntry> pronDict = new HashMap<>();
final List<String> fileContent = FileUtils.readLinesFromResourceFile(this.mContext,
R.raw.ice_pron_dict_standard_clear_2201_extended);
Expand All @@ -255,14 +276,14 @@ private Map<String, PronDictEntry> readIpaPronDict() {
}

public NativeG2P GetG2p() {
return mG2P;
return initializeG2P();
}

public Map<String, PronDictEntry> GetPronDict() {
return mPronDict;
return initializePronDict();
}
public Map<String, PronDictEntry> GetIpaPronDict() {
return mIpaPronDict;
return initializeIpaPronDict();
}
public Map<String, Map<String, Map<String, String>>> GetAlphabets() {
return mAlphabets;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ public String transcribe(String text) {
* relevant phonemes were found.
*/
public String transcribe(String text, final String voiceType, final String voiceVersion) {
// lazy initialization of g2p
if (mPronounciation.GetG2p() == null)
mPronounciation.initializeG2P();
mG2P = mPronounciation.GetG2p();
assert(mG2P != null);

Expand Down
Loading

0 comments on commit 131289f

Please sign in to comment.