From 4634b2ffd2315a368fde2967de1a8f15a9c8ea57 Mon Sep 17 00:00:00 2001 From: Brandon Skari Date: Tue, 19 Jan 2016 03:05:45 +0700 Subject: [PATCH] Get broadcasting of already formatted files working --- setup.py | 2 +- src/python/_rpitxmodule.c | 152 ++++++++++++++++++++++---------------- src/python/rpitx.py | 19 ++++- 3 files changed, 106 insertions(+), 67 deletions(-) diff --git a/setup.py b/setup.py index a565fa6..8384266 100644 --- a/setup.py +++ b/setup.py @@ -23,5 +23,5 @@ setup( ), ], package_dir={'': 'src/python'}, - install_requires=['pydub'], + install_requires=['pydub', 'wave'], ) diff --git a/src/python/_rpitxmodule.c b/src/python/_rpitxmodule.c index 7649d19..57efcbe 100644 --- a/src/python/_rpitxmodule.c +++ b/src/python/_rpitxmodule.c @@ -15,95 +15,123 @@ static SNDFILE* sndFile; // These methods used by libsndfile's virtual file open function -sf_count_t virtualSndfileGetLength(void* unused) { - return sampleLength; +static 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; +static 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, ((char*)userData) + sampleOffset, numBytes); + sampleOffset += numBytes; + return numBytes; +} +static sf_count_t virtualSndfileTell(void* const unused) { + return sampleOffset; +} +static sf_count_t virtualSndfileSeek(const sf_count_t offset, const int whence, void* const unused) { + switch (whence) { + case SEEK_CUR: + sampleOffset += offset; + break; + case SEEK_SET: + sampleOffset = offset; + break; + case SEEK_END: + sampleOffset = sampleLength - offset; + break; + default: + assert(!"Invalid whence"); + } + return sampleOffset; +} +static sf_count_t virtualSndfileWrite(const void *ptr, sf_count_t count, void *user_data) { + return 0; } typedef struct { - double frequency; - uint32_t waitForThisSample; + 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; +static ssize_t formatRfWrapper(void* const outBuffer, const size_t count) { + static float wavBuffer[1024]; + static int wavOffset = 0; + static int wavFilled = 0; - 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; + const int excursion = 6000; + int numBytesWritten = 0; + samplerf_t samplerf; + char* const out = outBuffer; + + while (numBytesWritten < count) { + for (; numBytesWritten <= count - sizeof(samplerf_t) && wavOffset < wavFilled; ++wavOffset) { + const float x = wavBuffer[wavOffset]; + samplerf.frequency = x * excursion * 2.0; + samplerf.waitForThisSample = 1e9 / 48000.0; //en 100 de nanosecond + memcpy(&out[numBytesWritten], &samplerf, sizeof(samplerf_t)); + numBytesWritten += sizeof(samplerf_t); + } + + assert(wavOffset <= wavFilled); + + if (wavOffset == wavFilled) { + wavFilled = sf_readf_float(sndFile, wavBuffer, COUNT_OF(wavBuffer)); + wavOffset = 0; + } + } + return numBytesWritten; +} +static void reset(void) { + sampleOffset = 0; } - static PyObject* _rpitx_broadcast_fm(PyObject* self, PyObject* args) { - int address; - int length; - float frequency; - if (!PyArg_ParseTuple(args, "iif", &address, &length, &frequency)) { - return NULL; - } + int address; + int length; + float frequency; + if (!PyArg_ParseTuple(args, "iif", &address, &length, &frequency)) { + return NULL; + } - sampleBase = (void*)address; - sampleLength = length; - sampleOffset = 0; + 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); + SF_VIRTUAL_IO virtualIo = { + .get_filelen = virtualSndfileGetLength, + .seek = virtualSndfileSeek, + .read = virtualSndfileRead, + .write = virtualSndfileWrite, + .tell = virtualSndfileTell + }; + SF_INFO sfInfo; + sndFile = sf_open_virtual(&virtualIo, SFM_READ, &sfInfo, sampleBase); + if (sf_error(sndFile) != SF_ERR_NO_ERROR) { + // TODO: Throw an exception + fprintf(stderr, "Unable to open sound file: %s\n", sf_strerror(sndFile)); + Py_RETURN_NONE; + } - pitx_run(MODE_IQ, 44000, frequency, 0.0, 0, formatIqWrapper, NULL); - sf_close(sndFile); + pitx_run(MODE_RF, 48000, frequency * 1000.0, 0.0, 0, formatRfWrapper, reset); + sf_close(sndFile); - Py_RETURN_NONE; + Py_RETURN_NONE; } static PyMethodDef _rpitx_methods[] = { - {"broadcast_fm", _rpitx_broadcast_fm, METH_VARARGS, "Low-level broadcasting."}, - {NULL, NULL, 0, NULL} + {"broadcast_fm", _rpitx_broadcast_fm, METH_VARARGS, "Low-level broadcasting."}, + {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC init_rpitx(void) { - (void) Py_InitModule("_rpitx", _rpitx_methods); + (void) Py_InitModule("_rpitx", _rpitx_methods); } diff --git a/src/python/rpitx.py b/src/python/rpitx.py index 403f24c..34e7c98 100644 --- a/src/python/rpitx.py +++ b/src/python/rpitx.py @@ -6,6 +6,7 @@ import _rpitx import array import logging import os +import wave def broadcast_fm(file_, frequency): @@ -32,13 +33,23 @@ def broadcast_fm(file_, frequency): ): logger.debug('Reencoding file') reencoded = StringIO.StringIO() - original.export(reencoded, format='wav', bitrate='44k') - return reencoded + return AudioSegment.from_file(reencoded) return original - encoded_file = _reencode(file_) + raw_audio_data = _reencode(file_) + + wav_data = StringIO.StringIO() + wav_writer = wave.open(wav_data, 'w') + wav_writer.setnchannels(1) + wav_writer.setsampwidth(2) + wav_writer.setframerate(48000) + wav_writer.writeframes(raw_audio_data.raw_data) + wav_writer.close() + raw_array = array.array('c') - raw_array.fromstring(encoded_file.raw_data) + raw_array.fromstring(wav_data.getvalue()) + #with open('sampleaudio.wav', 'rb') as file_: + # raw_array.fromstring(file_.read()) array_address, length = raw_array.buffer_info() _rpitx.broadcast_fm(array_address, length, frequency)