It would be trivial to build an arduino based device that could plug into the headphone jack and then buzz a buzzer at different speeds depending on the frequency of the headphone signal so the buzzer would be able to pass along any tone ID information. I recently built a similar device that turned on and off strings of leds depending on the frequencies of the music that was playing. Someone could use the same code but make slight modifications to the code base in order to make it buzz a buzzer instead of light up lights. Probably cost <$40 or so to build.
Here is the code. It was written for an arduino uno but should run ok on an arduino mini which would be a better form factor for such a device. All you would need to do is hook up the headphone jack output to analog pin 0 and then rewrite the led output code to output a vibration frequency on one of the digital pins and then hook up the buzzer to that pin and ground. You could 3D print a case for it if you wanted.
// audio input to pin A0
// output leds
const int lowLedPin = 4;
const int midLedPin = 5;
const int mid2LedPin = 6;
const int hiLedPin = 7;
//clipping indicator variables
boolean clipping = 0;
//data storage variables
byte newData = 0;
byte prevData = 0;
unsigned int time = 0; //keeps time and sends vales to store in timer[] occasionally
int timer[10]; //sstorage for timing of events
int slope[10]; //storage for slope of events
unsigned int totalTimer; //used to calculate period
unsigned int period; //storage for period of wave
byte index = 0; //current storage index
float frequency; //storage for frequency calculations
int maxSlope = 0; //used to calculate max slope as trigger point
int newSlope; //storage for incoming slope data
//variables for decided whether you have a match
byte noMatch = 0; //counts how many non-matches you've received to reset variables if it's been too long
byte slopeTol = 4; //slope tolerance- adjust this if you need
int timerTol = 25;
//variables for amp detection
unsigned int ampTimer = 0;
byte maxAmp = 0;
byte checkMaxAmp;
byte ampThreshold = 20; //raise if you have a very noisy signal
boolean LLP,mLP, m2LP, hLP = false;
void setup(){
Serial.begin(57600); // for debugging output via usb
pinMode(13,OUTPUT); //led indicator pin
pinMode(12,OUTPUT); //output pin
pinMode(lowLedPin, OUTPUT);
pinMode(midLedPin, OUTPUT);
pinMode(mid2LedPin, OUTPUT);
pinMode(hiLedPin, OUTPUT);
cli(); //disable interrupts
//set up continuous sampling of analog pin 0 at 38.5kHz
//clear ADCSRA and ADCSRB registers
ADCSRA = 0;
ADCSRB = 0;
ADMUX |= (1 << REFS0); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements
sei(); //enable interrupts
}
ISR(ADC_vect) { //when new ADC value ready
PORTB &= B11101111; //set pin 12 low
prevData = newData; //store previous value
newData = ADCH; //get value from A0
if (prevData < 127 && newData >=127){ //if increasing and crossing midpoint
newSlope = newData - prevData; //calculate slope
if (abs(newSlope-maxSlope)<slopeTol){ //if slopes are ==
//record new data and reset time
slope[index] = newSlope;
timer[index] = time;
time = 0;
if (index == 0){ //new max slope just reset
PORTB |= B00010000; //set pin 12 high
noMatch = 0;
index++;//increment index
}
else if (abs(timer[0]-timer[index])<timerTol && abs(slope[0]-newSlope)<slopeTol){ //if timer duration and slopes match
//sum timer values
totalTimer = 0;
for (byte i=0;i<index;i++){
totalTimer+=timer;
}
period = totalTimer; //set period
//reset new zero index values to compare with
timer[0] = timer[index];
slope[0] = slope[index];
index = 1;//set index to 1
PORTB |= B00010000; //set pin 12 high
noMatch = 0;
}
else{ //crossing midpoint but not match
index++; //increment index
if (index > 9){
reset();
}
}
}
else if (newSlope>maxSlope){ //if new slope is much larger than max slope
maxSlope = newSlope;
time = 0; //reset clock
noMatch = 0;
index = 0; //reset index
}
else{//slope not steep enough
noMatch++; //increment no match counter
if (noMatch>9){
reset();
}
}
}
if (newData == 0 || newData == 1023){ //if clipping
PORTB |= B00100000; //set pin 13 high- turn on clipping indicator led
clipping = 1; //currently clipping
}
time++; //increment timer at rate of 38.5kHz
ampTimer++; //increment amplitude timer
if (abs(127-ADCH)>maxAmp){
maxAmp = abs(127-ADCH);
}
if (ampTimer==1000){
ampTimer = 0;
checkMaxAmp = maxAmp;
maxAmp = 0;
}
}
void reset(){ //clear out some variables
index = 0; //reset index
noMatch = 0; //reset match counter
maxSlope = 0; //reset slope
}
void checkClipping(){ //manage clipping indicator LED
if (clipping){ //if currently clipping
PORTB &= B11011111; //turn off clipping indicator led
clipping = 0;
}
}
void loop(){
checkClipping();
if (checkMaxAmp>ampThreshold){
frequency = 38462/float(period); //calculate frequency timer rate/period
/*
//print results remove comment characters for debugging
Serial.print(frequency);
Serial.println(" hz");
*/
}
delay(3);
// turn on leds as indicated lLP,mLP, m2LP, hLP
// modify the code below to buzz buzzer instead of light leds, currently set up for 4 tones ranges
// < 400 hz
// 400 hz thru 1250 hz
// 1250 hz thru 2 Khz
// > 2Khz
if (frequency < 400) {
LLP = HIGH;
} else {
LLP = LOW;}
digitalWrite(lowLedPin, LLP);
if (frequency > 400 && frequency < 1250) {
mLP = HIGH;
} else {
mLP = LOW;
}
digitalWrite(midLedPin, mLP);
if (frequency >1250 && frequency < 2000) {
m2LP = HIGH;
} else {
m2LP = LOW;
}
digitalWrite(mid2LedPin, m2LP);
if (frequency > 2000) {
hLP = HIGH;
} else {
hLP = LOW;
}
digitalWrite(hiLedPin, hLP) ;
}