Edit

kc3-lang/SDL/Xcode-iOS/Demos/src/mixer.c

Branch :

  • Show log

    Commit

  • Author : Sylvain
    Date : 2021-11-22 17:00:48
    Hash : c0029f01
    Message : remove 'free' in comment

  • Xcode-iOS/Demos/src/mixer.c
  • /*
     *  mixer.c
     *  written by Holmes Futrell
     *  use however you want
     */
    
    #include "SDL.h"
    #include "common.h"
    
    #define NUM_CHANNELS 8          /* max number of sounds we can play at once */
    #define NUM_DRUMS 4             /* number of drums in our set */
    
    static struct
    {
        SDL_Rect rect;              /* where the button is drawn */
        SDL_Color upColor;          /* color when button is not active */
        SDL_Color downColor;        /* color when button is active */
        int isPressed;              /* is the button being pressed ? */
        int touchIndex;             /* what mouse (touch) index pressed the button ? */
    } buttons[NUM_DRUMS];
    
    struct sound
    {
        Uint8 *buffer;              /* audio buffer for sound file */
        Uint32 length;              /* length of the buffer (in bytes) */
    };
    
    /* this array holds the audio for the drum noises */
    static struct sound drums[NUM_DRUMS];
    
    /* function declarations */
    void handleMouseButtonDown(SDL_Event * event);
    void handleMouseButtonUp(SDL_Event * event);
    int playSound(struct sound *);
    void initializeButtons(SDL_Renderer *);
    void audioCallback(void *userdata, Uint8 * stream, int len);
    void loadSound(const char *file, struct sound *s);
    
    struct
    {
        /* channel array holds information about currently playing sounds */
        struct
        {
            Uint8 *position;        /* what is the current position in the buffer of this sound ? */
            Uint32 remaining;       /* how many bytes remaining before we're done playing the sound ? */
            Uint32 timestamp;       /* when did this sound start playing ? */
        } channels[NUM_CHANNELS];
        SDL_AudioSpec outputSpec;   /* what audio format are we using for output? */
        int numSoundsPlaying;       /* how many sounds are currently playing */
    } mixer;
    
    /* sets up the buttons (color, position, state) */
    void
    initializeButtons(SDL_Renderer *renderer)
    {
        int i;
        int spacing = 10;           /* gap between drum buttons */
        SDL_Rect buttonRect;        /* keeps track of where to position drum */
        SDL_Color upColor = { 86, 86, 140, 255 };   /* color of drum when not pressed */
        SDL_Color downColor = { 191, 191, 221, 255 };       /* color of drum when pressed */
        int renderW, renderH;
    
        SDL_RenderGetLogicalSize(renderer, &renderW, &renderH);
    
        buttonRect.x = spacing;
        buttonRect.y = spacing;
        buttonRect.w = renderW - 2 * spacing;
        buttonRect.h = (renderH - (NUM_DRUMS + 1) * spacing) / NUM_DRUMS;
    
        /* setup each button */
        for (i = 0; i < NUM_DRUMS; i++) {
    
            buttons[i].rect = buttonRect;
            buttons[i].isPressed = 0;
            buttons[i].upColor = upColor;
            buttons[i].downColor = downColor;
    
            buttonRect.y += spacing + buttonRect.h; /* setup y coordinate for next drum */
    
        }
    }
    
    /*
     loads a wav file (stored in 'file'), converts it to the mixer's output format,
     and stores the resulting buffer and length in the sound structure
     */
    void
    loadSound(const char *file, struct sound *s)
    {
        SDL_AudioSpec spec;         /* the audio format of the .wav file */
        SDL_AudioCVT cvt;           /* used to convert .wav to output format when formats differ */
        int result;
        if (SDL_LoadWAV(file, &spec, &s->buffer, &s->length) == NULL) {
            fatalError("could not load .wav");
        }
        /* build the audio converter */
        result = SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq,
                                   mixer.outputSpec.format,
                                   mixer.outputSpec.channels,
                                   mixer.outputSpec.freq);
        if (result == -1) {
            fatalError("could not build audio CVT");
        } else if (result != 0) {
            /*
               this happens when the .wav format differs from the output format.
               we convert the .wav buffer here
             */
            cvt.buf = (Uint8 *) SDL_malloc(s->length * cvt.len_mult);       /* allocate conversion buffer */
            cvt.len = s->length;    /* set conversion buffer length */
            SDL_memcpy(cvt.buf, s->buffer, s->length);      /* copy sound to conversion buffer */
            if (SDL_ConvertAudio(&cvt) == -1) {     /* convert the sound */
                fatalError("could not convert .wav");
            }
            SDL_free(s->buffer);    /* Free the original (unconverted) buffer */
            s->buffer = cvt.buf;    /* point sound buffer to converted buffer */
            s->length = cvt.len_cvt;        /* set sound buffer's new length */
        }
    }
    
    /* called from main event loop */
    void
    handleMouseButtonDown(SDL_Event * event)
    {
    
        int x, y, mouseIndex, i, drumIndex;
    
        mouseIndex = 0;
        drumIndex = -1;
    
        SDL_GetMouseState(&x, &y);
        /* check if we hit any of the drum buttons */
        for (i = 0; i < NUM_DRUMS; i++) {
            if (x >= buttons[i].rect.x
                && x < buttons[i].rect.x + buttons[i].rect.w
                && y >= buttons[i].rect.y
                && y < buttons[i].rect.y + buttons[i].rect.h) {
                drumIndex = i;
                break;
            }
        }
        if (drumIndex != -1) {
            /* if we hit a button */
            buttons[drumIndex].touchIndex = mouseIndex;
            buttons[drumIndex].isPressed = 1;
            playSound(&drums[drumIndex]);
        }
    
    }
    
    /* called from main event loop */
    void
    handleMouseButtonUp(SDL_Event * event)
    {
        int i;
        int mouseIndex = 0;
        /* check if this should cause any of the buttons to become unpressed */
        for (i = 0; i < NUM_DRUMS; i++) {
            if (buttons[i].touchIndex == mouseIndex) {
                buttons[i].isPressed = 0;
            }
        }
    }
    
    /* draws buttons to screen */
    void
    render(SDL_Renderer *renderer)
    {
        int i;
        SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
        SDL_RenderClear(renderer);       /* draw background (gray) */
        /* draw the drum buttons */
        for (i = 0; i < NUM_DRUMS; i++) {
            SDL_Color color =
                buttons[i].isPressed ? buttons[i].downColor : buttons[i].upColor;
            SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
            SDL_RenderFillRect(renderer, &buttons[i].rect);
        }
        /* update the screen */
        SDL_RenderPresent(renderer);
    }
    
    /*
        finds a sound channel in the mixer for a sound
        and sets it up to start playing
    */
    int
    playSound(struct sound *s)
    {
        /*
           find an empty channel to play on.
           if no channel is available, use oldest channel
         */
        int i;
        int selected_channel = -1;
        int oldest_channel = 0;
    
        if (mixer.numSoundsPlaying == 0) {
            /* we're playing a sound now, so start audio callback back up */
            SDL_PauseAudio(0);
        }
    
        /* find a sound channel to play the sound on */
        for (i = 0; i < NUM_CHANNELS; i++) {
            if (mixer.channels[i].position == NULL) {
                /* if no sound on this channel, select it */
                selected_channel = i;
                break;
            }
            /* if this channel's sound is older than the oldest so far, set it to oldest */
            if (mixer.channels[i].timestamp <
                mixer.channels[oldest_channel].timestamp)
                oldest_channel = i;
        }
    
        /* no empty channels, take the oldest one */
        if (selected_channel == -1)
            selected_channel = oldest_channel;
        else
            mixer.numSoundsPlaying++;
    
        /* point channel data to wav data */
        mixer.channels[selected_channel].position = s->buffer;
        mixer.channels[selected_channel].remaining = s->length;
        mixer.channels[selected_channel].timestamp = SDL_GetTicks();
    
        return selected_channel;
    }
    
    /*
        Called from SDL's audio system.  Supplies sound input with data by mixing together all
        currently playing sound effects.
    */
    void
    audioCallback(void *userdata, Uint8 * stream, int len)
    {
        int i;
        int copy_amt;
        SDL_memset(stream, mixer.outputSpec.silence, len);  /* initialize buffer to silence */
        /* for each channel, mix in whatever is playing on that channel */
        for (i = 0; i < NUM_CHANNELS; i++) {
            if (mixer.channels[i].position == NULL) {
                /* if no sound is playing on this channel */
                continue;           /* nothing to do for this channel */
            }
    
            /* copy len bytes to the buffer, unless we have fewer than len bytes remaining */
            copy_amt =
                mixer.channels[i].remaining <
                len ? mixer.channels[i].remaining : len;
    
            /* mix this sound effect with the output */
            SDL_MixAudioFormat(stream, mixer.channels[i].position,
                               mixer.outputSpec.format, copy_amt, SDL_MIX_MAXVOLUME);
    
            /* update buffer position in sound effect and the number of bytes left */
            mixer.channels[i].position += copy_amt;
            mixer.channels[i].remaining -= copy_amt;
    
            /* did we finish playing the sound effect ? */
            if (mixer.channels[i].remaining == 0) {
                mixer.channels[i].position = NULL;  /* indicates no sound playing on channel anymore */
                mixer.numSoundsPlaying--;
                if (mixer.numSoundsPlaying == 0) {
                    /* if no sounds left playing, pause audio callback */
                    SDL_PauseAudio(1);
                }
            }
        }
    }
    
    int
    main(int argc, char *argv[])
    {
        int done;                   /* has user tried to quit ? */
        SDL_Window *window;         /* main window */
        SDL_Renderer *renderer;
        SDL_Event event;
        int i;
        int width;
        int height;
    
        if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
            fatalError("could not initialize SDL");
        }
        window = SDL_CreateWindow(NULL, 0, 0, 320, 480, SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI);
        renderer = SDL_CreateRenderer(window, 0, 0);
    
        SDL_GetWindowSize(window, &width, &height);
        SDL_RenderSetLogicalSize(renderer, width, height);
    
        /* initialize the mixer */
        SDL_memset(&mixer, 0, sizeof(mixer));
        /* setup output format */
        mixer.outputSpec.freq = 44100;
        mixer.outputSpec.format = AUDIO_S16LSB;
        mixer.outputSpec.channels = 2;
        mixer.outputSpec.samples = 256;
        mixer.outputSpec.callback = audioCallback;
        mixer.outputSpec.userdata = NULL;
    
        /* open audio for output */
        if (SDL_OpenAudio(&mixer.outputSpec, NULL) != 0) {
            fatalError("Opening audio failed");
        }
    
        /* load our drum noises */
        loadSound("ds_kick_big_amb.wav", &drums[3]);
        loadSound("ds_brush_snare.wav", &drums[2]);
        loadSound("ds_loose_skin_mute.wav", &drums[1]);
        loadSound("ds_china.wav", &drums[0]);
    
        /* setup positions, colors, and state of buttons */
        initializeButtons(renderer);
    
        /* enter main loop */
        done = 0;
        while (!done) {
            while (SDL_PollEvent(&event)) {
                switch (event.type) {
                case SDL_MOUSEBUTTONDOWN:
                    handleMouseButtonDown(&event);
                    break;
                case SDL_MOUSEBUTTONUP:
                    handleMouseButtonUp(&event);
                    break;
                case SDL_QUIT:
                    done = 1;
                    break;
                }
            }
            render(renderer);               /* draw buttons */
    
            SDL_Delay(1);
        }
    
        /* cleanup code, let's free up those sound buffers */
        for (i = 0; i < NUM_DRUMS; i++) {
            SDL_free(drums[i].buffer);
        }
        /* let SDL do its exit code */
        SDL_Quit();
    
        return 0;
    }