kopia lustrzana https://github.com/F5OEO/rpitx
Get broadcasting of already formatted files working
rodzic
1aef5f7c5d
commit
4634b2ffd2
2
setup.py
2
setup.py
|
@ -23,5 +23,5 @@ setup(
|
|||
),
|
||||
],
|
||||
package_dir={'': 'src/python'},
|
||||
install_requires=['pydub'],
|
||||
install_requires=['pydub', 'wave'],
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Ładowanie…
Reference in New Issue