summaryrefslogtreecommitdiffstats
path: root/apps/beep.c
blob: a6244d932c764d16deea3c10b3ce8aa547b6717e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (c) 2011 Michael Sevakis
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "config.h"
#include "system.h"
#include "settings.h"
#include "dsp.h"
#include "pcm.h"
#include "pcm_mixer.h"
#include "misc.h"

static int32_t beep_phase;      /* Phase of square wave generator */
static uint32_t beep_step;      /* Step of square wave generator on each sample */
static uint32_t beep_amplitude; /* Amplitude of square wave generator */
static int beep_count;          /* Number of samples remaining to generate */

/* Reserve enough static space for keyclick to fit */
#define BEEP_BUF_COUNT (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION)
static uint32_t beep_buf[BEEP_BUF_COUNT] IBSS_ATTR;

/* Actually output samples into beep_buf */
#if defined(CPU_ARM)
static FORCE_INLINE void beep_generate(int count)
{
    uint32_t *out = beep_buf;
    uint32_t s;

    asm volatile (
    "1:                            \n"
        "eor   %3, %5, %1, asr #31 \n"
        "subs  %2, %2, #1          \n"
        "str   %3, [%0], #4        \n"
        "add   %1, %1, %4          \n"
        "bgt   1b                  \n"
        : "+r"(out), "+r"(beep_phase), "+r"(count),
          "=&r"(s)
        : "r"(beep_step), "r"(beep_amplitude));
}
#elif defined (CPU_COLDFIRE)
static FORCE_INLINE void beep_generate(int count)
{
    uint32_t *out = beep_buf;
    uint32_t s;

    asm volatile (
    "1:                   \n"
        "move.l %1, %3    \n"
        "add.l  %4, %1    \n"
        "add.l  %3, %3    \n"
        "subx.l %3, %3    \n"
        "eor.l  %5, %3    \n"
        "move.l %3, (%0)+ \n"
        "subq.l #1, %2    \n"
        "bgt.b  1b        \n"
        : "+a"(out), "+d"(beep_phase), "+d"(count),
          "=&d"(s)
        : "r"(beep_step), "d"(beep_amplitude));
}
#else
static FORCE_INLINE void beep_generate(int count)
{
    uint32_t *out = beep_buf;
    uint32_t amplitude = beep_amplitude;
    uint32_t step = beep_step;
    int32_t phase = beep_phase;

    do
    {
        *out++ = (phase >> 31) ^ amplitude;
        phase += step;
    }
    while (--count > 0);

    beep_phase = phase;
}
#endif

/* Callback to generate the beep frames - also don't want inlining of
   call below in beep_play */
static void __attribute__((noinline))
beep_get_more(unsigned char **start, size_t *size)
{
    int count = beep_count;

    if (count > 0)
    {
        count = MIN(count, BEEP_BUF_COUNT);
        beep_count -= count;
        *start = (unsigned char *)beep_buf;
        *size = count * sizeof(uint32_t);
        beep_generate(count);
    }
}

/* Generates a constant square wave sound with a given frequency in Hertz for
   a duration in milliseconds */
void beep_play(unsigned int frequency, unsigned int duration,
               unsigned int amplitude)
{
    mixer_channel_stop(PCM_MIXER_CHAN_BEEP);

    if (frequency == 0 || duration == 0 || amplitude == 0)
        return;

    if (amplitude > INT16_MAX)
        amplitude = INT16_MAX;

    /* Setup the parameters for the square wave generator */
    beep_phase = 0;
    beep_step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
    beep_count = NATIVE_FREQUENCY / 1000 * duration;
    beep_amplitude = amplitude | (amplitude << 16); /* Word:|AMP16|AMP16| */

    /* If it fits - avoid cb overhead */
    unsigned char *start;
    size_t size;

    /* Generate first frame here */
    beep_get_more(&start, &size);

    mixer_channel_set_amplitude(PCM_MIXER_CHAN_BEEP, MIX_AMP_UNITY);
    mixer_channel_play_data(PCM_MIXER_CHAN_BEEP,
                            beep_count ? beep_get_more : NULL,
                            start, size);
}