• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
aregee

Trying to reduce the massive amount of variables in my struct

12 posts in this topic

I am adding music formats to my audio player.  This time I am adding support for the ancient Amiga module format that was used in a lot of music trackers under lots of various names.  While I have not added every effect used by the "format" yet, it is playing my old music surprisingly well.

 

My question is about my structs.

 

I have organised the module (the song) in structs in a similar way to this:

typedef struct AmigaModule {
    uint8_t *ModuleName;
    uint8_t SampleCount;
    ModuleSample *SampleList;
    uint8_t SongPositions;
    uint8_t RestartPosition;
    uint8_t PatternTable[128];
    uint8_t MaxPattern;
    uint8_t ChannelCount;
    ModuleChannel *ChannelList;
} AmigaModule;

Where ModuleSample is a struct for each sample, and ModuleChannel is a struct for each channel in the song, with more structs to describe the pattern data for each channel, but this is not really interesting for my question.

 

ModuleChannel that has grown massively big with lots of arbitrary variables for each effect that tracks state of the song while it is playing, and I have made an effort in refactoring my code to a better structure.  Now I have removed all the state variables in its own struct to track states.

 

It looks something like this:

typedef enum ModuleChannelEffect {
    EFFECT_NONE,
    EFFECT_ARPEGGIO,
    EFFECT_PORTAMENTO,
    EFFECT_VIBRATO,
    EFFECT_TREMOLO,
    EFFECT_VOLUME_SLIDE,
    EFFECT_RETRIGGER_SAMPLE
} ModuleChannelEffect;

typedef enum ModuleChannelEffectSpecifier {
    EFFECT_SPECIFIER_NONE,
    EFFECT_SPECIFIER_PORTAMENTO_TO_NOTE,
    EFFECT_SPECIFIER_PORTAMENTO_UP,
    EFFECT_SPECIFIER_PORTAMENTO_DOWN,
    EFFECT_SPECIFIER_VOLUME_SLIDE_UP,
    EFFECT_SPECIFIER_VOLUME_SLIDE_DOWN
    //And more coming later...
} ModuleChannelEffectSpecifier;

typedef struct ModuleEffect {
    ModuleChannelEffect primaryEffect;
    ModuleChannelEffectSpecifier primaryEffectSpecifier;
    uint16_t parameter1;
    uint16_t parameter2;
    uint16_t parameter3;
} ModuleEffect;

And here is my issue:

See the three lines 'parameter1', 'parameter2', 'parameter3'?

 

In code, it looks like this:

currentChannel->effect.parameter1 = periodsTable[translatedPeriod];
currentChannel->effect.parameter2 = periodsTable[arpNoteIndex1];
currentChannel->effect.parameter3 = periodsTable[arpNoteIndex2];

And when I use those values:

currentChannel->period = currentChannel->effect.parameter1;

I like two things:

 

1. Code that is self-explanatory.

2. Generalisation, as long as it is not over-generalisation.

 

My problem is that 'parameter1' doesn't tell me anything, really.  In the portamento effect, is 'parameter1' the speed or the note I am sliding towards?  I could name the values for what they are, but then I wouldn't solve the problem with too many variables inside the struct again, because I would need to make lots of variables I don't always need to use.

 

Then it came to me that I could do something like this:

#define ArpeggioValue1 parameter1
#define ArpeggioValue2 parameter2
#define ArpeggioValue3 parameter3

Now I can reference the variable names in a more sensible and descriptive way:

currentChannel->effect.ArpeggioValue1 = periodsTable[translatedPeriod];
currentChannel->effect.ArpeggioValue2 = periodsTable[arpNoteIndex1];
currentChannel->effect.ArpeggioValue3 = periodsTable[arpNoteIndex2];
And:
 
currentChannel->period = currentChannel->effect.ArpeggioValue1;

My question is whether this is an acceptable coding practise, is there a better way of doing this?  Do you have any other suggestion?  The point here is really that I want to learn to get better.  What I am doing now is working, but how would you go about it?  Make massive objects to handle this?  Some other thing?  What is the "best" way?

 

1

Share this post


Link to post
Share on other sites

Have you thought about using unions, possibly anonymous unions?

Depends on which compiler you are using but I could imagine something like this:

typedef struct ModuleEffect {
    ModuleChannelEffect primaryEffect;
    ModuleChannelEffectSpecifier primaryEffectSpecifier;
    union {
        uint16_t note;
        uint16_t speed;
        uint16_t depth;
        uint16_t something_else;
        uint16_t and_another;
    };
    // ...
} ModuleEffect;


Also I don't think the original format with parameter1, 2, 3 is too bad. After all that's how the file format is laid out. How many hours are you going to spend editing these in code.

Edited by Madhed
2

Share this post


Link to post
Share on other sites

You have two options, first is to create one scruct for each effect, so you can properly name them.

 

The second is, as Madhed suggested, use anonymous unions, but be very careful as you should only use one of them. You could also use named unions, them you will be explicit about which variable you are changing (coding wise, this will be the same as the first option, but it will be more memory efficient, but prone to override errors).

Edited by KnolanCross
0

Share this post


Link to post
Share on other sites

Ah brilliant!  Two very good answers. I never had a look into unions before, I had even forgotten that they exists.  This is exactly what I need.

 

I also like the idea of creating one struct for each effect, but how do I combine them in a "polymorphic" way?  Using unions again?

 

 

Also I don't think the original format with parameter1, 2, 3 is too bad. After all that's how the file format is laid out. How many hours are you going to spend editing these in code.

 

I am not sure I understand the question, but if you mean how many hours I am going to spend making the module player, I have spent about 3 days and it is almost done.  It does play most of what I throw at it, with exception of a few files that the file size does not quite match the position where the loader thinks it should be, and where certain data is illegal or weird values.  But that is normal.  I just need to investigate the cases that I find, and I know there are bugs in a lot of the editors that were used to make these songs, plus some people added custom markers to sync parts of their demos with the music, etc.  I have written everything from scratch, so it is my own code that I am refactoring.  Why I would do that when there are tons of other players out there?  Just because it is fun. ;)  I am listening to the music that it is playing right as we speak.  :)

0

Share this post


Link to post
Share on other sites

Actually it is Objective-C, but most of the code I am writing in this part is C.  I have not set the project file to support C++.

 

Edit:

I am also considering moving fully to C++ instead of using Objective-C, especially now that Apple is moving towards Swift.  Nothing wrong with that, but Objective-C is at least semi-portable.  Swift is far away from that, and I have a feeling that is the point...

Edited by aregee
0

Share this post


Link to post
Share on other sites

Reading about unions makes me a bit uncertain in regards to the undefined behaviour issues, but it seems that for my use, where I am always reading the last value I sat, I should be good.

0

Share this post


Link to post
Share on other sites
Not sure offhand if Objective-C has a more idiomatic solution to this, but here's what I might do in C:

typedef enum {
    PARAMETER_PACK_TYPE_FOO,
    PARAMETER_PACK_TYPE_BAR,
} EParameterPackTypes;

typedef struct {
    EParameterPackTypes PackType;
} ParameterPack;

typedef struct {
    EParameterPackTypes PackType;
    int Something;
    unsigned SomethingElse;
} ParameterPackFoo;

typedef struct {
    EParameterPackTypes PackType;
    float Stuff;
} ParameterPackBar;

typedef struct {
    int CommonFieldOne;
    float CommonFieldTwo;
    ParameterPack * AdditionalParams;
} ThingWithParameters;
Then you can allocate a ParameterPackWhatever to hold the data you need. Since all ParameterPack* structs begin with the enum to tell you their type, you can assert that the package of parameters is correct for the effect (or whatever else).

This is as close to polymorphic objects as C gets (sans lots of horrible magic).
1

Share this post


Link to post
Share on other sites

Reading about unions makes me a bit uncertain in regards to the undefined behaviour issues, but it seems that for my use, where I am always reading the last value I sat, I should be good.

 

There is no "undefined" behavior, is just that you can run into non intuitive errors.

 

For instance, if you use something like this:

struct example_st {
    union {
        struct {
            int volume;
            int pitch;
        };
        struct{
            int atenuation;
            int something;
        };
    };
};

Then you code:

    struct example_st ex;

    ex.volume = 1;
    ex.pitch = 10;

If at some other point of the code you access ex.atenuation, it will have the value of 1. If you change the atenuation value, it will affect the value of volume as well. Also notice that at this example, structs where perfectly aligned, if they were not (specially if there are some floating point variable there), you could run into some really strange values.

0

Share this post


Link to post
Share on other sites

I also like the idea of creating one struct for each effect, but how do I combine them in a "polymorphic" way?  Using unions again?

Just process each effect in its own loop. Chain them together. You can then pipeline them over multiple threads or split them into non-overlapping chunks and run the effect pipeline over each chunk in its own thread.

There's no need for effects to know about each other. If there are some pairs of effects that do need to work together, make that a separate effect all on its own and morph the input data to use that special combined effect instead of the individual effects.
0

Share this post


Link to post
Share on other sites

IIRC it is undefined behavior to write to a union field as one type and then read from it as another.

 

That is exactly what I was thinking about, but I am safe here, since that is not what I will be doing anyway.  I read something about differences in how different fields are padded on different compilers, which probably can be tested with lots of voodoo magic, but that kind of defies the whole usefulness of writing one type and reading another.  One example I saw was to test for endianness, an example that is actually void due to the undefined behaviour.  Edit: It is actually not void, when I think about it, because the array in the example I saw was the same size as the field that was used to test for endianness.

 

KnolanKross, in your example, there won't be any problems, because you are using the same underlying size of the data types.  Had you used uint16_t mixed with uint8_t, it would probably work, but there you *might* stumble across a compiler that pads the field differently, which would lead to undefined behaviour.

 

ApochPiQ, it took a little while to wrap my head around your example, but to be sure I understood:

 

Allocate the ParameterPackWhatever-type I need, cast it to the type ParameterPack, and assign it to the AdditionalParams field in ThingsWithParameters struct.  Since the first field in all the ParameterPackWhatever-structs are EParameterPackTypes, I can now safely check the type before accessing (and casting) to the correct struct type and fetch the fields I need from there?

 

Thank you all!  This is exactly the kind of answers I was hoping for.  You have been a great help, and I have learnt a lot of new stuff!  biggrin.png

Edited by aregee
0

Share this post


Link to post
Share on other sites

 

I also like the idea of creating one struct for each effect, but how do I combine them in a "polymorphic" way?  Using unions again?

Just process each effect in its own loop. Chain them together. You can then pipeline them over multiple threads or split them into non-overlapping chunks and run the effect pipeline over each chunk in its own thread.

There's no need for effects to know about each other. If there are some pairs of effects that do need to work together, make that a separate effect all on its own and morph the input data to use that special combined effect instead of the individual effects.

 

 

I have a feeling that this is an interesting idea, but I am not quite sure I understand exactly what you mean. To me it sounds like you are suggesting to process several effects at once.  In that case, the music format is pretty primitive.  Only one effect is run at a time.  If a new effect comes on a channel, it breaks the other effect.  It is essentially a state machine, with exception of a few that might be combined with some volume adjustments that I will make a secondary effect field for.  BUT -- you gave me an idea I will have a look into: maybe process each channel on separate threads, but that probably depends on how much data I am processing for each request to fill the playback buffer.  I have chosen to have a relatively small buffer to reduce latency, and I am only using 1% of the cpu at all times, opposite of my FLAC part of the audio player that lies around 6%.

 

I have used threading in my app, though.  I have long had a problem with the beginning of the song not playing occasionally.  It plays, though, but with no sound.  I had no idea what caused it, and I was fairly sure it was not a bug from my side.  (Aren't we all? ;) )  I was going to make a post about it here to ask for help, when I wrote about that it, it came to me: it seemed like the OS introduced a lag in the system because of my hard drives were spinning up or something, causing a buffer underrun.  AudioQueues has a bad habit of playing silently for a while after being subjected to a severe buffer underrun situation.  But I have an SSD disk...  Then it came to me that I also have three USB disks connected, and I now had a hunch. 

 

To solve the problem, I tried to let the current song continue to play, while I was loading the next in the background.  If it took more than half a second, I would also display an indeterminate progress bar until the task told the player that it was ready to play the next song.  That solved my problem.  The indeterminate progress bar thing was also a really good psychological help in that you would see that the app was indeed working on something and not just stalling for no reason.  I just cancelled that post.

 

OS X has a bad habit of stalling the system on disk access when you have connected USB drives that need to spin up, even when you are not accessing the drives at all.

 

Edit: pardon my messy post, but it is bed time now and I am just rambling now.  :)

Edited by aregee
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0