diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-01-23 11:18:45 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-23 11:18:45 -0500 | 
| commit | e999db0f10e16f3e1bf17ff14abd5bbde450b5ff (patch) | |
| tree | 103e49cb2cf8f209c3dbe3cf6835b7206498294d /ext/mixed/js | |
| parent | 349d9a36110976c9da912e432d9828dbf1c8a7cf (diff) | |
More display audio refactoring (#1293)
* Add _getExpressionAndReading utility
* Add _getExpressionReadingKey utility
* Add _getAudioOptions
* Separate some logic into _createAudioFromInfoList
* Update cache data format so that it can be accessed synchronously
* Allow sources to be manually specified
* Add options to enable playback of a specific entry of a source
Diffstat (limited to 'ext/mixed/js')
| -rw-r--r-- | ext/mixed/js/display-audio.js | 119 | 
1 files changed, 94 insertions, 25 deletions
| diff --git a/ext/mixed/js/display-audio.js b/ext/mixed/js/display-audio.js index ea919d14..d4ee754a 100644 --- a/ext/mixed/js/display-audio.js +++ b/ext/mixed/js/display-audio.js @@ -62,8 +62,8 @@ class DisplayAudio {      }      setupEntriesComplete() { -        const {audio} = this._display.getOptions(); -        if (!audio.enabled || !audio.autoPlay) { return; } +        const audioOptions = this._getAudioOptions(); +        if (!audioOptions.enabled || !audioOptions.autoPlay) { return; }          this.clearAutoPlayTimer(); @@ -97,21 +97,22 @@ class DisplayAudio {          this._audioPlaying = null;      } -    async playAudio(definitionIndex, expressionIndex) { +    async playAudio(definitionIndex, expressionIndex, sources=null, sourceDetailsMap=null) {          this.stopAudio();          this.clearAutoPlayTimer(); -        const {definitions} = this._display; -        if (definitionIndex < 0 || definitionIndex >= definitions.length) { return; } - -        const definition = definitions[definitionIndex]; -        if (definition.type === 'kanji') { return; } - -        const {expressions} = definition; -        if (expressionIndex < 0 || expressionIndex >= expressions.length) { return; } +        const expressionReading = this._getExpressionAndReading(definitionIndex, expressionIndex); +        if (expressionReading === null) { return; } -        const {expression, reading} = expressions[expressionIndex]; -        const {sources, textToSpeechVoice, customSourceUrl, volume} = this._display.getOptions().audio; +        const {expression, reading} = expressionReading; +        const audioOptions = this._getAudioOptions(); +        const {textToSpeechVoice, customSourceUrl, volume} = audioOptions; +        if (!Array.isArray(sources)) { +            ({sources} = audioOptions); +        } +        if (!(sourceDetailsMap instanceof Map)) { +            sourceDetailsMap = null; +        }          const progressIndicatorVisible = this._display.progressIndicatorVisible;          const overrideToken = progressIndicatorVisible.setOverride(true); @@ -119,7 +120,7 @@ class DisplayAudio {              // Create audio              let audio;              let title; -            const info = await this._createExpressionAudio(sources, expression, reading, {textToSpeechVoice, customSourceUrl}); +            const info = await this._createExpressionAudio(sources, sourceDetailsMap, expression, reading, {textToSpeechVoice, customSourceUrl});              if (info !== null) {                  let source;                  ({audio, source} = info); @@ -187,8 +188,8 @@ class DisplayAudio {          return results;      } -    async _createExpressionAudio(sources, expression, reading, details) { -        const key = JSON.stringify([expression, reading]); +    async _createExpressionAudio(sources, sourceDetailsMap, expression, reading, details) { +        const key = this._getExpressionReadingKey(expression, reading);          let sourceMap = this._cache.get(key);          if (typeof sourceMap === 'undefined') { @@ -199,33 +200,67 @@ class DisplayAudio {          for (let i = 0, ii = sources.length; i < ii; ++i) {              const source = sources[i]; -            let infoListPromise = sourceMap.get(source); -            if (typeof infoListPromise === 'undefined') { +            let infoListPromise; +            let sourceInfo = sourceMap.get(source); +            if (typeof sourceInfo === 'undefined') {                  infoListPromise = this._getExpressionAudioInfoList(source, expression, reading, details); -                sourceMap.set(source, infoListPromise); +                sourceInfo = {infoListPromise, infoList: null}; +                sourceMap.set(source, sourceInfo); +            } + +            let {infoList} = sourceInfo; +            if (infoList === null) { +                infoList = await infoListPromise; +                sourceInfo.infoList = infoList;              } -            const infoList = await infoListPromise; -            for (let j = 0, jj = infoList.length; j < jj; ++j) { -                const item = infoList[j]; +            let start = 0; +            let end = infoList.length; + +            if (sourceDetailsMap !== null) { +                const sourceDetails = sourceDetailsMap.get(source); +                if (typeof sourceDetails !== 'undefined') { +                    const {start: start2, end: end2} = sourceDetails; +                    if (this._isInteger(start2)) { start = this._clamp(start2, start, end); } +                    if (this._isInteger(end2)) { end = this._clamp(end2, start, end); } +                } +            } + +            const audio = await this._createAudioFromInfoList(source, infoList, start, end); +            if (audio !== null) { return audio; } +        } + +        return null; +    } + +    async _createAudioFromInfoList(source, infoList, start, end) { +        for (let i = start; i < end; ++i) { +            const item = infoList[i]; + +            let {audio, audioResolved} = item; +            if (!audioResolved) {                  let {audioPromise} = item;                  if (audioPromise === null) {                      audioPromise = this._createAudioFromInfo(item.info, source);                      item.audioPromise = audioPromise;                  } -                let audio;                  try {                      audio = await audioPromise;                  } catch (e) {                      continue; +                } finally { +                    item.audioResolved = true;                  } -                return {audio, source, infoListIndex: j}; +                item.audio = audio;              } -        } +            if (audio === null) { continue; } + +            return {audio, source, infoListIndex: i}; +        }          return null;      } @@ -244,4 +279,38 @@ class DisplayAudio {          const infoList = await api.getExpressionAudioInfoList(source, expression, reading, details);          return infoList.map((info) => ({info, audioPromise: null}));      } + +    _getExpressionAndReading(definitionIndex, expressionIndex) { +        const {definitions} = this._display; +        if (definitionIndex < 0 || definitionIndex >= definitions.length) { return null; } + +        const definition = definitions[definitionIndex]; +        if (definition.type === 'kanji') { return null; } + +        const {expressions} = definition; +        if (expressionIndex < 0 || expressionIndex >= expressions.length) { return null; } + +        const {expression, reading} = expressions[expressionIndex]; +        return {expression, reading}; +    } + +    _getExpressionReadingKey(expression, reading) { +        return JSON.stringify([expression, reading]); +    } + +    _getAudioOptions() { +        return this._display.getOptions().audio; +    } + +    _isInteger(value) { +        return ( +            typeof value === 'number' && +            Number.isFinite(value) && +            Math.floor(value) === value +        ); +    } + +    _clamp(value, min, max) { +        return Math.max(min, Math.min(max, value)); +    }  } |