Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
65 changes: 61 additions & 4 deletions includes/Classifai/Providers/OpenAI/TextToSpeech.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,33 @@ public function get_model(): string {
return apply_filters( 'classifai_openai_text_to_speech_model', $model );
}

/**
* Get the instructions for voice control.
*
* @param string $instructions The instructions to use. If empty, the instructions from the settings will be used.
* @return string
*/
public function get_instructions( string $instructions = '' ): string {
if ( empty( $instructions ) ) {
$settings = $this->feature_instance->get_settings();
$instructions = $settings[ static::ID ]['instructions'] ?? '';
}

/**
* Filter the instructions for voice control.
*
* Useful if you want to modify the instructions for certain use cases.
*
* @since x.x.x
* @hook classifai_openai_text_to_speech_instructions
*
* @param string $instructions The current instructions to use.
*
* @return string The instructions to use.
*/
return apply_filters( 'classifai_openai_text_to_speech_instructions', $instructions );
}

/**
* Register settings for the provider.
*/
Expand Down Expand Up @@ -234,10 +261,11 @@ public function get_default_provider_settings(): array {
return array_merge(
$common_settings,
[
'tts_model' => 'gpt-4o-mini-tts',
'voice' => 'alloy',
'format' => 'mp3',
'speed' => 1,
'tts_model' => 'gpt-4o-mini-tts',
'voice' => 'alloy',
'format' => 'mp3',
'speed' => 1,
'instructions' => '',
]
);
}
Expand Down Expand Up @@ -275,6 +303,9 @@ public function sanitize_settings( array $new_settings ): array {
if ( 0.25 <= $speed || 4.00 >= $speed ) {
$new_settings[ static::ID ]['speed'] = sanitize_text_field( $new_settings[ static::ID ]['speed'] );
}

// Sanitize instructions field.
$new_settings[ static::ID ]['instructions'] = sanitize_textarea_field( $new_settings[ static::ID ]['instructions'] ?? '' );
}

return $new_settings;
Expand Down Expand Up @@ -362,6 +393,31 @@ public function synthesize_speech( int $post_id ) {
'input' => $post_content,
);

// Add instructions if provided.
$instructions = $this->get_instructions( $settings[ static::ID ]['instructions'] ?? '' );
if ( ! empty( $instructions ) ) {
$request_body['instructions'] = $instructions;
}

/**
* Filter the request body before sending to OpenAI.
*
* @since x.x.x
* @hook classifai_openai_text_to_speech_request_body
*
* @param array $request_body The request body that will be sent to OpenAI.
* @param int $post_id Post ID.
* @param string $post_content Post content.
*
* @return array The filtered request body.
*/
$request_body = apply_filters(
'classifai_openai_text_to_speech_request_body',
$request_body,
$post_id,
$post_content
);

$response = $request->post(
$this->get_api_url(),
[
Expand Down Expand Up @@ -397,6 +453,7 @@ public function get_debug_information(): array {
$debug_info[ __( 'Model', 'classifai' ) ] = $provider_settings['tts_model'] ?? '';
$debug_info[ __( 'Voice', 'classifai' ) ] = $provider_settings['voice'] ?? '';
$debug_info[ __( 'Audio format', 'classifai' ) ] = $provider_settings['format'] ?? '';
$debug_info[ __( 'Instructions', 'classifai' ) ] = $provider_settings['instructions'] ?? '';

// We don't save the response transient because WP does not support serialized binary data to be inserted to the options.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import {
__experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis
SelectControl,
TextareaControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';

Expand Down Expand Up @@ -84,6 +85,7 @@ export const OpenAITextToSpeechSettings = ( { isConfigured = false } ) => {
},
] }
__nextHasNoMarginBottom
__next40pxDefaultSize
/>
</SettingsRow>
<SettingsRow
Expand Down Expand Up @@ -145,6 +147,36 @@ export const OpenAITextToSpeechSettings = ( { isConfigured = false } ) => {
},
] }
__nextHasNoMarginBottom
__next40pxDefaultSize
/>
</SettingsRow>
<SettingsRow
label={ __( 'Voice instructions', 'classifai' ) }
description={
<>
{ __(
'Optional instructions to control the voice characteristics of the generated audio.',
'classifai'
) }{ ' ' }
{ __(
'For example: "Speak in a calm, professional tone" or "Use a more energetic delivery".',
'classifai'
) }
</>
}
>
<TextareaControl
id={ `${ providerName }_instructions` }
onChange={ ( value ) =>
onChange( { instructions: value } )
}
value={ providerSettings.instructions || '' }
rows={ 3 }
placeholder={ __(
'Enter instructions to control voice characteristics…',
'classifai'
) }
__nextHasNoMarginBottom
/>
</SettingsRow>
<SettingsRow
Expand All @@ -169,6 +201,7 @@ export const OpenAITextToSpeechSettings = ( { isConfigured = false } ) => {
},
] }
__nextHasNoMarginBottom
__next40pxDefaultSize
/>
</SettingsRow>
<SettingsRow
Expand All @@ -186,6 +219,7 @@ export const OpenAITextToSpeechSettings = ( { isConfigured = false } ) => {
step="0.25"
min="0.25"
max="4"
__next40pxDefaultSize
/>
</SettingsRow>
</>
Expand Down