This picture will be scaled up to create a large Dalek that is about 1 meter in height, The Google Beta test hardware will make a nice brain for the unit.
The Voice of the Dalek
The sound that I am trying to create:
A Google search found a nice article on how to produce the Dalek sounding voice.
https://www.homebrewaudio.com/doctor-dalek-voice/
The ring modulator technique is done digitally, by making modifications to the Google AIY Beta test software. The pitch of the voice is changed by adjusting the sampling rate of the playback to change the voice from a female sounding voice to a male sounding voice.
The play_bytes() function from audio.py was modified to do the ring modulator technique. The sampling frequency of the playback command is lowered a bit to produce a Dalek sounding voice!
class Player(object):
"""Plays short audio clips from a buffer or file."""
def __init__(self, output_device='default'):
self._output_device = output_device
def play_bytes(self, audio_bytes, sample_rate, sample_width=2):
"""Play audio from the given bytes-like object.
audio_bytes: audio data (mono)
sample_rate: sample rate in Hertz (24 kHz by default)
sample_width: sample width in bytes (eg 2 for 16-bit audio)
"""
cmd = [
'aplay',
'-q',
'-t', 'raw',
'-D', self._output_device,
'-c', '1',
'-f', sample_width_to_string(sample_width),
'-r', '14000' #str(sample_rate), #12000 is OK, LOOKING for the perfect pitch bend
]
byte_array = bytearray()
for i in range(0,len(audio_bytes),2):
k = nm.cos((3*3.141592653*(i%1000))/1000)# was 2*3.141 before pitch bend
# num is not signed like you are hoping it is
# so the most significant bit needs to get adjusted to
# make it a signed number
num = audio_bytes[i+1]*256 + audio_bytes[i]
if num & 32768:
num -= 65536
# ring modulation applied here
scaled = int(num * k)
if scaled < 0:
scaled += 65536
byte_array += bytes([scaled%256])
byte_array += bytes([(scaled>>8)&255])
imm_bytes = bytes( byte_array)
aplay = subprocess.Popen(cmd, stdin=subprocess.PIPE)
# substitute processed (distorted audio)
aplay.stdin.write(imm_bytes)
# boring normal audio
#aplay.stdin.write(audio_bytes)
aplay.stdin.close()
retcode = aplay.wait()
if retcode:
logger.error('aplay failed with %d', retcode)
Adding Motion
I am reactivating a project I did in 2011 to add robotic features to the DIY DALEK.
This is a hacked Radio Control car, it originally had a HumVee body. The Radio control signal is replaced by a substitute signal generated by a micro-controller.
There are some advantages to having a separate micro-controller handle the servos and motion control aspects of the project for Raspberry Pi.
- Raspberry Pi may get very busy with the voice assistant
- The motion control code is already done from the previous project
How Does it work?
The radio control transmitter sends a signal that has the speed control signal and the steering control signal in a repeating fashion.
The micro-controller creates a substitute signal that replaces the signal from the radio receiver. A trace is cut on the board and a wire with the substitute signal is soldered there and the micro-controller is in charge of the car!
Good stuff has come along from the old project.
- A very robust long range 2 way control and telemetry radio link.
- GPS capabilities
- Accelerometer feedback for fine motion control.
- Additional servos for DALEK arm/blaster and eye control
A snippet of code showing how the timer compare interrupt makes a substitute radio control signal. The throttle control pulse comes out first and then is followed by the steering control pulse. On a dull rainy day I may convert the hardware to a new platform (arduino or mbed) but the basic technique of hacking the R/C car stays the same. I won't bother you with details concerning the Freescale Coldfire processor which was new in 2011 and now may be considered ancient :) or the software tools that support it which are also showing signs of age.
/********************************************************************/
/*
* GPT Timer Overflow on Timer Channel 0
*/
void GPT_C0F_Compare(void)
{
switch(compare_step++){
case 0 :
MCF_GPT_GPTC0 += 1420 + (unsigned short)(11*throttle);
MCF_GPT_GPTCTL1 = 0xA9;
break;
case 1 :
MCF_GPT_GPTC0 += 1000;
MCF_GPT_GPTCTL1 = 0xA9;
break;
case 2 :
MCF_GPT_GPTC0 += 1420 + (unsigned short)(11*steering);
MCF_GPT_GPTCTL1 = 0xA9;
break;
case 3 :
MCF_GPT_GPTC0 += 1000;
MCF_GPT_GPTCTL1 = 0xA9;
break;
case 4:
MCF_GPT_GPTC0 = 65535;
MCF_GPT_GPTCTL1 = 0xA9;
simple_servos(); // adjust servos 1 thru 7
if(duration_count) // countdown to prevent runaway R/C vehicle
{
duration_count--;
if(!duration_count)
{
throttle = STOP;
}
}
break;
default:
MCF_GPT_GPTC0 = (unsigned short)(1000);
compare_step = 0; // start the pulse sequence over again
MCF_GPT_GPTCTL1 = 0xAA;
break;
}
return;
}
Comments