cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

beep.c (5316B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "beep.h"
     21#include "rdp.h"
     22#include "settings.h"
     23
     24#include <freerdp/freerdp.h>
     25#include <guacamole/audio.h>
     26#include <guacamole/client.h>
     27#include <guacamole/mem.h>
     28#include <winpr/wtypes.h>
     29
     30#include <inttypes.h>
     31#include <stdlib.h>
     32#include <string.h>
     33
     34/**
     35 * Fills the given buffer with signed 8-bit, single-channel PCM at the given
     36 * sample rate which will produce a beep of the given frequency.
     37 *
     38 * @param buffer
     39 *     The buffer to fill with PCM data.
     40 *
     41 * @param frequency
     42 *     The frequency of the beep to generate, in hertz.
     43 *
     44 * @param rate
     45 *     The sample rate of the PCM to generate, in samples per second.
     46 *
     47 * @param buffer_size
     48 *     The number of bytes of PCM data to write to the given buffer.
     49 */
     50static void guac_rdp_beep_fill_triangle_wave(unsigned char* buffer,
     51        int frequency, int rate, int buffer_size) {
     52
     53    /* With the distance between each positive/negative peak and zero being the
     54     * amplitude, and with the "bounce" between those peaks occurring once
     55     * every two periods, the number of distinct states that the triangle wave
     56     * function goes through is twice the peak-to-peak amplitude, or four times
     57     * the overall amplitude */
     58    const int wave_period = GUAC_RDP_BEEP_AMPLITUDE * 4;
     59
     60    /* With the number of distinct states being the wave_period defined above,
     61     * the "bounce" point within that period is half the period */
     62    const int wave_bounce_offset = wave_period / 2;
     63
     64    for (int position = 0; position < buffer_size; position++) {
     65
     66        /* Calculate relative position within the repeating portion of the wave
     67         * (the portion with wave_period unique states) */
     68        int wave_position = (position * frequency * wave_period / rate) % wave_period;
     69
     70        /* Calculate state of the triangle wave function at the calculated
     71         * offset, knowing in advance the relative location that the function
     72         * should "bounce" */
     73        *(buffer++) = abs(wave_position - wave_bounce_offset) - GUAC_RDP_BEEP_AMPLITUDE;
     74
     75    }
     76
     77}
     78
     79/**
     80 * Writes PCM data to the given guac_audio_stream which produces a beep of the
     81 * given frequency and duration. The provided guac_audio_stream may be
     82 * configured for any sample rate but MUST be configured for single-channel,
     83 * 8-bit PCM.
     84 *
     85 * @param audio
     86 *     The guac_audio_stream which should receive the PCM data.
     87 *
     88 * @param frequency
     89 *     The frequency of the beep, in hertz.
     90 *
     91 * @param duration
     92 *     The duration of the beep, in milliseconds.
     93 */
     94static void guac_rdp_beep_write_pcm(guac_audio_stream* audio,
     95        int frequency, int duration) {
     96
     97    size_t buffer_size = guac_mem_ckd_mul_or_die(audio->rate, duration) / 1000;
     98    unsigned char* buffer = guac_mem_alloc(buffer_size);
     99
    100    /* Beep for given frequency/duration using a simple triangle wave */
    101    guac_rdp_beep_fill_triangle_wave(buffer, frequency, audio->rate, buffer_size);
    102    guac_audio_stream_write_pcm(audio, buffer, buffer_size);
    103
    104    guac_mem_free(buffer);
    105
    106}
    107
    108BOOL guac_rdp_beep_play_sound(rdpContext* context,
    109        const PLAY_SOUND_UPDATE* play_sound) {
    110
    111    guac_client* client = ((rdp_freerdp_context*) context)->client;
    112    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    113    guac_rdp_settings* settings = rdp_client->settings;
    114
    115    /* Ignore if audio is not enabled */
    116    if (!settings->audio_enabled) {
    117        guac_client_log(client, GUAC_LOG_DEBUG, "Ignoring request to beep "
    118                "for %" PRIu32 " millseconds at %" PRIu32 " Hz as audio is "
    119                "disabled.", play_sound->duration, play_sound->frequency);
    120        return TRUE;
    121    }
    122
    123    /* Allocate audio stream which sends audio in a format supported by the
    124     * connected client(s) */
    125    guac_audio_stream* beep = guac_audio_stream_alloc(client, NULL,
    126            GUAC_RDP_BEEP_SAMPLE_RATE, 1, 8);
    127
    128    /* Stream availability is not guaranteed */
    129    if (beep == NULL) {
    130        guac_client_log(client, GUAC_LOG_DEBUG, "Ignoring request to beep "
    131                "for %" PRIu32 " millseconds at %" PRIu32 " Hz as no audio "
    132                "stream could be allocated.", play_sound->duration,
    133                play_sound->frequency);
    134        return TRUE;
    135    }
    136
    137    /* Limit maximum duration of each beep */
    138    int duration = play_sound->duration;
    139    if (duration > GUAC_RDP_BEEP_MAX_DURATION)
    140        duration = GUAC_RDP_BEEP_MAX_DURATION;
    141
    142    guac_rdp_beep_write_pcm(beep, play_sound->frequency, duration);
    143    guac_audio_stream_free(beep);
    144
    145    return TRUE;
    146
    147}
    148