Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Functions to Voice. #32

Closed
odysseywestra opened this issue Sep 9, 2012 · 13 comments
Closed

Add Functions to Voice. #32

odysseywestra opened this issue Sep 9, 2012 · 13 comments

Comments

@odysseywestra
Copy link
Contributor

odysseywestra commented Sep 9, 2012

Alright I've come up with an Idea on how to add functions to voice, like switching on and off individual characters and having the developer set a default voice directory for their game. I like some feed back before I continue this...

NEW VOICE PARAMETERS

Usuage: voice(tag_name, file, **kwargs)

This extra contains the new implementation of voice support. It can
be toggle on and off as a whole, but now has support to toggle off
and on for individual characters in the Preference Screen. Plus
once you set the config.voice_dir you can place the voice files in
another folder without haveing to type or retype the path/to/file
every time you want voice in the game.

To use it, place a voice tag_name sndfile line before
each line of dialogue. Please note that the tag_name is an
optional argument, but in order to switch off an on voice for
individual characters a character name must be place there.

  voice "e_1002.ogg"
  e "Voice support lets you add spoken word to your games."

Normally, a voice is cancelled at the start of the next interaction.
If you want a voice to span interactions, call the
voice_sustains

  voice "e_1002.ogg"
  e "Voice sustain is a technique that allows the same voice #        file..."

  voice sustain
  e "...to play through the two lines of dialogue"

Now to in order to toggle on and off the voice for each individual
character, you to set the has_voice in the Character() to
true cause by default has_voice is set to false.

   difine e = Character("Elieen", has_voice=True)

When has_voice is set to true the Character() name variable
will be added to the _voice.list variable as a string with a
list of other Character() that has the has_voice set to
true.

  _voice.list = [("Elieen", "True"),("Darin", "True")]

Now to make sure that renpy will not re-add these characters to the
_voice.list variable it will be set as a persistent variable and
will be automatically loaded next time the game is started again.

On the Preference Screen developer would set an
action button(s) that would utilize the _voice.list variable
of characters. When you click on the characters name it will darken
indicating the character's voice is off or normal color indicating
the character's voice is on.

Alright to utilize the voice toggle in the game script, the
developer needs to place the character's name or substitute name from
in the define argument in to the tag_name from voice.

  voice "Elieen" "e_1002.ogg"
  e "Now since \"Elieen\" is added to voice...
   ..My voice can now be toggle on or off by the user."

Now and example with the substitute name from define argument.

  voice "e" "e_1002.ogg"
  e "Even though \"e\" is used instead of \"Elieen\...
   ...My voice can still be toggle on or off by the user."

Now the one last new thing about voice is that if you set the
config.voice_dir in the config.rpy to the directory of the
voice files. Once that is set, you wouldn't have to type or retype
the path/to/file every time you want voice in the game.

  config.voice_dir = "Path/To/Voice/Dir"

Please not that config.voice_dir default path will be the game
base which is when it is set to "None"

@renpytom
Copy link
Member

renpytom commented Sep 9, 2012

I'm thinking that this is a good idea, but perhaps a bit over-complicated, for what we need. Let's talk about requirements, and then we can talk about what it would take to establish those requirements.

So the main requirement is that we want to be able to tag voices with character names, so that we can toggle voices on and off by character.

I think you're on the right track with adding a tag parameter to voice, but for backwards compatibility's sake, it has to be a keyword argument that defaults to None. So the voice function should probably have the signature:

def voice(filename, tag=None):
    """
    :doc: voice

    Plays `filename` on the voice channel.

    `filename`
        The filename to play. This is formatted by
        :var:`config.voice_filename_format` variable is used to format
        this parameter.

    `tag`
        If this is not None, it should be a string giving a voice tag
        to be played. If None, this takes its default value from the
        voice_tag of the Character that causes the next interaction.

        The voice tag is used to specify which character is speaking,
        to allow a user to mute or unmute the voices of particular
        characters.
    """

The voice statement should also take an optional voice tag. I think we should make it a name, rather than a string, so that the user can write:

voice eileen "1002.ogg"

I think characters should take a voice tag, but in a somewhat different way. Basically, if a character has a voice tag, the value of that tag should be stored in a variable that the voice_interact function can access. So the code:

define e = Eileen("Eileen", voice_tag="eileen")

voice "0001.ogg"
e "Hello, world."

should be equivalent to

define e = Eileen("Eileen")

voice eileen "0001.ogg"
e "Hello, world."

Finally, I'd suggest that instead of voice_dir, we have config.voice_filename_format, which would be formatted with {filename} and {tag}. (There should be a 'default' tag, probably.) This would let us write:

init python:
    config.voice_filename_format = "voice/{tag}/{filename}.ogg"

Which would then let us have:

voice eileen "0001"

play the file named voice/eileen/0001.ogg .

What do you think of this variant?

@odysseywestra
Copy link
Contributor Author

odysseywestra commented Sep 10, 2012

Alright I see the logic in that. I really like your idea about the config.voice_filename_format idea. It would also be a great Idea to add these filename_formats options in other places like images, music and sounds as well. Now With the file format, would you would want the default be "voice/{tag}/{filename}.ogg" or keep the default at the game base with the option of them setting it themselves in the config.rpy? I'm asking this cause someone is going to wonder why their mp3 file won't work with this filename format. I would make the default at the game base with the option of the developer changing this config function personally.

Now, back to the voice tagging system, I see how you going about this. So with voice_tag in the Character() funtion, Renpy would add the voice_tag in the voice_list variable in the first init run (which would become a persistent variable so less loading time). Then a user could toggle off that character in the Preference Screen which would cause the tag_name be set to false. When RenPy come to a voice() function it would read the dialog() name thats under it and compare it to the list of name_tags that it got from the Character() function. If that tag_name is set to false it would send the signal to the voice_interact function and skip it. Correct?

Another option for the voice_list variable, would you want it to add voices as the game goes, or just add it on the first init and keep the voice_list variable persistent. Later on after getting this part working I want make it so that it would mask the voice name till a user would trip flag and the name would reveal itself later in the game, but that it in the future idea after we get the base function put in.

Edit: With the last Idea, that should be a screen option not a renpy system option.

@renpytom
Copy link
Member

renpytom commented Sep 11, 2012

The default voice_filename_format has to be {filename}, for backwards compatibility.

I don't see voice_list the way you see it, as something that needs to be updated. Instead, what we'll have is persistent._voice_mute, which should be a dict mapping a voice tag to a "mute" state. If the voice tag is not present, or maps to False, the voice is played - otherwise, it's muted.

Finally, we should have a pair of actions that update persistent._voice_mute. SetVoiceMute(voice_tag, mute) allows us to set the voice mute of a channel, while ToggleVoiceMute(voice_tag, invert_select=False) allows us to create a button that toggles mute. (ToggleVoiceMute should be selected when the channel is muted - but invert_select should invert it.)

@odysseywestra
Copy link
Contributor Author

odysseywestra commented Sep 12, 2012

Alright, I'm getting what you meaning slowly. So in other words when a user uses

voice(filename, tag=None)

The filename is the path of the file, but can be change with the

config.voice_filename_format

Which by default is set to "{filename}" but can be change to "voice/{tag}/{filename}.ogg" if the user wishes to.

If the tag is not set to "None" it would be the tag that the user desires like

vocie("0001.ogg", tag="eileen")

or like in the voice statement which would use a name instead of a string

voice eileen "0001.ogg"

Now if the "tag" is set to "None", it will used the "voice_tag" from the Character() function

define e = Character('Eileen', voice_tag = 'eileen')

voice "0001.ogg"
e "Hurrahh, I have a voice!!!"

Which comes to a question...how would renpy know to uses the 'eileen' voice tag if the say statement is below that has the character's voice_tag? Would it read the next line in the background? Then if the Character() has a voice_tag, renpy use it to play the voice file?

Now moving right along assuming renpy knows what it doing, to toggle individual character's voice on and off.

On the preference screen when a user selects a voice button it would activate the action

SetVoiceMute('eileen' , mute)

Which then would add the voice_tag to the persistent._voice_mute as a dict. If renpy reads the voice() function and activate voice_interact(), voice_interact would see that the voice_tag 'eileen' is in a muted state it will basically skip it.

Now if the user click the button again to unmute it, it would activate the

ToggleVoiceMute('eileen', invert_select=False)

Thus remove the voice_tag from the persistent._voice_mute dict and once again when renpy voice() activate voice_interact and voice_interacts see that the voice_tag map to false, it would play the voice file in the 'voice' channel.

Please correct if I'm wrong. Once we agree on a certain method to accomplish this, I'll create a roadmap txt file stating what I am doing and follow that roadmap till the voice_enhancement project is finish. I'm a bit new to python so I may ask for help along the way, but I want to learn more about python and this is a way to do it. By just doing it. ^^

Btw sorry for the weird interaction with git's comment system. When previewing I pressed the spacebar a few times and it posted all four of them. And then again when in the comment preveiw i pressed tab then space which cause it to close the issue and post the comment. Gotta love user Error.....^^'

@renpytom
Copy link
Member

renpytom commented Sep 12, 2012

Basically correct. (I suggest we use voice_tag to refer to voice tags exclusively.)

"how would renpy know to uses the 'eileen' voice tag if the say statement is below that has the character's voice_tag?"

take a look at common/00voice.rpy to see how it works. Basically, all voice() does is to store information in the _voice object. The voice_interact function - which is called automatically at the start of each interaction - takes care of playing the voice.

I think you're confusing how to use SetVoiceMute and ToggleVoiceMute. Basically, we'd like to support code like:

vbox:
    label "Eileen's Voice"
    textbutton "Not Muted" action SetVoiceMute("eileen", False)
    textbutton "Muted" action ToggleVoiceMute("eileen", True)
    textbutton "Toggle Mute" action  ToggleVoiceMute("eileen")

@odysseywestra
Copy link
Contributor Author

odysseywestra commented Sep 13, 2012

I get it now. Since voice_interact is called upon each interaction, then when it comes across a say statement, it would read the Character()'s name and then pull up the voice_tag from it and if the voice_tag is not in the persistent._voice_mute, Renpy will play the voice file that is according to the voice() function. If that is the logic, let me know cause I want to get this README stating what I'm doing, done. Plus I'll be adding the comments to 'common/00voice.rpy' so that I would know where to add the lines needed. I'm learning so I need to have a roadmap to work with.

@odysseywestra
Copy link
Contributor Author

odysseywestra commented Sep 14, 2012

Btw how would I add the voice_tag to the Charater() function? I know it's in renpy/character.py but I don't see a way to do so.

@renpytom
Copy link
Member

renpytom commented Sep 15, 2012

I think it's probably best if I add it for you. The Character code is opaque, to say the least. But if you can handle the 00voice.rpy portion of this, we can meet in the middle.

@odysseywestra
Copy link
Contributor Author

odysseywestra commented Sep 16, 2012

Okay, so do you want me to add you as a collaborator to my branch then?

@renpytom
Copy link
Member

renpytom commented Sep 17, 2012

Sure, add me as a collaborator.

@renpytom
Copy link
Member

renpytom commented Sep 17, 2012

I've added a voice_tag parameter to Character, and a corresponding config.voice_tag_callback config variable, and checked the result into your branch.

So you should be good to go.

The way you're initializing the voice mute dict seems wrong. First of all, it should be persistent._voice_mute - the leading underscore makes it a Ren'Py-internal name. Secondly, you only want to initialize it if it's None - your current code does so unconditionally.

Also - why don't we make it a set? That seems to match what we're doing with it better than a dict.

So the final initialization should look like:

    if persistent._voice_mute is None:
        persistent._voice_mute = set()

@odysseywestra
Copy link
Contributor Author

odysseywestra commented Sep 18, 2012

Okay, after reading what set() dose in python docs, and testing and playing around with in python, I think it's a good idea. I'll go ahead and add that Idea into the voice_enhancement branch. Btw while working on finishing 00voice.rpy, and add the two actions in 00screen.rpy, could you put a post on the forum asking for voice samples? I want to add the changes to the RenPy' tutorial to showcase voice so they know it's there and show examples on how to implement it. I'll leave it up to you as to what they say.

@renpytom
Copy link
Member

renpytom commented Feb 12, 2013

I took your branch, merged it, cleaned it up, and added the remaining features.

It'll be released with 6.15.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants