2015-12-16 20:30:58 +00:00
|
|
|
/*
|
|
|
|
fm_transmitter - use Raspberry Pi as FM transmitter
|
|
|
|
|
2018-02-05 22:28:06 +00:00
|
|
|
Copyright (c) 2018, Marcin Kondej
|
2015-12-16 20:30:58 +00:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
See https://github.com/markondej/fm_transmitter
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without modification, are
|
|
|
|
permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this list
|
|
|
|
of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice, this
|
|
|
|
list of conditions and the following disclaimer in the documentation and/or other
|
|
|
|
materials provided with the distribution.
|
|
|
|
|
|
|
|
3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
|
|
used to endorse or promote products derived from this software without specific
|
|
|
|
prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
|
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
|
|
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "transmitter.h"
|
2018-12-29 02:18:27 +00:00
|
|
|
#include "error_reporter.h"
|
|
|
|
#include <bcm_host.h>
|
2015-12-16 20:30:58 +00:00
|
|
|
#include <sstream>
|
|
|
|
#include <cmath>
|
2016-12-07 12:34:19 +00:00
|
|
|
#include <fcntl.h>
|
2015-12-16 20:30:58 +00:00
|
|
|
#include <sys/mman.h>
|
|
|
|
|
|
|
|
using std::ostringstream;
|
2018-12-29 02:18:27 +00:00
|
|
|
using std::vector;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
|
|
|
#define GPIO_BASE 0x00200000
|
|
|
|
#define CLK0_BASE 0x00101070
|
|
|
|
#define CLK0DIV_BASE 0x00101074
|
|
|
|
#define TCNT_BASE 0x00003004
|
|
|
|
|
|
|
|
#define ACCESS(base, offset) *(volatile unsigned*)((int)base + offset)
|
|
|
|
#define ACCESS64(base, offset) *(volatile unsigned long long*)((int)base + offset)
|
|
|
|
|
|
|
|
Transmitter::Transmitter()
|
|
|
|
{
|
|
|
|
int memFd;
|
|
|
|
if ((memFd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
|
|
|
|
throw ErrorReporter("Cannot open /dev/mem (permission denied)");
|
|
|
|
}
|
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
peripherals = mmap(NULL, bcm_host_get_peripheral_size(), PROT_READ | PROT_WRITE, MAP_SHARED, memFd, bcm_host_get_peripheral_address());
|
2015-12-16 20:30:58 +00:00
|
|
|
close(memFd);
|
|
|
|
if (peripherals == MAP_FAILED) {
|
|
|
|
throw ErrorReporter("Cannot obtain access to peripherals (mmap error)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Transmitter::~Transmitter()
|
|
|
|
{
|
2018-12-29 02:18:27 +00:00
|
|
|
munmap(peripherals, bcm_host_get_peripheral_size());
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Transmitter* Transmitter::getInstance()
|
|
|
|
{
|
|
|
|
static Transmitter instance;
|
|
|
|
return &instance;
|
|
|
|
}
|
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
void Transmitter::play(WaveReader* reader, double frequency, unsigned char dmaChannel, bool loop)
|
2015-12-16 20:30:58 +00:00
|
|
|
{
|
2016-12-14 23:55:21 +00:00
|
|
|
if (transmitting) {
|
2015-12-16 20:30:58 +00:00
|
|
|
throw ErrorReporter("Cannot play, transmitter already in use");
|
|
|
|
}
|
|
|
|
|
2016-12-14 23:55:21 +00:00
|
|
|
transmitting = true;
|
2016-12-13 01:43:28 +00:00
|
|
|
forceStop = false;
|
2016-12-07 12:17:26 +00:00
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
PCMWaveHeader header = reader->getHeader();
|
|
|
|
unsigned bufferFrames = (unsigned)((unsigned long long)header.sampleRate * BUFFER_TIME / 1000000);
|
|
|
|
vector<float>* frames = reader->getFrames(bufferFrames, forceStop);
|
|
|
|
if (frames == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bool eof = frames->size() < bufferFrames;
|
|
|
|
vector<float>* buffer = frames;
|
|
|
|
|
|
|
|
unsigned frameOffset = 0;
|
|
|
|
unsigned clockDivisor = (unsigned)((500 << 12) / frequency + 0.5);
|
|
|
|
bool restart = false;
|
|
|
|
|
|
|
|
void* params[8] = {
|
|
|
|
peripherals,
|
|
|
|
(void*)(unsigned*)&buffer,
|
|
|
|
(void*)(unsigned*)&frameOffset,
|
|
|
|
(void*)(unsigned*)&clockDivisor,
|
|
|
|
(void*)(unsigned*)&transmitting,
|
|
|
|
(void*)(unsigned*)&restart,
|
|
|
|
(void*)(unsigned*)&dmaChannel,
|
|
|
|
(void*)(unsigned*)&header.sampleRate
|
|
|
|
};
|
|
|
|
|
|
|
|
pthread_t thread;
|
|
|
|
int returnCode = pthread_create(&thread, NULL, &Transmitter::transmit, (void*)¶ms);
|
|
|
|
if (returnCode) {
|
|
|
|
delete frames;
|
|
|
|
ostringstream oss;
|
|
|
|
oss << "Cannot create new thread (code: " << returnCode << ")";
|
|
|
|
throw ErrorReporter(oss.str());
|
|
|
|
}
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
usleep(BUFFER_TIME / 2);
|
2016-12-06 03:24:52 +00:00
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
bool isError = false;
|
|
|
|
string errorMessage;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2016-12-14 23:55:21 +00:00
|
|
|
try {
|
|
|
|
while (!forceStop) {
|
|
|
|
while (!eof && !forceStop) {
|
|
|
|
if (buffer == NULL) {
|
2016-12-19 17:02:24 +00:00
|
|
|
if (!reader->setFrameOffset(frameOffset + bufferFrames)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
frames = reader->getFrames(bufferFrames, forceStop);
|
2016-12-14 23:55:21 +00:00
|
|
|
if (frames == NULL) {
|
|
|
|
forceStop = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-12-13 01:43:28 +00:00
|
|
|
eof = frames->size() < bufferFrames;
|
|
|
|
buffer = frames;
|
|
|
|
}
|
|
|
|
usleep(BUFFER_TIME / 2);
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
2016-12-14 23:55:21 +00:00
|
|
|
if (loop && !forceStop) {
|
|
|
|
frameOffset = 0;
|
|
|
|
restart = true;
|
2017-11-20 14:38:04 +00:00
|
|
|
if (!reader->setFrameOffset(0)) {
|
2016-12-19 17:02:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
frames = reader->getFrames(bufferFrames, forceStop);
|
2016-12-14 23:55:21 +00:00
|
|
|
if (frames == NULL) {
|
|
|
|
break;
|
|
|
|
}
|
2016-12-13 01:43:28 +00:00
|
|
|
eof = frames->size() < bufferFrames;
|
|
|
|
buffer = frames;
|
|
|
|
usleep(BUFFER_TIME / 2);
|
2016-12-14 23:55:21 +00:00
|
|
|
} else {
|
|
|
|
forceStop = true;
|
2016-12-13 01:43:28 +00:00
|
|
|
}
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|
2018-12-29 02:18:27 +00:00
|
|
|
} catch (ErrorReporter &error) {
|
|
|
|
errorMessage = error.what();
|
|
|
|
isError = true;
|
2016-12-14 23:55:21 +00:00
|
|
|
}
|
2018-12-29 02:18:27 +00:00
|
|
|
transmitting = false;
|
|
|
|
pthread_join(thread, NULL);
|
|
|
|
if (isError) {
|
|
|
|
throw ErrorReporter(errorMessage);
|
|
|
|
}
|
|
|
|
}
|
2015-12-16 20:30:58 +00:00
|
|
|
|
|
|
|
void* Transmitter::transmit(void* params)
|
|
|
|
{
|
2018-12-29 02:18:27 +00:00
|
|
|
void* peripherals = ((void**)params)[0];
|
|
|
|
vector<float>** buffer = (vector<float>**)((void**)params)[1];
|
|
|
|
unsigned* frameOffset = (unsigned*)((void**)params)[2], clockDivisor = (unsigned*)((void**)params)[3];
|
|
|
|
bool* transmitting = (bool*)((void**)params)[4], restart = (bool*)((void**)params)[5];
|
|
|
|
unsigned char dmaChannel = *(unsigned char*)((void**)params)[6];
|
|
|
|
unsigned sampleRate = *(unsigned*)((void**)params)[7];
|
|
|
|
|
2015-12-16 20:30:58 +00:00
|
|
|
unsigned long long current, start, playbackStart;
|
2018-12-29 02:18:27 +00:00
|
|
|
unsigned offset, length, prevOffset;
|
2015-12-16 20:30:58 +00:00
|
|
|
vector<float>* frames = NULL;
|
|
|
|
float* data;
|
2016-12-13 00:37:54 +00:00
|
|
|
float value;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
|
|
|
#ifndef NO_PREEMP
|
2018-12-29 02:18:27 +00:00
|
|
|
float prevValue = 0.0;
|
2015-12-16 20:30:58 +00:00
|
|
|
float preemp = 0.75 - 250000.0 / (float)(sampleRate * 75);
|
|
|
|
#endif
|
|
|
|
|
2016-12-06 03:29:47 +00:00
|
|
|
ACCESS(peripherals, GPIO_BASE) = (ACCESS(peripherals, GPIO_BASE) & 0xFFFF8FFF) | (0x01 << 14);
|
|
|
|
ACCESS(peripherals, CLK0_BASE) = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06;
|
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
current = ACCESS64(peripherals, TCNT_BASE);
|
|
|
|
playbackStart = current;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
while (*transmitting) {
|
2016-12-06 03:24:52 +00:00
|
|
|
start = current;
|
2018-12-29 02:18:27 +00:00
|
|
|
|
|
|
|
while ((*buffer == NULL) && *transmitting) {
|
2016-02-02 16:11:08 +00:00
|
|
|
usleep(1);
|
2015-12-16 20:30:58 +00:00
|
|
|
current = ACCESS64(peripherals, TCNT_BASE);
|
|
|
|
}
|
2018-12-29 02:18:27 +00:00
|
|
|
if (!*transmitting) {
|
2015-12-16 20:30:58 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-12-29 02:18:27 +00:00
|
|
|
if (*restart) {
|
2016-12-06 03:24:52 +00:00
|
|
|
playbackStart = current;
|
|
|
|
start = current;
|
2018-12-29 02:18:27 +00:00
|
|
|
*restart = false;
|
2016-12-06 03:24:52 +00:00
|
|
|
}
|
2018-12-29 02:18:27 +00:00
|
|
|
frames = *buffer;
|
|
|
|
*frameOffset = (current - playbackStart) * (sampleRate) / 1000000;
|
|
|
|
*buffer = NULL;
|
|
|
|
|
|
|
|
offset = (current - start) * (sampleRate) / 1000000;
|
2015-12-16 20:30:58 +00:00
|
|
|
|
|
|
|
length = frames->size();
|
|
|
|
data = &(*frames)[0];
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (offset >= length) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
prevOffset = offset;
|
2015-12-16 20:30:58 +00:00
|
|
|
value = data[offset];
|
|
|
|
|
|
|
|
#ifndef NO_PREEMP
|
|
|
|
value = value + (value - prevValue) * preemp;
|
|
|
|
value = (value < -1.0) ? -1.0 : ((value > 1.0) ? 1.0 : value);
|
|
|
|
#endif
|
|
|
|
|
2018-12-29 02:18:27 +00:00
|
|
|
ACCESS(peripherals, CLK0DIV_BASE) = (0x5A << 24) | ((*clockDivisor) - (int)(round(value * 16.0)));
|
|
|
|
while (offset == prevOffset) {
|
2016-02-02 07:26:28 +00:00
|
|
|
asm("nop");
|
2015-12-16 20:30:58 +00:00
|
|
|
current = ACCESS64(peripherals, TCNT_BASE);
|
|
|
|
offset = (current - start) * (sampleRate) / 1000000;
|
|
|
|
}
|
|
|
|
#ifndef NO_PREEMP
|
|
|
|
prevValue = value;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
delete frames;
|
|
|
|
}
|
|
|
|
|
|
|
|
ACCESS(peripherals, CLK0_BASE) = (0x5A << 24);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Transmitter::stop()
|
|
|
|
{
|
2016-12-13 01:43:28 +00:00
|
|
|
forceStop = true;
|
2015-12-16 20:30:58 +00:00
|
|
|
}
|