/** * Functions which control the PC speaker. You must execute this code as root * otherwise the beep(..) function will crash with error message 'Segmentation * fault'. * * Tested under Linux 2.4 on Intel architecture only. * * Author: Adam Buckley, adambuckley@gmx.net * Version: $Id: beep.c,v 1.2 2002/05/23 12:13:55 adam Exp $ */ /** * This source code is copyright (c) 2002 Adam Buckley, . * * This source code 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 source code is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * The GNU General Public License can be found at * http://www.gnu.org/licenses/gpl.html */ #ifndef ADAMLIB_BEEP_C #define ADAMLIB_BEEP_C #include /** * Makes a PC beep of frequency 'frequency' and of length 'length' in * microseconds. * * This function will block for 'length' microseconds. * * Note, because this function uses low level port input and output, the user * must be root, otherwise a Segmentation Fault will occur. * * Note also, from the man pages of 'outb': * * You compile with -O or -O2 or similar. The functions are defined as * inline macros, and will not be substituted in without optimization * enabled, causing unresolved references at link time. */ void beep(unsigned int frequency, unsigned int length) { unsigned int count = 1193180 / frequency; // Grant this user process permissions to access ports // The user must be 'root' in order for these lines to succeed ioperm(0x61, 0x20, 1); ioperm(0x42, 0x02, 1); // Switch on the speaker outb_p(inb_p(0x61)|3, 0x61); // Set command for counter 2, 2 byte write outb_p(0xB6, 0x43); // Select desired Hz outb_p(count & 0xff, 0x42); outb((count >> 8) & 0xff, 0x42); // Block for 'length' microseconds usleep(length); // Switch off the speaker outb(inb_p(0x61)&0xFC, 0x61); } int lookUpTable[128]; char haveMadeLookupTable = 0; /** * Builds the MIDI note number >> Hz table, ie. populates 'lookUpTable'. * * This function is called automatically by midiToHz(..) */ void makeLookupTable() { int i; // Frequency of MIDI note 0 in Hz float frequency = 8.175799; // Ratio: 2 to the power 1/12 float ratio = 1.0594631; for(i=0; i<128; i++) { lookUpTable[i] = (int) frequency; // Put value into table frequency = frequency * ratio; // Find next note in hertz } } /** * Returns the frequency in Hz of the specified MIDI note number. * * If noteNumber is smaller than 0 or greater than 127 then this function * returns the frequency of MIDI note 60. */ int midiToHz(unsigned char noteNumber) { if(haveMadeLookupTable==0) { makeLookupTable(); haveMadeLookupTable = 1; } // Unnecessary, because an unsigned char is always > 0 //if(noteNumber<0) noteNumber = 60; if(noteNumber>127) noteNumber = 60; return lookUpTable[noteNumber]; } /** * Makes a PC beep of MIDI note number 'noteNumber' and of length 'length' in * microseconds. * * If 'noteNumber' is not between 0 and 127, then this function will default * to MIDI note 60. * * This function will block for 'length' microseconds. * * Note, because this function uses low level port input and output, the user * must be root, otherwise a Segmentation Fault will occur. */ void beepNote(unsigned char noteNumber, unsigned int length) { beep(midiToHz(noteNumber), length); } /** * Silences the PC speaker */ void beepOff() { // Switch off the speaker outb(inb_p(0x61)&0xFC, 0x61); } #endif