Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
16 / 16 |
|
100.00% |
2 / 2 |
CRAP | |
100.00% |
1 / 1 |
LarynxEngine | |
100.00% |
16 / 16 |
|
100.00% |
2 / 2 |
7 | |
100.00% |
1 / 1 |
register | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAudioData | n/a |
0 / 0 |
n/a |
0 / 0 |
4 | |||||
getSsml | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Phonos\Engine; |
4 | |
5 | use DOMDocument; |
6 | use MediaWiki\Extension\Phonos\Exception\PhonosException; |
7 | |
8 | class LarynxEngine extends Engine { |
9 | |
10 | protected string $apiEndpoint; |
11 | |
12 | protected function register(): void { |
13 | $this->apiEndpoint = $this->config->get( 'PhonosApiEndpointLarynx' ); |
14 | } |
15 | |
16 | /** |
17 | * @inheritDoc |
18 | * @codeCoverageIgnore |
19 | * @throws PhonosException |
20 | */ |
21 | public function getAudioData( AudioParams $params ): string { |
22 | $persistedAudio = $this->getPersistedAudio( $params ); |
23 | if ( $persistedAudio ) { |
24 | return $persistedAudio; |
25 | } |
26 | |
27 | $ssml = trim( $this->getSsml( $params ) ); |
28 | $url = $this->apiEndpoint . '?' . http_build_query( [ |
29 | 'ssml' => true, |
30 | // TODO: should the voice be configurable, too? |
31 | 'voice' => 'en-us/blizzard_lessac-glow_tts', |
32 | 'text' => $ssml, |
33 | ] ); |
34 | $options = [ |
35 | 'method' => 'GET' |
36 | ]; |
37 | |
38 | if ( $this->apiProxy ) { |
39 | $options['proxy'] = $this->apiProxy; |
40 | } |
41 | |
42 | $request = $this->requestFactory->create( |
43 | $url, |
44 | $options, |
45 | __METHOD__ |
46 | ); |
47 | $status = $request->execute(); |
48 | |
49 | if ( !$status->isOK() ) { |
50 | $error = $status->getMessage()->text(); |
51 | throw new PhonosException( 'phonos-engine-error', [ 'Larynx', $error ] ); |
52 | } |
53 | |
54 | $out = $this->convertWavToMp3( $request->getContent() ); |
55 | $this->persistAudio( $params, $out ); |
56 | |
57 | return $out; |
58 | } |
59 | |
60 | /** |
61 | * @inheritDoc |
62 | */ |
63 | public function getSsml( AudioParams $params ): string { |
64 | $ipa = trim( $params->getIpa() ); |
65 | $text = $params->getText() ?: $ipa; |
66 | |
67 | $ssmlDoc = new DOMDocument( '1.0' ); |
68 | |
69 | $speakNode = $ssmlDoc->createElement( 'speak' ); |
70 | $speakNode->setAttribute( 'xmlns', 'http://www.w3.org/2001/10/synthesis' ); |
71 | $speakNode->setAttribute( 'version', '1.1' ); |
72 | $speakNode->setAttribute( 'xml:lang', $params->getLang() ); |
73 | $ssmlDoc->appendChild( $speakNode ); |
74 | |
75 | /** |
76 | * Adds the following to the <speak> node: |
77 | * <phoneme alphabet="ipa" ph={$ipa}> |
78 | * <w>{$text} ?: {$ipa}</w> |
79 | * </phoneme> |
80 | */ |
81 | |
82 | $phonemeNode = $ssmlDoc->createElement( 'phoneme' ); |
83 | $phonemeNode->setAttribute( 'alphabet', 'ipa' ); |
84 | $phonemeNode->setAttribute( 'ph', $ipa ); |
85 | $wNode = $ssmlDoc->createElement( 'w', $text ); |
86 | $phonemeNode->appendChild( $wNode ); |
87 | $speakNode->appendChild( $phonemeNode ); |
88 | |
89 | return $ssmlDoc->saveXML(); |
90 | } |
91 | } |