pxt-ev3/libs/music/music.cpp

204 lines
4.7 KiB
C++
Raw Normal View History

2017-07-07 12:44:34 +02:00
#include "pxt.h"
2017-07-11 16:44:35 +02:00
#include "ev3const.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
2017-10-30 19:55:23 +01:00
#include <pthread.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
2017-07-07 12:44:34 +02:00
#define NOTE_PAUSE 20
namespace music {
2017-07-11 16:44:35 +02:00
uint8_t currVolume = 2;
2017-10-30 19:55:23 +01:00
uint8_t *lmsSoundMMap;
2017-07-11 16:44:35 +02:00
2017-10-30 19:55:23 +01:00
int writeDev(void *data, int size) {
2017-07-11 16:44:35 +02:00
int fd = open("/dev/lms_sound", O_WRONLY);
2017-10-30 19:55:23 +01:00
int res = write(fd, data, size);
2017-07-11 16:44:35 +02:00
close(fd);
2017-10-30 19:55:23 +01:00
return res;
2017-07-11 16:44:35 +02:00
}
2017-07-07 12:44:34 +02:00
/**
* Set the output volume of the sound synthesizer.
* @param volume the volume 0...256, eg: 128
*/
//% weight=96
//% blockId=synth_set_volume block="set volume %volume"
//% parts="speaker" blockGap=8
//% volume.min=0 volume.max=256
//% help=music/set-volume
//% weight=1
void setVolume(int volume) {
currVolume = max(0, min(100, volume * 100 / 256));
}
2017-07-11 16:44:35 +02:00
#define SOUND_CMD_BREAK 0
#define SOUND_CMD_TONE 1
#define SOUND_CMD_PLAY 2
#define SOUND_CMD_REPEAT 3
#define SOUND_CMD_SERVICE 4
struct ToneCmd {
uint8_t cmd;
uint8_t vol;
uint16_t freq;
uint16_t duration;
};
static void _stopSound() {
uint8_t cmd = SOUND_CMD_BREAK;
writeDev(&cmd, sizeof(cmd));
}
2017-10-30 18:25:58 +01:00
static void _playTone(uint16_t frequency, uint16_t duration, uint8_t volume) {
2017-07-11 16:44:35 +02:00
ToneCmd cmd;
cmd.cmd = SOUND_CMD_TONE;
cmd.vol = volume;
cmd.freq = frequency;
cmd.duration = duration;
// (*SoundInstance.pSound).Busy = TRUE;
writeDev(&cmd, sizeof(cmd));
}
2017-10-30 19:55:23 +01:00
static bool pumpMusicThreadRunning;
static pthread_mutex_t pumpMutex;
static Buffer currentSample;
static int samplePtr;
static pthread_cond_t sampleDone;
static void pumpMusic() {
if (currentSample == NULL) {
if (samplePtr > 0 && *lmsSoundMMap == 0) {
samplePtr = 0;
2017-10-30 19:56:03 +01:00
pthread_cond_broadcast(&sampleDone);
2017-10-30 19:55:23 +01:00
}
return;
}
uint8_t buf[250]; // max size supported by hardware
buf[0] = SOUND_CMD_SERVICE;
int len = min((int)sizeof(buf) - 1, currentSample->length - samplePtr);
if (len == 0) {
decrRC(currentSample);
currentSample = NULL;
return;
}
memcpy(buf + 1, currentSample->data + samplePtr, len);
int rc = writeDev(buf, len + 1);
if (rc > 0) {
samplePtr += len;
}
}
static void *pumpMusicThread(void *dummy) {
while (true) {
sleep_core_us(10000);
pthread_mutex_lock(&pumpMutex);
pumpMusic();
pthread_mutex_unlock(&pumpMutex);
}
}
void playSample(Buffer buf) {
if (lmsSoundMMap == NULL) {
int fd = open("/dev/lms_sound", O_RDWR);
2017-10-30 19:56:03 +01:00
lmsSoundMMap = (uint8_t *)mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2017-10-30 19:55:23 +01:00
close(fd);
}
if (!pumpMusicThreadRunning) {
pumpMusicThreadRunning = true;
pthread_t pid;
pthread_create(&pid, NULL, pumpMusicThread, NULL);
pthread_detach(pid);
}
stopUser();
2017-10-30 19:55:23 +01:00
pthread_mutex_lock(&pumpMutex);
*lmsSoundMMap = 1; // BUSY
uint8_t cmd[] = {SOUND_CMD_PLAY, (uint8_t)((currVolume / 33) + 1)};
writeDev(cmd, 2);
decrRC(currentSample);
currentSample = buf;
incrRC(buf);
samplePtr = 44; // WAV header is 44 bytes
pumpMusic();
pthread_cond_wait(&sampleDone, &pumpMutex);
pthread_mutex_unlock(&pumpMutex);
startUser();
2017-10-30 19:55:23 +01:00
}
2017-07-07 12:44:34 +02:00
/**
* Play a tone through the speaker for some amount of time.
* @param frequency pitch of the tone to play in Hertz (Hz)
* @param ms tone duration in milliseconds (ms)
*/
//% help=music/play-tone
2017-07-07 12:44:34 +02:00
//% blockId=music_play_note block="play tone|at %note=device_note|for %duration=device_beat"
//% parts="headphone" async
2017-07-07 12:44:34 +02:00
//% blockNamespace=music
//% weight=76 blockGap=8
2017-07-07 12:44:34 +02:00
void playTone(int frequency, int ms) {
if (frequency <= 0) {
2017-07-11 16:44:35 +02:00
_stopSound();
2017-07-07 12:44:34 +02:00
if (ms >= 0)
sleep_ms(ms);
} else {
if (ms > 0) {
int d = max(1, ms - NOTE_PAUSE); // allow for short rest
int r = max(1, ms - d);
2017-07-11 16:44:35 +02:00
_playTone(frequency, d, currVolume);
2017-07-07 12:44:34 +02:00
sleep_ms(d + r);
} else {
// ring
2017-07-11 16:44:35 +02:00
_playTone(frequency, 0, currVolume);
2017-07-07 12:44:34 +02:00
}
}
sleep_ms(1);
}
2017-10-30 18:25:58 +01:00
2017-12-14 01:31:42 +01:00
/**
* Play a tone through the speaker for some amount of time.
*/
2017-12-15 19:42:44 +01:00
//% help=music/stop-all-sounds
//% blockId=music_stop_all_sounds block="stop all sounds"
2017-12-14 18:17:47 +01:00
//% parts="headphone"
2017-12-14 01:31:42 +01:00
//% blockNamespace=music
//% weight=97
2017-12-15 19:42:44 +01:00
void stopAllSounds() {
2017-12-14 18:17:47 +01:00
if (currentSample) {
samplePtr = currentSample->length;
}
2017-12-14 01:31:42 +01:00
_stopSound();
}
2017-10-30 18:25:58 +01:00
/** Makes a sound bound to a buffer in WAV format. */
//%
Sound fromWAV(Buffer buf) {
incrRC(buf);
return buf;
}
}
//% fixedInstances
namespace SoundMethods {
/** Returns the underlaying Buffer object. */
//% property
Buffer buffer(Sound snd) {
incrRC(snd);
return snd;
}
2017-10-30 19:55:23 +01:00
/** Play sound. */
2017-10-30 18:25:58 +01:00
//% promise
2017-10-30 19:55:23 +01:00
void play(Sound snd) {
music::playSample(snd);
2017-10-30 18:25:58 +01:00
}
2017-07-07 12:44:34 +02:00
}