File: cwsound.c

package info (click to toggle)
cwirc 1.8.8-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 640 kB
  • ctags: 585
  • sloc: ansic: 5,399; makefile: 292
file content (215 lines) | stat: -rw-r--r-- 6,002 bytes parent folder | download | duplicates (6)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* CWirc - X-Chat plugin for sending and receiving raw morse code over IRC
   (c) Pierre-Philippe Coupard - 18/06/2003

   CW sound generation routines

   This program is distributed under the terms of the GNU General Public License
   See the COPYING file for details
*/
#include <stdlib.h>
#include <math.h>

#include "types.h"
#include "cwsound.h"
#include "sounder_down.h"
#include "sounder_up.h"



/* Definitions */
#define SOUNDER_UP_TO_DOWN_DELAY	10	/* ms */
#define SOUNDER_DOWN_TO_UP_DELAY	60	/* ms */
#define QRN_AMPLIFICATION		2
#define QRN_CRACKLING_DURATION		2	/* ms */
#define QRN_CRACKLING_OCCUR_PROB	.005
#define SQUELCH_TIMEOUT			3000	/* ms */



/* Generate a sound fragment that is "nbsamples" long at an audio frequency
   "freq", at an amplitude "amplitude" (between 0 and 100), in the form of a
    serie of samples betweem -1 and 1, at a sampling rate of "samplerate", at
    an offset of "offset_keyup" or "offset_keydown" sample ticks, corresponding
    to a morse key state that is either up or down. There are 2 types of sounds:

    0 --> beep : simple sine wave at "freq" Hz that's either present or absent
          depending on the state of the key.
    1 --> sounder clicks : 2 sounds, one is a sharp click when the key goes up
          and the other is a duller click when the key goes down. In this case,
          "freq" is ignored.

   Also, make sure the sound changes "softly" when the key changes state, so it
   doesn't make nasty clicks.

   Return code: 0 --> all samples are null, 1 --> sound was generated
*/
int generate_cw_sound_fragment(int sndtype,int keystate_prev,int keystate,
	int samplerate,int nbsamples,int freq,int amplitude,
	unsigned long long offset_keyup,unsigned long long offset_keydown,
	double *samplebuf)
{
  static T_BOOL firstcall=1;
  static double sinetable[1000];
  unsigned long long sine_i;
  int retcode;
  int i;

  retcode=0;
  switch(sndtype)
  {
  case 0:	/* beeping sound */
    if(keystate || keystate_prev)
    {
      /* Is it the first time we're called ? */
      if(firstcall)
      {
        /* Generate the sine table. The 1000 samples contain exactly one period.
           All sine values are pre-divided by 100 so we can multiply by the
           amplitude in the sample generation code blow without having to do an
           extra division. */
        for(i=0;i<1000;i++)
          sinetable[i]=sin(((double)i*M_PI*2)/1000)/100;
        firstcall=0;
      }

      /* Make sure the sound frequency is above 75Hz so it's always audible */
      if(freq<75)
        freq=75;

      /* Generate the samples */
      for(i=0;i<nbsamples;i++)
      {
        sine_i=((double)((offset_keydown+i)*freq)*1000)/(double)samplerate;
        if(sine_i>=0)
          samplebuf[i]=(double)amplitude*sinetable[sine_i%1000];
        else
          samplebuf[i]=(double)amplitude*-sinetable[(-sine_i)%1000];

        /* Make the beeps fade in and out so they don't generate nasty clicks */
        if(!keystate_prev)
          samplebuf[i]*=(double)i/(double)nbsamples;
        else if(!keystate)
          samplebuf[i]*=(double)(nbsamples-i)/(double)nbsamples;

        if(samplebuf[i]!=0)
          retcode=1;
      }
    }
    else
      for(i=0;i<nbsamples;i++)
        samplebuf[i]=0;
    break;

  case 1:	/* sounder clicking sound */
    if(keystate)
    {
      offset_keyup-=(samplerate*SOUNDER_UP_TO_DOWN_DELAY)/1000;
      for(i=0;i<nbsamples;i++)
        if(offset_keyup>=0 && offset_keydown+i<sounder_down_nbsamples)
        {
          samplebuf[i]=(double)amplitude*((double)sounder_down[offset_keydown+i]
			/32768)/100;

          if(samplebuf[i]!=0)
            retcode=1;
        }
        else
          samplebuf[i]=0;
    }
    else
    {
      offset_keyup-=(samplerate*SOUNDER_DOWN_TO_UP_DELAY)/1000;
      for(i=0;i<nbsamples;i++)
        if(offset_keyup>=0 && offset_keyup+i<sounder_up_nbsamples)
        {
          samplebuf[i]=(double)amplitude*((double)sounder_up[offset_keyup+i]
			/32768)/100;

          if(samplebuf[i]!=0)
            retcode=1;
        }
        else
          samplebuf[i]=0;
    }
    break;
  }

  return(retcode);
}



/* Generate a QRN sound fragment that is "nbsamples" long, at an amplitude
   "amplitude" (between 0 and 100), in the form of a serie of samples betweem
   -1 and 1. */
void generate_qrn_sound_fragment(int samplerate,int nbsamples,int amplitude,
				double *samplebuf)
{
  static int div;
  static double rndval=0,rndval2;
  static double sample=0;
  static int getrndval_cnt=0;
  static int cracklingcnt=0;
  int i;

  div=samplerate/5513;

  /* Generate crackling */
  rndval2=(double)rand()/RAND_MAX;
  if(rndval2<QRN_CRACKLING_OCCUR_PROB && !cracklingcnt)
    cracklingcnt=(samplerate*QRN_CRACKLING_DURATION)/1000;

  /* Generate fake pink noise, inserting cracklings of white noise when needed*/
  for(i=0;i<nbsamples;i++)
  {
    if(!getrndval_cnt)
      rndval=((((double)rand()*2)/RAND_MAX-1)*amplitude)/100;
    sample=((sample+rndval)/div)*QRN_AMPLIFICATION;
    if(sample>1)
      sample=1;
    else if(sample<-1)
      sample=-1;

    if(cracklingcnt)
    {
      cracklingcnt--;
      samplebuf[i]=(((double)rand()/RAND_MAX-1)*amplitude)/100;
    }
    else
      samplebuf[i]=sample;

    getrndval_cnt=(getrndval_cnt+1)%div;
  }
}



/* Squelch sounds under a certain threshold */
void squelch(int squelch_level,double *sndbuf,int nbsamples,double ticklen)
{
  static double squelch_timeout=0;
  static int fadecnt=0;
  int sample;
  int i;

  for(i=0;i<nbsamples;i++)
  {
    if(squelch_timeout<=0)
    {
      sample=sndbuf[i]*100;
      if(sample>squelch_level || sample<-squelch_level)
        squelch_timeout=SQUELCH_TIMEOUT;

      /* Squelch the sound, do a fadeout */
      fadecnt+=fadecnt<nbsamples?1:0;
    }
    else
      /* Unsquelch the sound, do a fadein */
      fadecnt-=fadecnt?1:0;

    sndbuf[i]*=((double)nbsamples-(double)fadecnt)/(double)nbsamples;
  }

  if(squelch_timeout>0)
    squelch_timeout-=ticklen;
}