Thread: Producing a "beeep!" as simply as possible

  1. #1
    Registered User
    Join Date
    Dec 2008
    Posts
    15

    Producing a "beeep!" as simply as possible

    Hi all,

    I just finished my very first programming class in C. (I got an A!) I feel fairly comfortable with the basics but now that class is over I wanted to challenge myself to write something useful.

    I have recently become interested in Amateur (Ham) Radio and I would like to teach myself Morse Code. My goal is to write a C program that will read a simple text file and convert the ascii characters into their corresponding "dots" and "dashes" to be played over the speaker. I would imagine I am looking for some sort of Beep() function that allows me to specify a time interval as an argument. What also comes to mind is some sort of way to pause between letters.

    My problem is, I can't seem to find a function that allows me to access the speaker! I have searched prior threads, but the solutions offered seem like overkill - I don't want to playback MP3s and I'm not sure I know enough yet to be tooling around with libraries that access the soundcard.

    FYI, just in case it matters I am running Ubuntu 7.10 with the GNU/gcc compiler and the regular old standard set of library/header files that come with it.

    Any and all suggestions are greatly appreciated!
    Thanks,
    -Dave

  2. #2
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    I believe you just do this:
    Code:
    putchar('\a');
    \a is a special beep character. Try it!
    There is also a Beep function for Windows. Dunno if something similar for Linux. There's should be but the above is the easiest

  3. #3
    Registered User
    Join Date
    Dec 2008
    Posts
    15
    [QUOTE=C_ntua;817013]I believe you just do this:
    Code:
    putchar('\a');
    I had actually thought of that at first, but there doesn't seem to be a way to control the duration of the beep, which is necessary to reproduce Morse Code.

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    So output it two or more times ..... that results in multiple beeps back-to-back which will often be perceived as one long beep.

  5. #5
    Registered User
    Join Date
    Dec 2008
    Posts
    15
    Sorry to be difficult, but not having a finer control over the duration would actually defeat the purpose of the program.

    You see, in order to understand morse code, you have to start out by comprehending each letter slowly, and gradually work up to a fairly quick rate of speed. Some people even claim that they progress beyond hearing letters and actually start to hear entire words as one quick succession of "dots" and "dashes."

    I realize that a function to control the speaker is a platform-specific thing, but surely there has to be something for Intel platforms that sounds a tone. I'm pretty sure I'm looking for the equivalent of BASIC's SOUND function.

    Does anyone know what I would need to start reading about to access the sound card? Maybe I can find a library that will have the function I am looking for. How does a library work for soundcards, considering the wide variety of manufacturers out there. Is there a generic "Soundblaster" library?

    Worried,
    -Dave

  6. #6
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by mutandis View Post
    Does anyone know what I would need to start reading about to access the sound card? Maybe I can find a library that will have the function I am looking for. How does a library work for soundcards, considering the wide variety of manufacturers out there. Is there a generic "Soundblaster" library?
    This will depend on your platform. The easiest thing, IMHO, is to use MIDI to produce sounds of the particular durations you need. How that's done will depend on the OS. Windows, Linux, what?
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  7. #7
    Registered User
    Join Date
    Dec 2008
    Posts
    15
    I am running Linux. Ubuntu 7.1 to be specific....

    I have had some success compiling the example code found here:
    http://www.oreilly.de/catalog/multil...pt/ch14-01.htm

    One solution seems to be to write to the /dev/dsp device... but as I've stated I'm very new to C so it's not entirely clear everything that's happening in this code.

    It looks like the write() function is what I need to focus on, but from what I can make of the example it seems like it's looking for a pointer to audio data. Now all I have to do is figure out how to generate some audio data to feed to the write function to reproduce the sounds I need. Right? Does that make sense to anyone else besides me?

    Anyone have any suggestions on how I should go about doing this?

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by mutandis View Post
    It looks like the write() function is what I need to focus on, but from what I can make of the example it seems like it's looking for a pointer to audio data. Now all I have to do is figure out how to generate some audio data to feed to the write function to reproduce the sounds I need. Right? Does that make sense to anyone else besides me?
    It makes sense, but it seems like a lot of work considering that you just want to produce "beeps." I still think MIDI is a good option. You are trying to produce Morse code, not get into the details of waveforms and sampling rates and such things...
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    Turbo C/C++ to the rescue!

    From their help file:

    Turns PC speaker on at specified frequency.

    Code:
     Syntax:
       void sound(unsigned frequency);
    
     Prototype in:
     dos.h
    
     Remarks:
    sound turns on the PC's speaker at a given frequency.
    
    frequency specifies the frequency of the sound in hertz (cycles per second).
    
    To turn the speaker off after a call to sound, call the function nosound.
    
     Return Value: None.
    
     Portability:
    sound works with IBM PCs and compatibles only. A corresponding function
    exists in Turbo Pascal.
    
     See Also:
      delay    nosound
    
     Example:
     /* Emits a 440-Hz tone (A above middle C) for 1 second. */
    
     #include <dos.h>
    
     int main(void)
     {
        sound(440);
        delay(1000);
        nosound();
        return 0;
     }
    Good luck!

    This does not use a sound card - just a pc speaker, like in the old DOS days. Today, not all pc's have an internal speaker, but if you hear a "beep", when you boot up, or etc., then you're good to go.

    You'll need to update this for your Ubuntu Linux. They have a nice forum that can assist, I'm sure. BTW, 8.04.1 "Hardy Heron" LTS is a sweet upgrade. Haven't tried the 8.1 version.

    This works fine in WindowsXP, btw.
    Last edited by Adak; 12-12-2008 at 10:33 PM.

  10. #10
    Registered User
    Join Date
    Dec 2008
    Posts
    15
    Thanks Adak! Unfortunately I have a Linux machine and haven't yet figured out how to adapt dos.h into my library, but that is an encouraging avenue to investigate.

    In the meantime, I wanted to report back on some googling I've been doing. I haven't had much success getting any simple examples of MIDI working. Several examples will compile, but for some reason I can't seem to get any sound out. I believe this is because either; A) I have something configured incorrectly or, B) The code I am running is trying to output MIDI signals to an *external* device. I'll keep playing around.

    I did however find what I thought was a really neat piece of simple code that allows you to record and playback:

    Code:
    /*
     * parrot.c
     * Program to illustrate /dev/dsp device
     * Records several seconds of sound, then echoes it back.
     * Runs until Control-C is pressed.
     */
    
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/ioctl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <linux/soundcard.h>
    
    #define LENGTH 3    /* how many seconds of speech to store */
    #define RATE 8000   /* the sampling rate */
    #define SIZE 8      /* sample size: 8 or 16 bits */
    #define CHANNELS 1  /* 1 = mono 2 = stereo */
    
    /* this buffer holds the digitized audio */
    unsigned char buf[LENGTH*RATE*SIZE*CHANNELS/8];
    
    int main()
    {
      int fd;	/* sound device file descriptor */
      int arg;	/* argument for ioctl calls */
      int status;   /* return status of system calls */
    
      /* open sound device */
      fd = open("/dev/dsp", O_RDWR);
      if (fd < 0) {
        perror("open of /dev/dsp failed");
        exit(1);
      }
    
      /* set sampling parameters */
      arg = SIZE;	   /* sample size */
      status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
      if (status == -1)
        perror("SOUND_PCM_WRITE_BITS ioctl failed");
      if (arg != SIZE)
        perror("unable to set sample size");
    
      arg = CHANNELS;  /* mono or stereo */
      status = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &arg);
      if (status == -1)
        perror("SOUND_PCM_WRITE_CHANNELS ioctl failed");
      if (arg != CHANNELS)
        perror("unable to set number of channels");
    
      arg = RATE;	   /* sampling rate */
      status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg);
      if (status == -1)
        perror("SOUND_PCM_WRITE_WRITE ioctl failed");
    
      while (1) { /* loop until Control-C */
        printf("Say something:\n");
        status = read(fd, buf, sizeof(buf)); /* record some sound */
        if (status != sizeof(buf))
          perror("read wrong number of bytes");
        printf("You said:\n");
        status = write(fd, buf, sizeof(buf)); /* play it back */
        if (status != sizeof(buf))
          perror("wrote wrong number of bytes");
        /* wait for playback to complete before recording again */
        status = ioctl(fd, SOUND_PCM_SYNC, 0); 
      if (status == -1)
        perror("SOUND_PCM_SYNC ioctl failed");
      }
    }
    I have since modified this code to write the buf pointer out to a file. From there I was able to simply issue the bash command, "cat outputfilename > /dev/dsp" and it plays back the file! Neato! Not very helpful for what I'm doing, but it's a fun distraction and the code seems small enough for a newbie like myself to tackle at a later time. I'm not quite sure what to make of the data it outputs, however, as I am unable to open the file with any of my audio utilities. Does anyone know what (if any) "format" this data is written in?

    Back on topic: Next, I found this piece of code which claims to generate tones, which would be great - **if I could get it to compile...**

    Code:
    /* tonegen.c - Generate single, dual, or Touch Tone tones
       Version 0.1
       Copyright (c) 2004 Joseph Battaglia <[email protected]>
       
       Permission is hereby granted, free of charge, to any person obtaining a copy
       of this software and associated documentation files (the "Software"), to
       deal in the Software without restriction, including without limitation the
       rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
       sell copies of the Software, and to permit persons to whom the Software is
       furnished to do so, subject to the following conditions:
       
       The above copyright notice and this permission notice shall be included in
       all copies or substantial portions of the Software.
       
       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
       FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
       IN THE SOFTWARE.
    */
    
    #include <fcntl.h>
    #include <math.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <linux/ioctl.h>
    #include <linux/soundcard.h>
    #include <linux/stat.h>
    #include <linux/types.h>
    #include <unistd.h>
    
    
    #define BITS 8
    #define BUF_SIZE 4096
    #define CHANNELS 1
    #define DEVICE "/dev/dsp"
    #define RATE 8000
    #define VERSION "0.1"
    
    
    char *device;
    int bits, channels, rate, silent = 0;
    
    int bufpos = 0, sound_fd;
    unsigned char audio_buffer[BUF_SIZE];
    
    
    void write_buffer(void) {
      
      if(bufpos != 0) {
        
        if(write(sound_fd, audio_buffer, bufpos) == -1) {
          perror("write()");
          exit(1);
        }
        
        bufpos = 0;
        
      }
      
    }
    
    
    void add_to_buffer(int val) {
      
      int i;
      
      if(bufpos >= BUF_SIZE - 4) {
        write_buffer();
      }
      
      for(i = 0; i < channels; i++) {
        
        if(bits == 8) {
          audio_buffer[bufpos++] = 128 + (val >> 8);
        }
        
        if(bits == 16) {
          audio_buffer[bufpos++] = val & 0xff;
          audio_buffer[bufpos++] = (val >> 8) & 0xff;
        }
        
      }
      
    }
    
    
    void single_tone(int freq, int msec) {
      
      double add, c;
      int length, val;
      
      add = (double)freq / rate;
      length = (msec * rate) / 1000;
      
      while(--length >= 0) {
        
        val = (int)(sin(c * 2 * M_PI) * 16383);
        
        add_to_buffer(val);
        
        c += add;
        
      }
      
      write_buffer();
      
    }
    
    
    void dual_tone(int freq1, int freq2, int msec) {
      
      double add1, add2, c1, c2;
      int length, val, val1, val2;
      
      add1 = (double)freq1 / rate;
      add2 = (double)freq2 / rate;
      length = (msec * rate) / 1000;
      
      while(--length >= 0) {
        
        val1 = (int)(sin(c1 * 2 * M_PI) * 16383);
        val2 = (int)(sin(c2 * 2 * M_PI) * 16383);
        
        val = val1 + val2;
        
        add_to_buffer(val);
        
        c1 += add1;
        c2 += add2;
        
      }
      
      write_buffer();
      
    }
    
    
    void touch_tone(char tone, int msec) {
      
      switch(tone) {
        case '0':
          dual_tone(941, 1336, msec);
          break;
        case '1':
          dual_tone(697, 1209, msec);
          break;
        case '2':
          dual_tone(697, 1336, msec);
          break;
        case '3':
          dual_tone(697, 1477, msec);
          break;
        case '4':
          dual_tone(770, 1209, msec);
          break;
        case '5':
          dual_tone(770, 1336, msec);
          break;
        case '6':
          dual_tone(770, 1477, msec);
          break;
        case '7':
          dual_tone(852, 1209, msec);
          break;
        case '8':
          dual_tone(852, 1336, msec);
          break;
        case '9':
          dual_tone(852, 1477, msec);
          break;
        case '*':
          dual_tone(941, 1209, msec);
          break;
        case '#':
          dual_tone(941, 1477, msec);
          break;
        case 'A':
          dual_tone(697, 1633, msec);
          break;
        case 'B':
          dual_tone(770, 1633, msec);
          break;
        case 'C':
          dual_tone(852, 1633, msec);
          break;
        case 'D':
          dual_tone(941, 1633, msec);
          break;
        case 'R':
          dual_tone(440, 480, msec);
          break;
        case 'S':
          dual_tone(480, 620, msec);
          break;
        case 'T':
          dual_tone(350, 440, msec);
          break;
        case ',':
          dual_tone(0, 0, msec);
          break;
      }
      
    }
    
    
    void print_version(FILE *stream) {
      
      fprintf(stream, "tonegen - Generate single, dual, or Touch Tone tones\n");
      fprintf(stream, "Version %s\n", VERSION);
      fprintf(stream, "Copyright (c) 2004 Joseph Battaglia\n");
      
    }
    
    
    void print_help(FILE *stream, char *exec) {
      
      print_version(stream);
      
      fprintf(stream, " Usage %s [OPTIONS]\n", exec);
      fprintf(stream, "  -a   Specify a string of Touch Tone numbers\n");
      fprintf(stream, "  -b   Specify number of bits (default %d)\n", bits);
      fprintf(stream, "  -c   Specify number of channels (default %d)\n", channels);
      fprintf(stream, "  -d   Specify sound card device (default %s)\n", device);
      fprintf(stream, "  -f   Specify frequencies (one, two, or Touch Tone)\n");
      fprintf(stream, "         eg. -f 2600 (or) -f 1100,1700 (or) -f T1 \n");
      fprintf(stream, "         Special T tones: R: ring  S: busy  T: dial\n");
      fprintf(stream, "  -h   Show help and exit\n");
      fprintf(stream, "  -l   Specify length of tone (in miliseconds)\n");
      fprintf(stream, "  -m   Match tonegen settings to sound card (closest supported match)\n");
      fprintf(stream, "  -r   Specify sample rate (default %d)\n", rate);
      fprintf(stream, "  -s   Silent\n");
      fprintf(stream, "  -v   Show version and exit\n");
      
    }
    
    
    int main(int argc, char *argv[]) {
      
      char tt = -1;
      char *tts = NULL;
      int ch, freq1 = 0, freq2 = 0, i, length = 1000, match = 0;
      int s_bits, s_channels, s_rate;
      extern char *optarg;
      
      bits = BITS;
      channels = CHANNELS;
      rate = RATE;
      
      if((device = malloc(strlen(DEVICE) + 1)) == NULL) {
        perror("malloc()");
        exit(1);
      }
      
      strcpy(device, DEVICE);
      
      while((ch = getopt(argc, argv, "a:b:c:d:f:hl:mr:sv")) != -1) {
        switch(ch) {
          case 'a':
            if((tts = malloc(strlen(optarg) + 1)) == NULL) {
              perror("malloc()");
              exit(1);
            }
            strcpy(tts, optarg);
            break;
          case 'b':
            bits = atoi(optarg);
            if(bits != 8 && bits != 16) {
              fprintf(stderr, "-b argument must be either 8 or 16\n");
              exit(1);
            }
            break;
          case 'c':
            channels = atoi(optarg);
            if(channels != 1 && channels != 2) {
              fprintf(stderr, "-c argument must be either 1 or 2\n");
              exit(1);
            }
            break;
          case 'd':
            if((device = realloc(device, strlen(optarg) + 1)) == NULL) {
              perror("realloc()");
              exit(1);
            }
            strcpy(device, optarg);
            break;
          case 'f':
            if(optarg[0] == 'T') {
              tt = optarg[1];
            }
            else {
              if(strchr(optarg, ',') == NULL) {
                if((freq1 = atoi(optarg)) < 0) {
                  fprintf(stderr, "Invalid frequency\n");
                  exit(1);
                }
              }
              else {
                if((freq1 = atoi(strtok(optarg, ","))) < 0) {
                  fprintf(stderr, "Invalid frequency\n");
                  exit(1);
                }
                if((freq2 = atoi(strtok(NULL, ","))) < 0) {
                  fprintf(stderr, "Invalid frequency\n");
                  exit(1);
                }
              }
            }
            break;
          case 'h':
            print_help(stdout, argv[0]);
            return(0);
            break;
          case 'l':
            length = atoi(optarg);
            if(rate <= 0) {
              fprintf(stderr, "Invalid length argument\n");
              exit(1);
            }
            break;
          case 'm':
            match = 1;
            break;
          case 'r':
            rate = atoi(optarg);
            if(rate <= 0 && rate > 65535) {
              fprintf(stderr, "Invalid sample rate\n");
              exit(1);
            }
            break;
          case 's':
            silent = 1;
            break;
          case 'v':
            print_version(stdout);
            return(0);
            break;
          default:
            break;
        }
      }
      
      i = 0;
      if(freq1) { i++; }
      if(tt != -1) { i++; }
      if(tts != NULL) { i++; }
      if(i > 1) {
        fprintf(stderr, "You may only specify one type of tone generation\n\n");
        print_help(stderr, argv[0]);
        exit(1);
      }
      if(i < 1) {
        fprintf(stderr, "Please specify a type of tone generation\n");
        print_help(stderr, argv[0]);
        exit(1);
      }
      
      if((sound_fd = open(device, O_WRONLY)) == -1) {
        perror("open()");
        exit(1);
      }
      
      if(match) {
        if(ioctl(sound_fd, SOUND_PCM_WRITE_BITS, &bits) == -1) {
          perror("ioctl()");
          exit(1);
        }
        if(ioctl(sound_fd, SOUND_PCM_WRITE_CHANNELS, &channels) == -1) {
          perror("ioctl()");
          exit(1);
        }
        if(ioctl(sound_fd, SOUND_PCM_WRITE_RATE, &rate) == -1) {
          perror("ioctl()");
          exit(1);
        }
      }
      
      if(ioctl(sound_fd, SOUND_PCM_READ_BITS, &s_bits) == -1) {
        perror("ioctl()");
        exit(1);
      }
      if(ioctl(sound_fd, SOUND_PCM_READ_CHANNELS, &s_channels) == -1) {
        perror("ioctl()");
        exit(1);
      }
      if(ioctl(sound_fd, SOUND_PCM_READ_RATE, &s_rate) == -1) {
        perror("ioctl()");
        exit(1);
      }
      
      if(match) {
        bits = s_bits;
        channels = s_channels;
        rate = s_rate;
      }
      
      if(!silent) {
        print_version(stdout);
        printf("\n");
        printf("Device: %s\n", device);
        printf("\n");
        printf("Sound Card Settings\n");
        printf("-------------------\n");
        printf("Bits: %d\n", s_bits);
        printf("Channels: %d\n", s_channels);
        printf("Rate: %dhz\n", s_rate);
        printf("\n");
        printf("tonegen Settings\n");
        printf("-------------------\n");
        printf("Bits: %d\n", bits);
        printf("Channels: %d\n", channels);
        printf("Rate: %dhz\n", rate);
      }
      
      if(!freq1 && !freq2) {
        if(tts != NULL) {
          for(i = 0; i < strlen(tts); i++) {
            touch_tone(tts[i], length);
          }
        }
        else {
          touch_tone(tt, length);
        }
      }
      else {
        if(!freq2) {
          single_tone(freq1, length);
        }
        else {
          dual_tone(freq1, freq2, length);
        }
      }
      
      close(sound_fd);
      
      return(0);
      
    }
    It looks like I may not have all of the pieces to this program because some undeclared functions and constants prevent this from compiling. Nevertheless, I believe with some tinkering I can take some ideas from this code and apply it to the first piece of code to generate tones by writing to the buffer.

    We'll see, in the meantime I've been having lots of fun as this is the most difficult task I have faced to date!

    -Dave

  11. #11
    Registered User
    Join Date
    Dec 2008
    Posts
    15
    Update: The second example compiles fine. I forgot about the -lm option in the case of #including the math.h library. Check it out, pretty nifty stuff!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. converting "String___gc" simply to "string"
    By mitilkhatoon in forum C++ Programming
    Replies: 12
    Last Post: 09-29-2006, 04:07 AM
  2. Simply initializing an array AFTER defining it
    By fd9 in forum C++ Programming
    Replies: 7
    Last Post: 12-02-2005, 11:09 PM
  3. Producing Beep Sound with C++
    By ron4ldk in forum C++ Programming
    Replies: 8
    Last Post: 05-01-2004, 01:05 AM
  4. A Good Discussion - Stay on Topic
    By DavidP in forum A Brief History of Cprogramming.com
    Replies: 71
    Last Post: 04-28-2004, 08:52 AM
  5. IT people - weird genius or simply narrow-minded, antisocial, lazy people
    By Carlos in forum A Brief History of Cprogramming.com
    Replies: 5
    Last Post: 10-11-2001, 05:00 AM