Add callback for processing FM files

pull/13/head
Brandon Skari 2016-01-17 15:07:35 +07:00
rodzic 2df0f54626
commit 96db9cb3dc
5 zmienionych plików z 107 dodań i 65 usunięć

Wyświetl plik

@ -19,9 +19,9 @@ setup(
'src/RpiDma.c',
'src/RpiGpio.c',
],
extra_link_args=['-lrt'],
extra_link_args=['-lrt', '-lsndfile'],
),
],
package_dir={'': 'src/python'},
install_requires=['wave', 'pydub'],
install_requires=['pydub'],
)

Wyświetl plik

@ -1032,27 +1032,6 @@ static void resetFile(void) {
}
static void* arrayBaseAddress;
static int arrayLength;
static int arrayPosition;
/** Wrapper around reading from memory with an interface similar to read. */
ssize_t readArray(void *buffer, const size_t count) {
if (arrayPosition >= arrayLength) {
return 0;
}
const int left = arrayLength - arrayPosition;
const int numBytesToCopy = left > count ? count : left;
memcpy(buffer, arrayBaseAddress + arrayPosition, numBytesToCopy);
arrayPosition += numBytesToCopy;
return numBytesToCopy;
}
void setUpReadArray(void* baseAddress, size_t length) {
arrayBaseAddress = baseAddress;
arrayLength = length;
arrayPosition = 0;
}
int
main(int argc, char* argv[])
{

Wyświetl plik

@ -24,8 +24,4 @@ int pitx_run(
);
/** Wrapper around reading from memory with an interface similar to read. */
ssize_t readArray(void *buffer, const size_t count);
void setUpReadArray(void* baseAddress, size_t length);
#endif

Wyświetl plik

@ -1,10 +1,74 @@
#include <Python.h>
#include <assert.h>
#include <sndfile.h>
#include "../RpiTx.h"
#include "../RpiGpio.h"
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
static void* sampleBase;
static sf_count_t sampleLength;
static sf_count_t sampleOffset;
static SNDFILE* sndFile;
// These methods used by libsndfile's virtual file open function
sf_count_t virtualSndfileGetLength(void* unused) {
return sampleLength;
}
sf_count_t virtualSndfileRead(void* const dest, const sf_count_t count, void* const userData) {
const sf_count_t bytesAvailable = sampleLength - sampleOffset;
const int numBytes = bytesAvailable > count ? count : bytesAvailable;
memcpy(dest, userData, numBytes);
sampleOffset += numBytes;
return numBytes;
}
typedef struct {
double frequency;
uint32_t waitForThisSample;
} samplerf_t;
/**
* Formats a chunk of an array of a mono 44k wav at a time and outputs IQ
* formatted array for broadcast.
*/
ssize_t formatIqWrapper(void* const outBuffer, size_t count) {
static float readBuffer[1024];
const int excursion = 6000;
samplerf_t samplerf;
char* out = outBuffer;
int readCount;
int k;
int totalBytesToRead = count / sizeof(samplerf_t);
int bytesToRead = totalBytesToRead;
if (bytesToRead > COUNT_OF(readBuffer)) {
bytesToRead = COUNT_OF(readBuffer);
}
int bytesWritten = 0;
while ((readCount = sf_readf_float(sndFile, readBuffer, bytesToRead))) {
for (k = 0; k < readCount; k++) {
const int x = readBuffer[k];
samplerf.frequency = x * excursion * 2.0;
samplerf.waitForThisSample = 1e9 / 48000.0; //en 100 de nanosecond
memcpy(&out[bytesWritten], &samplerf, sizeof(samplerf_t));
bytesWritten += sizeof(samplerf_t);
}
totalBytesToRead -= bytesToRead;
if (totalBytesToRead <= 0) {
break;
}
}
return bytesWritten;
}
static PyObject*
_rpitx_broadcast(PyObject* self, PyObject* args) {
_rpitx_broadcast_fm(PyObject* self, PyObject* args) {
int address;
int length;
float frequency;
@ -12,14 +76,29 @@ _rpitx_broadcast(PyObject* self, PyObject* args) {
return NULL;
}
setUpReadArray((void*)address, length);
pitx_run(MODE_IQ, 44000, frequency, 0.0, 0, readArray, NULL);
sampleBase = (void*)address;
sampleLength = length;
sampleOffset = 0;
SF_VIRTUAL_IO virtualIo = {
.get_filelen = virtualSndfileGetLength,
.seek = NULL,
.read = virtualSndfileRead,
.write = NULL,
.tell = NULL
};
SF_INFO sfInfo ;
sndFile = sf_open_virtual(&virtualIo, SFM_READ, &sfInfo, sampleBase);
pitx_run(MODE_IQ, 44000, frequency, 0.0, 0, formatIqWrapper, NULL);
sf_close(sndFile);
Py_RETURN_NONE;
}
static PyMethodDef _rpitx_methods[] = {
{"broadcast", _rpitx_broadcast, METH_VARARGS, "Low-level broadcasting."},
{"broadcast_fm", _rpitx_broadcast_fm, METH_VARARGS, "Low-level broadcasting."},
{NULL, NULL, 0, NULL}
};

Wyświetl plik

@ -2,55 +2,43 @@
from pydub import AudioSegment
import StringIO
import array
import _rpitx
import wave
import array
import logging
import os
def play_fm(file_, frequency):
def broadcast_fm(file_, frequency):
"""Play a music file over FM."""
logging.basicConfig()
logger = logging.getLogger('rpitx')
def _reencode(file_name):
"""Returns a file-like object reencoded to the proper WAV format."""
reencoded = StringIO.StringIO()
# AudioSegment doesn't support context managers either
"""Returns an AudioSegment file reencoded to the proper WAV format."""
original = AudioSegment.from_file(file_name)
if original.channels > 2:
raise ValueError('Too many channels in sound file')
if original.channels == 2:
# TODO: Support stereo. For now, just overlay into mono.
logger.info('Reducing stereo channels to mono')
left, right = original.split_to_mono()
original = left.overlay(right)
original.export(reencoded, format='wav', bitrate='44k')
return reencoded
if (
original.frame_rate != 48000
# TODO: There should be a better way to check if it's wav
or not file_name.endswith('.wav')
):
logger.debug('Reencoding file')
reencoded = StringIO.StringIO()
original.export(reencoded, format='wav', bitrate='44k')
return reencoded
encoded_file = None
if isinstance(file_, str):
if file_.endswith('.wav'):
with open(file_) as raw_file:
# wave.open doesn't support context managers, so we need to be
# careful about closing the file
wav_file = wave.open(raw_file, 'r')
num_channels = wav_file.getnchannels()
framerate = wav_file.getframerate()
sample_width = wav_file.getsampwidth()
wav_file.close()
if (
num_channels != 1
or framerate != 1
or sample_width != 2
):
encoded_file = _reencode(file_)
else:
encoded_file = AudioSegment.from_file(file_)
else:
encoded_file = _reencode(file_)
else:
encoded_file = _reencode(file_)
return original
encoded_file = _reencode(file_)
raw_array = array.array('c')
raw_array.fromstring(str(encoded_file))
raw_array.fromstring(encoded_file.raw_data)
array_address, length = raw_array.buffer_info()
_rpitx.broadcast(array_address, length, frequency)
_rpitx.broadcast_fm(array_address, length, frequency)