/* * Serial Audio Streamer Thingy v1.1 - by Napalm @ Netcore2K * * You may use all or part of my code as long as you give me * credit as per the following license. * * License: Creative Commons Attribution 2.0 UK: England & Wales License * http://creativecommons.org/licenses/by/2.0/uk/ * * ASCII Schematic: * * Serial Port 12Kohm * Pin 4 (DTR) o------/\/\/\----+------------o Analog Output * | * 47nF === * Serial Port | * Pin 5 (GND) o----------------+------------o Circuit Ground * */ #include #include #include #include #include #include #include #include #include #include typedef struct _serial_audio_t { struct termios oldstate; int fd, oldbits; } serial_audio_t; typedef unsigned int pwm_t; #define BITRATE 20000 #define SAMPLERATE (BITRATE / (sizeof(pwm_t) * 8)); int serial_audio_open(char *device, serial_audio_t *audio) { struct termios newstate; if((audio->fd = open(device, O_RDWR|O_NOCTTY|O_NONBLOCK)) < 0){ fprintf(stderr, "cannot open serial port: %s\n", strerror(errno)); return -1; } tcgetattr(audio->fd, &audio->oldstate); newstate = audio->oldstate; newstate.c_iflag = IGNBRK | IGNPAR; newstate.c_oflag = 0; newstate.c_cflag = B9600 | CS8 | CREAD | CLOCAL; newstate.c_lflag = 0; newstate.c_cc[VMIN] = 0; newstate.c_cc[VTIME] = 0; tcsetattr(audio->fd, TCSANOW, &newstate); audio->oldbits = 0; ioctl(audio->fd, TIOCMGET, &audio->oldbits); return 0; } int serial_audio_close(serial_audio_t *audio) { ioctl(audio->fd, TIOCMSET, &audio->oldbits); tcsetattr(audio->fd, TCSANOW, &audio->oldstate); close(audio->fd); return 0; } void changemode(int dir) { static struct termios oldt, newt; if(dir == 1){ tcgetattr( STDIN_FILENO, &oldt); newt = oldt; newt.c_lflag &= ~( ICANON | ECHO ); tcsetattr( STDIN_FILENO, TCSANOW, &newt); }else tcsetattr( STDIN_FILENO, TCSANOW, &oldt); } int kbhit(void) { struct timeval tv; fd_set rdfs; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&rdfs); FD_SET(STDIN_FILENO, &rdfs); select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv); return FD_ISSET(STDIN_FILENO, &rdfs); } pwm_t amplitude(float amplitude) { int duty = (int)((amplitude + 1) * ((sizeof(pwm_t) << 2) + 0.5)); return (pwm_t) 0xFFFFFFFF << duty; } int main(int argc, char **argv) { serial_audio_t audio; int err, timer, output, music; struct stat musicstat; pwm_t current_sample; unsigned int sample; volatile int pause; float *data; if((err = serial_audio_open("/dev/ttyS0", &audio)) < 0) exit(err); // Original audio file converted into a: raw mono 32bit IEEE floating point stream. if((music = open("music.raw", O_RDONLY)) < 0){ fprintf(stderr, "failed to open music.raw\n"); serial_audio_close(&audio); exit(music); } fstat(music, &musicstat); data = (float *)mmap(NULL, musicstat.st_size, PROT_READ, MAP_PRIVATE, music, 0); if(data == NULL){ fprintf(stderr, "failed to map music.raw\n"); serial_audio_close(&audio); close(music); exit(errno); } timer = 0; sample = 0; output = audio.oldbits & ~TIOCM_DTR; printf("Playing music.raw... press any key to quit.\n"); changemode(1); while(1) { if(!(timer++ % (sizeof(pwm_t)*8))){ current_sample = amplitude(data[sample++]); if(sample * 4 >= musicstat.st_size) sample = 0; if(kbhit()) break; } current_sample >>= 1; if(current_sample & 1) output |= TIOCM_DTR; else output &= ~TIOCM_DTR; ioctl(audio.fd, TIOCMSET, &output); // Dodgy way of syncing.. should be using interrupts/signals rather than // a while loop. But meh... it works for a demo. for (pause = 0; pause < 600; pause++); } printf("Done.\n"); getchar(); changemode(0); munmap(data, musicstat.st_size); close(music); serial_audio_close(&audio); return 0; }