Timing speech text and voice output + set one speech file per character

Started by TheEyess, Tue 21/02/2023 12:53:10

Previous topic - Next topic

TheEyess

Hey there,

I'm trying to add audio files when my characters are speaking and I have two questions regarding this:

1) I'm trying to get one audio file per character to play every time they're speaking. Is there a way to code that once so that I don't have to put in the same audio file manually every time my characters say something?

2) Is there a way to un-link the display duration of the speech text from the length of the audio file? I'm using a few short audio blips for every time my characters talk (as in Stardew Valley for example), so my audio file is pretty short. I noticed that the displayed text disappears as soon as the audio file finished playing, which is way to fast most of the time. Is there a way to make the display duration of the text indepent from the audio playback duration?

I hope I've been clear enough in my explanations. Thank u so much in advance!

Snarky

(1) The answer to this depends heavily on the answer to (2), so let's start there...

(2) No, not using the built-in support for voiced speech. What you will have to do is to play the audio separately from doing the speech:

Code: ags
  aGuybrushSpeechBlips.Play();
  cGuybush.Say("Blah blah blah");
  aGuybrushSpeechBlips.Stop();

The last line ensures that the "speaking" audio is cut off if the player skips the line.
(You can also set the audioclip to loop if you want it to keep going for as long as the speech is displayed.)

(1, again) Given that you need multiple lines of code to get the effect you want, the best way to simplify it is to use a helper function. It's similar to a problem I once gave a very detailed explanation of how to solve, so take a look at that (especially steps 3–7), and I'll just put the solution here:

Code: ags
void SayBlip(this Character*, String message)
{
  int audioId = this.GetProperty("SpeechClipId");
  if(audioId > 0 && audioId < Game.AudioClipCount)
    Game.AudioClips[audioId].Play();
  this.Say(message);
  if(audioId > 0 && audioId < Game.AudioClipCount)
    Game.AudioClips[audioId].Stop();
}

And now you can call it like this:

Code: ags
  cGuybrush.SayBlip("Blah blah blah!");

This requires you to have a Custom Character Property called "SpeechClipId". To set the AudioClip that will play when you call Character.SayBlip(), you would have something like this in game_start():

Code: ags
function game_start()
{
  cGuybrush.SetProperty("SpeechClipId", aGuybrushSpeechBlips.ID);
  cElaine.SetProperty("SpeechClipId", aElaineSpeechBlips.ID);
  // And so on ...
}

TheEyess

Thank u a lot, that sounds exactly like what I need!
I'll have a look at the other thread as soon as I get to it, thank u so much!

TheEyess

Just tested ur suggestions, working just fine! Thank u a lot!

I do have one followup question: Is there be a similar way of dealing with this for dialogues? Since they are based on the regular cCharacter.Say command (if I understood things correctly), the solution above does not apply to them. Would it be possible to modify the standard dialogue system so as to use your SayBlip command and work in the same way?

Snarky

Yes, it's a project setting (introduced in v3.5.0) called something like "Custom Say/Narrate function in dialog scripts."

Khris

There's another way this can be done without a custom speech function:

Code: ags
AudioChannel* blips;

void HandleSpeechBlips() {
  int id = -1;
  for (int i = 0; i < Game.CharacterCount; i++) {
    if (character[i].Speaking) {
      id = i;
      break;
    }
  }
  if (id == -1) {
    if (blips != null && blips.IsPlaying) blips.Stop();
  }
  else {
    AudioClip* ac = aBlip;
    // different clips for certain characters
    if (id == aWizard.ID || id == aWolf.ID) ac = aGravellyBlip;
    if (blips == null || !blips.IsPlaying) blips = ac.Play();
  }
}

Put this code somewhere above repeatedly_execute_always() and HandleSpeechBlips() inside it.

Snarky

@Khris, this will effectively loop the blips, so that they play the whole time the text is displayed (even after the speech animation has ended, depending on the speech-skip setting). From how TheEyess described the problem, I think this is probably not the desired effect.

It would be interesting to create a solution where the number of blips depends on the length of the text. (The typewriter modules can already do this, if having the text appear letter-by-letter is OK.)

TheEyess

Thank u so much, to the two of you! The "custom say" solution works great for my requirements, Snarky!

I do allow myself to ask another question, for the sake of wrapping my head around it and only if u still feel like explaining :)

With "custom say function" activated, everything the characters say is correctly combined with the relevant audio clip, with one exception: the dialog option that is on the left side of the dialog window and not written directly inside the dialog script. I know that u don't necessarily want that to be spoken by the characters and that I can simply copy it into the script so as to make the custom dialog script apply, but I was wondering if there might be another sleek way of doing it with some code.   

As I said, this is just for my understanding and I'm perfectly happy with the workaround that consists of copying the dialog option into the actual script. Again, thank u guys a lot! 

Snarky

Quote from: TheEyess on Wed 22/02/2023 13:02:41With "custom say function" activated, everything the characters say is correctly combined with the relevant audio clip, with one exception: the dialog option that is on the left side of the dialog window and not written directly inside the dialog script. I know that u don't necessarily want that to be spoken by the characters and that I can simply copy it into the script so as to make the custom dialog script apply, but I was wondering if there might be another sleek way of doing it with some code.   

No, I'm afraid that's currently hardcoded to use the standard Say() function. The only way around it is your workaround. See the release notes for v3.5.0.P3, under 'Added "Custom Say/Narrate function in dialog scripts"':

QuoteImportant: this does not work with the "Say" checkbox. The workaround is to duplicate option text as a first cue in the dialog script.

Crimson Wizard

Quote from: Snarky on Wed 22/02/2023 15:31:56No, I'm afraid that's currently hardcoded to use the standard Say() function. The only way around it is your workaround. See the release notes for v3.5.0.P3, under 'Added "Custom Say/Narrate function in dialog scripts"':

QuoteImportant: this does not work with the "Say" checkbox. The workaround is to duplicate option text as a first cue in the dialog script.

Hm, this made me realize that the manual does not have this warning.

SMF spam blocked by CleanTalk